This is an automated email from the ASF dual-hosted git repository. dpavlov pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git
The following commit(s) were added to refs/heads/master by this push: new f63df1d IGNITE-10336 Fix of chain collection to avoid missing builds - Fixes #77. f63df1d is described below commit f63df1df3baf71cf18fe16fd86f9e10f39d9644a Author: Dmitriy Pavlov <dpav...@apache.org> AuthorDate: Wed Nov 21 19:51:46 2018 +0300 IGNITE-10336 Fix of chain collection to avoid missing builds - Fixes #77. Signed-off-by: Dmitriy Pavlov <dpav...@apache.org> --- .../apache/ignite/ci/analysis/FullChainRunCtx.java | 2 +- .../ignite/ci/analysis/mode/LatestRebuildMode.java | 2 +- .../ci/tcbot/builds/CompareBuildsService.java | 2 +- .../ignite/ci/tcbot/chain/BuildChainProcessor.java | 240 ++++++++++++--------- .../ci/teamcity/ignited/BuildRefCompacted.java | 7 +- .../ignited/fatbuild/FatBuildCompacted.java | 3 + .../org/apache/ignite/ci/web/model/Version.java | 2 +- .../ci/tcbot/chain/BuildChainProcessorTest.java | 43 +++- 8 files changed, 185 insertions(+), 116 deletions(-) diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/FullChainRunCtx.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/FullChainRunCtx.java index 36602c6..c9cf274 100644 --- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/FullChainRunCtx.java +++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/FullChainRunCtx.java @@ -90,7 +90,7 @@ public class FullChainRunCtx { + (hasFullDurationInfo() ? "" : "+"); } - public void addAllSuites(ArrayList<MultBuildRunCtx> suites) { + public void addAllSuites(List<MultBuildRunCtx> suites) { this.buildCfgsResults.addAll(suites); } diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/mode/LatestRebuildMode.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/mode/LatestRebuildMode.java index 5083bb7..9583f5a 100644 --- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/mode/LatestRebuildMode.java +++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/mode/LatestRebuildMode.java @@ -22,6 +22,6 @@ public enum LatestRebuildMode { NONE, /** replace builds with Latest rebuild. */ LATEST, - /** Collect history of builds. */ + /** Collect history of builds. Rebuilds are applied, but have higher priority. */ ALL } diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/builds/CompareBuildsService.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/builds/CompareBuildsService.java index 71715c8..e420ca4 100644 --- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/builds/CompareBuildsService.java +++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/builds/CompareBuildsService.java @@ -80,7 +80,7 @@ public class CompareBuildsService { MultBuildRunCtx buildCtx = new MultBuildRunCtx(build, compactor); final FatBuildCompacted fatBuild = tcIgnited.getFatBuild(build.getId()); - buildCtx.addBuild(bcp.loadTestsAndProblems(fatBuild, tcIgnited)); + buildCtx.addBuild(bcp.loadChanges(fatBuild, tcIgnited)); for (String testName : buildCtx.tests()) tests.add(extractTestName(testName)); diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessor.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessor.java index 14d6d2e..5142fe6 100644 --- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessor.java +++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessor.java @@ -17,16 +17,19 @@ package org.apache.ignite.ci.tcbot.chain; +import com.google.common.base.Preconditions; import com.google.common.util.concurrent.Futures; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.function.Function; import java.util.stream.Collectors; @@ -92,22 +95,9 @@ public class BuildChainProcessor { if (entryPoints.isEmpty()) return res; - Map<Integer, FatBuildCompacted> builds = new ConcurrentHashMap<>(); + Map<Integer, Future<FatBuildCompacted>> builds = loadAllBuildsInChains(entryPoints, mode, teamcityIgnited); - final Stream<FatBuildCompacted> entryPointsFatBuilds = entryPoints.stream() - .filter(Objects::nonNull) - .filter(id -> !builds.containsKey(id)) //load and propagate only new entry points - .map(id -> builds.computeIfAbsent(id, teamcityIgnited::getFatBuild)); - - final ExecutorService svc = tcUpdatePool.getService(); - - final Stream<FatBuildCompacted> depsFirstLevel = entryPointsFatBuilds - .map(ref -> svc.submit(() -> dependencies(teamcityIgnited, builds, mode, ref))) - .collect(Collectors.toList()) - .stream() - .flatMap(fut -> FutureUtil.getResult(fut)); - - depsFirstLevel + builds.values().stream().map(FutureUtil::getResult) .filter(b -> !b.isComposite() && b.getTestsCount() > 0) .forEach(b -> { @@ -121,7 +111,7 @@ public class BuildChainProcessor { ); if (!lrTests.isEmpty()) { - Collections.sort(lrTests, (test0, test1) -> { + lrTests.sort((test0, test1) -> { long t0 = test0.time; long t1 = test1.time; @@ -156,18 +146,18 @@ public class BuildChainProcessor { /** * @param teamcity Teamcity. - * @param teamcityIgnited + * @param tcIgn Teamcity Ignited. * @param entryPoints Entry point(s): Build(s) to start scan from. * @param includeLatestRebuild Include latest rebuild. * @param procLog Process logger. * @param includeScheduledInfo Include scheduled info. * @param failRateBranch Fail rate branch. - * @param mode + * @param mode background data update mode. */ @AutoProfiling public FullChainRunCtx loadFullChainContext( IAnalyticsEnabledTeamcity teamcity, - ITeamcityIgnited teamcityIgnited, + ITeamcityIgnited tcIgn, Collection<Integer> entryPoints, LatestRebuildMode includeLatestRebuild, ProcessLogsMode procLog, @@ -178,39 +168,45 @@ public class BuildChainProcessor { if (entryPoints.isEmpty()) return new FullChainRunCtx(Build.createFakeStub()); - Map<Integer, FatBuildCompacted> builds = new ConcurrentHashMap<>(); + Map<Integer, Future<FatBuildCompacted>> builds = loadAllBuildsInChains(entryPoints, mode, tcIgn); - final Stream<FatBuildCompacted> entryPointsFatBuilds = entryPoints.stream() - .filter(Objects::nonNull) - .filter(id -> !builds.containsKey(id)) //load and propagate only new entry points - .map(id -> builds.computeIfAbsent(id, id0 -> teamcityIgnited.getFatBuild(id0, mode))); + Map<String, List<Future<FatBuildCompacted>>> freshRebuilds = new ConcurrentHashMap<>(); - final ExecutorService svc = tcUpdatePool.getService(); + groupByBuildType(builds).forEach( + (k, buildsForBt) -> { + List<Future<FatBuildCompacted>> futures = replaceWithRecent(buildsForBt, + entryPoints.size(), + includeLatestRebuild, + builds, + mode, + tcIgn); - final Stream<FatBuildCompacted> depsFirstLevel = entryPointsFatBuilds - .flatMap(ref -> dependencies(teamcityIgnited, builds, mode, ref)); + freshRebuilds.put(k, futures); + } + ); - Stream<FatBuildCompacted> secondLevelDeps = depsFirstLevel - .flatMap(ref -> dependencies(teamcityIgnited, builds, mode, ref)); + List<MultBuildRunCtx> contexts = new ArrayList<>(freshRebuilds.size()); - // builds may became non unique because of race in filtering and acquiring deps - final List<Future<Stream<FatBuildCompacted>>> phase3Submitted = secondLevelDeps - .map((fatBuild) -> svc.submit( - () -> replaceWithRecent(teamcityIgnited, includeLatestRebuild, mode, builds, fatBuild, entryPoints.size()))) + freshRebuilds.forEach((bt, listBuilds) -> { + List<FatBuildCompacted> buildsForSuite = listBuilds.stream() + .map(FutureUtil::getResult) + .filter(buildCompacted -> !buildCompacted.isFakeStub()) .collect(Collectors.toList()); - Map<String, MultBuildRunCtx> buildsCtxMap = new ConcurrentHashMap<>(); + if (buildsForSuite.isEmpty()) + return; + + BuildRef ref = buildsForSuite.iterator().next().toBuildRef(compactor); - phase3Submitted.stream() - .flatMap(fut -> FutureUtil.getResult(fut)) - .forEach((fatBuild) -> createCxt(teamcityIgnited, buildsCtxMap, fatBuild)); + final MultBuildRunCtx ctx = new MultBuildRunCtx(ref, compactor); - ArrayList<MultBuildRunCtx> contexts = new ArrayList<>(buildsCtxMap.values()); + buildsForSuite.forEach(buildCompacted -> ctx.addBuild(loadChanges(buildCompacted, tcIgn))); - contexts.forEach(multiCtx -> { - analyzeTests(multiCtx, teamcity, procLog); + analyzeTests(ctx, teamcity, procLog); - fillBuildCounts(multiCtx, teamcityIgnited, includeScheduledInfo); + fillBuildCounts(ctx, tcIgn, includeScheduledInfo); + + contexts.add(ctx); }); Function<MultBuildRunCtx, Float> function = ctx -> { @@ -227,8 +223,8 @@ public class BuildChainProcessor { }; Integer someEntryPnt = entryPoints.iterator().next(); - FatBuildCompacted build = builds.computeIfAbsent(someEntryPnt, id -> teamcityIgnited.getFatBuild(id, mode)); - FullChainRunCtx fullChainRunCtx = new FullChainRunCtx(build.toBuild(compactor)); + Future<FatBuildCompacted> build = getOrLoadBuild(someEntryPnt, mode, builds, tcIgn); + FullChainRunCtx fullChainRunCtx = new FullChainRunCtx(FutureUtil.getResult(build).toBuild(compactor)); contexts.sort(Comparator.comparing(function).reversed()); @@ -237,20 +233,55 @@ public class BuildChainProcessor { return fullChainRunCtx; } - @SuppressWarnings("WeakerAccess") - @AutoProfiling - protected void createCxt(ITeamcityIgnited teamcityIgnited, - Map<String, MultBuildRunCtx> buildsCtxMap, - FatBuildCompacted buildCompacted) { - final BuildRef ref = buildCompacted.toBuildRef(compactor); + @NotNull + public Map<Integer, Future<FatBuildCompacted>> loadAllBuildsInChains(Collection<Integer> entryPoints, + SyncMode mode, + ITeamcityIgnited tcIgn) { + Map<Integer, Future<FatBuildCompacted>> builds = new ConcurrentHashMap<>(); + + Stream<Future<FatBuildCompacted>> entryPointsFatBuilds = entryPoints.stream() + .filter(Objects::nonNull) + .map(id -> getOrLoadBuild(id, mode, builds, tcIgn)); - if (buildCompacted.isFakeStub() || ref.isFakeStub()) - return; + Set<Integer> remainedUnloadedDeps = entryPointsFatBuilds + .flatMap(ref -> dependencies(ref, mode, builds, tcIgn).stream()).collect(Collectors.toSet()); - final MultBuildRunCtx ctx = buildsCtxMap.computeIfAbsent(ref.buildTypeId, - k -> new MultBuildRunCtx(ref, compactor)); + for (int level = 1; level < 5; level++) { + if (remainedUnloadedDeps.isEmpty()) + break; + + Set<Integer> depsNextLevel = remainedUnloadedDeps + .stream() + .map(builds::get) + .peek(val -> Preconditions.checkNotNull(val, "Build future should be in context")) + .flatMap(ref -> dependencies(ref, mode, builds, tcIgn).stream()).collect(Collectors.toSet()); + + logger.info("Level [" + level + "] dependencies:" + depsNextLevel); + + remainedUnloadedDeps = depsNextLevel; + } - ctx.addBuild(loadTestsAndProblems(buildCompacted, teamcityIgnited)); + return builds; + } + + @NotNull + public Map<String, List<FatBuildCompacted>> groupByBuildType(Map<Integer, Future<FatBuildCompacted>> builds) { + Map<String, List<FatBuildCompacted>> buildsByBt = new ConcurrentHashMap<>(); + builds.values().forEach(bFut -> { + FatBuildCompacted b = FutureUtil.getResult(bFut); + + String buildTypeId = b.buildTypeId(compactor); + if (buildTypeId == null) + logger.error("Invalid build type ID for build " + b.getId()); + else + buildsByBt.computeIfAbsent(buildTypeId, k -> new ArrayList<>()).add(b); + }); + return buildsByBt; + } + + public Future<FatBuildCompacted> getOrLoadBuild(Integer id, SyncMode mode, + Map<Integer, Future<FatBuildCompacted>> builds, ITeamcityIgnited tcIgn) { + return builds.computeIfAbsent(id, id0 -> loadBuildAsync(id0, mode, tcIgn)); } /** @@ -260,51 +291,55 @@ public class BuildChainProcessor { * @param tcIgnited * @return Full context. */ - public SingleBuildRunCtx loadTestsAndProblems(@Nonnull FatBuildCompacted buildCompacted, + public SingleBuildRunCtx loadChanges(@Nonnull FatBuildCompacted buildCompacted, ITeamcityIgnited tcIgnited) { SingleBuildRunCtx ctx = new SingleBuildRunCtx(buildCompacted, compactor); ctx.setChanges(tcIgnited.getAllChanges(buildCompacted.changes())); - //todo support storing build.lastChanges.changes) ? - return ctx; } @SuppressWarnings("WeakerAccess") @NotNull @AutoProfiling - protected Stream<FatBuildCompacted> replaceWithRecent(ITeamcityIgnited teamcityIgnited, + protected List<Future<FatBuildCompacted>> replaceWithRecent(List<FatBuildCompacted> builds, + int cntLimit, LatestRebuildMode includeLatestRebuild, + Map<Integer, Future<FatBuildCompacted>> allBuildsMap, SyncMode syncMode, - Map<Integer, FatBuildCompacted> builds, - FatBuildCompacted buildCompacted, - int cntLimit) { - if (includeLatestRebuild == LatestRebuildMode.NONE) - return Stream.of(buildCompacted); + ITeamcityIgnited tcIgn) { + if (includeLatestRebuild == LatestRebuildMode.NONE || builds.isEmpty()) + return completed(builds); + + Optional<FatBuildCompacted> maxIdBuildOpt = builds.stream().max(Comparator.comparing(BuildRefCompacted::id)); + if (!maxIdBuildOpt.isPresent()) + return completed(builds); + + FatBuildCompacted freshBuild = maxIdBuildOpt.get(); - final String branch = getBranchOrDefault(buildCompacted.branchName(compactor)); + final String branch = getBranchOrDefault(freshBuild.branchName(compactor)); - final String buildTypeId = buildCompacted.buildTypeId(compactor); - Stream<BuildRefCompacted> hist = teamcityIgnited.getAllBuildsCompacted(buildTypeId, branch) + final String buildTypeId = freshBuild.buildTypeId(compactor); + Stream<BuildRefCompacted> hist = tcIgn.getAllBuildsCompacted(buildTypeId, branch) .stream() - .filter(t -> !t.isCancelled(compactor)) - .filter(t -> t.isFinished(compactor)); + .filter(bref -> !bref.isCancelled(compactor)) + .filter(bref -> bref.isFinished(compactor)); if (includeLatestRebuild == LatestRebuildMode.LATEST) { BuildRefCompacted recentRef = hist.max(Comparator.comparing(BuildRefCompacted::id)) - .orElse(buildCompacted); + .orElse(freshBuild); - return Stream.of(recentRef) - .map(b -> builds.computeIfAbsent(b.id(), id -> teamcityIgnited.getFatBuild(id, syncMode))); + return Collections.singletonList( + getOrLoadBuild(recentRef.id(), syncMode, allBuildsMap, tcIgn)); } if (includeLatestRebuild == LatestRebuildMode.ALL) { return hist .sorted(Comparator.comparing(BuildRefCompacted::id).reversed()) .limit(cntLimit) - // .filter(b -> !builds.containsKey(b.id())) // todo removing this causes incorrect count of failures (duplicated builds) - .map(b -> builds.computeIfAbsent(b.id(), id -> teamcityIgnited.getFatBuild(id, syncMode))); + .map(bref -> getOrLoadBuild(bref.id(), syncMode, allBuildsMap, tcIgn)) + .collect(Collectors.toList()); } throw new UnsupportedOperationException("invalid mode " + includeLatestRebuild); @@ -365,47 +400,40 @@ public class BuildChainProcessor { return branch; } + /** + * @param buildFut Chain build future. + * @param mode Mode. + * @param builds Builds. + * @param teamcityIgnited Teamcity ignited. + * @return Set of new builds found during this dependencies check round. + */ @NotNull - private Stream<FatBuildCompacted> dependencies( - ITeamcityIgnited teamcityIgnited, - Map<Integer, FatBuildCompacted> builds, + private Set<Integer> dependencies( + Future<FatBuildCompacted> buildFut, SyncMode mode, - FatBuildCompacted build) { + Map<Integer, Future<FatBuildCompacted>> builds, + ITeamcityIgnited teamcityIgnited) { + Set<Integer> newBuilds = new HashSet<>(); - Stream<FatBuildCompacted> stream = IntStream.of(build.snapshotDependencies()) - .mapToObj(id -> { - if (builds.containsKey(id)) - return Futures.<FatBuildCompacted>immediateFuture(null); //load and propagate only new dependencies + IntStream.of(FutureUtil.getResult(buildFut).snapshotDependencies()) + .forEach(id -> builds.computeIfAbsent(id, id0 -> { + newBuilds.add(id0); - if (mode == SyncMode.NONE) - return Futures.immediateFuture(loadBuild(teamcityIgnited, builds, mode, id)); + return loadBuildAsync(id0, mode, teamcityIgnited); + })); - return tcUpdatePool.getService().submit(() -> { - if (builds.containsKey(id)) - return null; + return newBuilds; + } - return loadBuild(teamcityIgnited, builds, mode, id); - }); - }) - .collect(Collectors.toList()) - .stream() - .map(future -> FutureUtil.getResult(future)) - .filter(Objects::nonNull); + public Future<FatBuildCompacted> loadBuildAsync(Integer id, SyncMode mode, ITeamcityIgnited teamcityIgnited) { + if (mode == SyncMode.NONE) + return Futures.immediateFuture(teamcityIgnited.getFatBuild(id, SyncMode.NONE)); - return Stream.concat( - Stream.of(build), - stream); + return tcUpdatePool.getService().submit(() -> teamcityIgnited.getFatBuild(id, mode)); } - @Nullable - private FatBuildCompacted loadBuild(ITeamcityIgnited teamcityIgnited, - Map<Integer, FatBuildCompacted> builds, - SyncMode mode, - int id) { - FatBuildCompacted buildLoaded = teamcityIgnited.getFatBuild(id, mode); - - FatBuildCompacted prevVal = builds.putIfAbsent(id, buildLoaded); - return prevVal == null ? buildLoaded : null; + private List<Future<FatBuildCompacted>> completed(List<FatBuildCompacted> builds) { + return builds.stream().map(Futures::immediateFuture).collect(Collectors.toList()); } } diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java index efda317..f6ccf03 100644 --- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java +++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java @@ -22,6 +22,7 @@ import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.apache.ignite.ci.db.Persisted; import org.apache.ignite.ci.tcmodel.hist.BuildRef; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static org.apache.ignite.ci.tcmodel.hist.BuildRef.*; @@ -93,7 +94,7 @@ public class BuildRefCompacted { } protected void fillBuildRefFields(IStringCompactor compactor, BuildRef res) { - res.setId(id < 0 ? null : id); + res.setId(getId()); res.buildTypeId = buildTypeId(compactor); res.branchName = branchName(compactor); res.status = compactor.getStringFromId(status); @@ -101,6 +102,10 @@ public class BuildRefCompacted { res.href = getHrefForId(id()); } + @Nullable public Integer getId() { + return id < 0 ? null : id; + } + public String buildTypeId(IStringCompactor compactor) { return compactor.getStringFromId(buildTypeId); } diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java index 95e88ad..419de92 100644 --- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java +++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java @@ -330,6 +330,9 @@ public class FatBuildCompacted extends BuildRefCompacted implements IVersionedEn * */ public boolean isFakeStub() { + if (getId() == null) + return true; + Boolean flag = getFlag(FAKE_BUILD_F); return flag != null && flag; diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java index 51ce92e..c406e7f 100644 --- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java +++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java @@ -23,7 +23,7 @@ package org.apache.ignite.ci.web.model; public static final String GITHUB_REF = "https://github.com/apache/ignite-teamcity-bot"; /** TC Bot Version. */ - public static final String VERSION = "20181120"; + public static final String VERSION = "20181121"; /** TC Bot Version. */ public String version = VERSION; diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessorTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessorTest.java index 2e2ec97..c09ea1c 100644 --- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessorTest.java +++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessorTest.java @@ -45,7 +45,6 @@ import org.apache.ignite.ci.teamcity.ignited.InMemoryStringCompactor; import org.apache.ignite.ci.teamcity.ignited.SyncMode; import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted; import org.jetbrains.annotations.NotNull; -import org.junit.Ignore; import org.junit.Test; import org.mockito.Mockito; @@ -60,8 +59,15 @@ import static org.mockito.Mockito.when; * Test for chain processor */ public class BuildChainProcessorTest { + /** Unique failed test, prefix for test name. This name will be unique each time. */ public static final String UNIQUE_FAILED_TEST = "uniqueFailedTest"; + + /** Test failing every time. */ public static final String TEST_FAILING_EVERY_TIME = "testFailingEveryTime"; + + /** Pds 1 build type ID. */ + public static final String PDS_1_BT_ID = "Pds1"; + /** Injector. */ private Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { @@ -73,7 +79,6 @@ public class BuildChainProcessorTest { /** * */ - @Ignore @Test public void testAllBuildsArePresentInMergedBuilds() { IStringCompactor c = injector.getInstance(IStringCompactor.class); @@ -102,14 +107,42 @@ public class BuildChainProcessorTest { else assertTrue(suite.failedTests() >= 1); - List<ITestFailures> tests = suite.getFailedTests(); - for (ITestFailures test : tests) { + for (ITestFailures test : suite.getFailedTests()) { if (test.getName().startsWith(UNIQUE_FAILED_TEST)) assertEquals(1, test.failuresCount()); else if (test.getName().equals(TEST_FAILING_EVERY_TIME)) assertEquals(10, test.failuresCount()); } } + + //Adding successfull re-runs + for (int j = 0; j < 10; j++) { + FatBuildCompacted pds1 = testFatBuild(c, 130 + j, PDS_1_BT_ID); + pds1.buildTypeName(UNIQUE_FAILED_TEST, c); + + TestOccurrenceFull t1 = new TestOccurrenceFull(); + t1.name = UNIQUE_FAILED_TEST + j; + t1.status = TestOccurrence.STATUS_SUCCESS; + pds1.addTests(c, Lists.newArrayList(t1)); + + builds.put(pds1.id(), pds1); + } + + FullChainRunCtx ctx2 = bcp.loadFullChainContext(tcOldMock(), tcIgnited, + entry, + LatestRebuildMode.ALL, ProcessLogsMode.SUITE_NOT_COMPLETE, false, ITeamcity.DEFAULT, SyncMode.NONE); + List<MultBuildRunCtx> suites2 = ctx2.failedChildSuites().collect(Collectors.toList()); + + assertTrue(!suites2.isEmpty()); + + for (MultBuildRunCtx suite : suites2) { + System.out.println(suite.getFailedTestsNames().collect(Collectors.toList())); + + if (suite.suiteName() != null && suite.suiteName().startsWith(UNIQUE_FAILED_TEST)) { + for (ITestFailures test : suite.getFailedTests()) + assertTrue("Failure found but should be hidden by re-run " + test.getName(), false); + } + } } /** @@ -144,7 +177,7 @@ public class BuildChainProcessorTest { builds.put(root.id(), root); - FatBuildCompacted pds1 = testFatBuild(c, 100 + i, "Pds1"); + FatBuildCompacted pds1 = testFatBuild(c, 100 + i, PDS_1_BT_ID); pds1.buildTypeName(UNIQUE_FAILED_TEST, c); TestOccurrenceFull t1 = new TestOccurrenceFull();