You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ignite.apache.org by GitBox <gi...@apache.org> on 2018/08/24 15:30:39 UTC

[GitHub] asfgit closed pull request #2: Changed algorithm of calculated flaky tests. Now flaky is test which …

asfgit closed pull request #2: Changed algorithm of calculated flaky tests. Now flaky is test which …
URL: https://github.com/apache/ignite-teamcity-bot/pull/2
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/BuildChainProcessor.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/BuildChainProcessor.java
index f7aa484..0a85be1 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/BuildChainProcessor.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/BuildChainProcessor.java
@@ -124,17 +124,26 @@
             )
             .forEach((BuildRef buildRef) -> {
                 Build build = teamcity.getBuild(buildRef.href);
+
                 if (build == null || build.isFakeStub())
                     return;
 
-                String buildTypeId = build.buildTypeId;
-                MultBuildRunCtx ctx = buildsCtxMap.computeIfAbsent(buildTypeId, k -> new MultBuildRunCtx(build));
+                MultBuildRunCtx ctx = buildsCtxMap.computeIfAbsent(build.buildTypeId, k -> new MultBuildRunCtx(build));
 
-                collectBuildContext(ctx, teamcity, procLog, contactPersonProps, includeScheduledInfo, build);
+                ctx.addBuild(teamcity.loadTestsAndProblems(build, ctx));
             });
 
-        Collection<MultBuildRunCtx> values = buildsCtxMap.values();
-        ArrayList<MultBuildRunCtx> contexts = new ArrayList<>(values);
+        ArrayList<MultBuildRunCtx> contexts = new ArrayList<>(buildsCtxMap.values());
+
+        contexts.forEach(multiCtx -> {
+            analyzeTests(multiCtx, teamcity, procLog, tcAnalytics);
+
+            fillBuildCounts(multiCtx, teamcity, includeScheduledInfo);
+
+            if (contactPersonProps != null && multiCtx.getContactPerson() == null)
+                multiCtx.setContactPerson(contactPersonProps.getProperty(multiCtx.suiteId()));
+        });
+
         if (tcAnalytics != null) {
             Function<MultBuildRunCtx, Float> function = ctx -> {
                 SuiteInBranch key = new SuiteInBranch(ctx.suiteId(), normalizeBranch(failRateBranch));
@@ -173,30 +182,29 @@ private static boolean ensureUnique(Map<Integer, BuildRef> unique, BuildRef ref)
         return prevVal == null;
     }
 
-    private static void collectBuildContext(
-        MultBuildRunCtx outCtx, ITeamcity teamcity, ProcessLogsMode procLog,
-        @Nullable Properties contactPersonProps, boolean includeScheduledInfo, Build build) {
-
-        SingleBuildRunCtx ctx = teamcity.loadTestsAndProblems(build, outCtx);
-        outCtx.addBuild(ctx);
-
-        if ((procLog == ProcessLogsMode.SUITE_NOT_COMPLETE && ctx.hasSuiteIncompleteFailure())
-            || procLog == ProcessLogsMode.ALL)
-            ctx.setLogCheckResultsFut(teamcity.analyzeBuildLog(ctx.buildId(), ctx));
-
+    private static void fillBuildCounts(MultBuildRunCtx outCtx, ITeamcity teamcity, boolean includeScheduledInfo) {
         if (includeScheduledInfo && !outCtx.hasScheduledBuildsInfo()) {
             Function<List<BuildRef>, Long> countRelatedToThisBuildType = list ->
                 list.stream()
-                    .filter(ref -> Objects.equals(ref.buildTypeId, build.buildTypeId))
-                    .filter(ref -> Objects.equals(normalizeBranch(build), normalizeBranch(ref)))
+                    .filter(ref -> Objects.equals(ref.buildTypeId, outCtx.buildTypeId()))
+                    .filter(ref -> Objects.equals(normalizeBranch(outCtx.branchName()), normalizeBranch(ref)))
                     .count();
 
             outCtx.setRunningBuildCount(teamcity.getRunningBuilds("").thenApply(countRelatedToThisBuildType));
             outCtx.setQueuedBuildCount(teamcity.getQueuedBuilds("").thenApply(countRelatedToThisBuildType));
         }
+    }
 
-        if (contactPersonProps != null && outCtx.getContactPerson() == null)
-            outCtx.setContactPerson(contactPersonProps.getProperty(outCtx.suiteId()));
+    private static void analyzeTests(MultBuildRunCtx outCtx, ITeamcity teamcity, ProcessLogsMode procLog,
+        ITcAnalytics tcAnalytics) {
+        for (SingleBuildRunCtx ctx : outCtx.getBuilds()) {
+            if (tcAnalytics != null)
+                tcAnalytics.calculateBuildStatistic(ctx);
+
+            if ((procLog == ProcessLogsMode.SUITE_NOT_COMPLETE && ctx.hasSuiteIncompleteFailure())
+                || procLog == ProcessLogsMode.ALL)
+                ctx.setLogCheckResultsFut(teamcity.analyzeBuildLog(ctx.buildId(), ctx));
+        }
     }
 
     @NotNull protected static String normalizeBranch(@NotNull final BuildRef build) {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcAnalytics.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcAnalytics.java
index 7f93b92..1599497 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcAnalytics.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcAnalytics.java
@@ -20,6 +20,7 @@
 import java.util.List;
 import java.util.function.Function;
 import org.apache.ignite.ci.analysis.RunStat;
+import org.apache.ignite.ci.analysis.SingleBuildRunCtx;
 import org.apache.ignite.ci.analysis.SuiteInBranch;
 import org.apache.ignite.ci.analysis.TestInBranch;
 
@@ -30,6 +31,7 @@
 
     /**
      * Return build statistics for default branch provider
+     *
      * @return map from suite ID to its run statistics
      */
     Function<SuiteInBranch, RunStat> getBuildFailureRunStatProvider();
@@ -43,5 +45,12 @@
 
     String getThreadDumpCached(Integer buildId);
 
+    /**
+     * Calculate required statistic for build if was not already calculated.
+     *
+     * @param ctx Context as provider build data.
+     */
+    void calculateBuildStatistic(SingleBuildRunCtx ctx);
+
     @Override void close();
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
index 1eb6899..9782cbf 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
@@ -163,6 +163,8 @@ default SingleBuildRunCtx loadTestsAndProblems(@Nonnull Build build, @Deprecated
             List<TestOccurrence> tests
                 = getTests(build.testOccurrences.href + TESTS_COUNT_7700, normalizedBranch).getTests();
 
+            ctx.setTests(tests);
+
             mCtx.addTests(tests);
 
             for (TestOccurrence next : tests) {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
index 7860bb4..95193bc 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
@@ -80,6 +80,7 @@
     //V2 caches, 32 parts
     public static final String TESTS_OCCURRENCES = "testOccurrences";
     public static final String TESTS_RUN_STAT = "testsRunStat";
+    public static final String CALCULATED_STATISTIC = "calculatedStatistic";
     public static final String LOG_CHECK_RESULT = "logCheckResult";
     public static final String CHANGE_INFO_FULL = "changeInfoFull";
     public static final String CHANGES_LIST = "changesList";
@@ -497,14 +498,7 @@ private void registerCriticalBuildProblemInStat(Build build, ProblemOccurrences
 
         return loadIfAbsent(testOccurrencesCache(),
             hrefForDb,  //hack to avoid test reloading from store in case of href filter replaced
-            hrefIgnored -> {
-                TestOccurrences loadedTests = teamcity.getTests(href, normalizedBranch);
-
-                //todo first touch of build here will cause build and its stat will be diverged
-                addTestOccurrencesToStat(loadedTests, normalizedBranch);
-
-                return loadedTests;
-            });
+            hrefIgnored -> teamcity.getTests(href, normalizedBranch));
     }
 
     private void addTestOccurrencesToStat(TestOccurrences val) {
@@ -512,8 +506,9 @@ private void addTestOccurrencesToStat(TestOccurrences val) {
     }
 
     private void addTestOccurrencesToStat(TestOccurrences val, String normalizedBranch) {
-        for (TestOccurrence next : val.getTests())
-            addTestOccurrenceToStat(next, normalizedBranch);
+        for (TestOccurrence next : val.getTests()) {
+            addTestOccurrenceToStat(next, normalizedBranch, null);
+        }
     }
 
     /** {@inheritDoc} */
@@ -612,7 +607,6 @@ private void addTestOccurrencesToStat(TestOccurrences val, String normalizedBran
         return key -> key == null ? null : testRunStatCache().get(key);
     }
 
-
     private Stream<RunStat> allTestAnalysis() {
         return StreamSupport.stream(testRunStatCache().spliterator(), false)
             .map(Cache.Entry::getValue);
@@ -622,6 +616,10 @@ private void addTestOccurrencesToStat(TestOccurrences val, String normalizedBran
         return getOrCreateCacheV2(ignCacheNme(TESTS_RUN_STAT));
     }
 
+    private IgniteCache<Integer, Boolean> calculatedStatistic() {
+        return getOrCreateCacheV2(ignCacheNme(CALCULATED_STATISTIC));
+    }
+
     /** {@inheritDoc} */
     @Override public Function<SuiteInBranch, RunStat> getBuildFailureRunStatProvider() {
         return key -> key == null ? null : buildsFailureRunStatCache().get(key);
@@ -643,7 +641,7 @@ private void addTestOccurrencesToStat(TestOccurrences val, String normalizedBran
         return getOrCreateCacheV2(ignCacheNme(LOG_CHECK_RESULT));
     }
 
-    private void addTestOccurrenceToStat(TestOccurrence next, String normalizedBranch) {
+    private void addTestOccurrenceToStat(TestOccurrence next, String normalizedBranch, Boolean changesExist) {
         String name = next.getName();
         if (Strings.isNullOrEmpty(name))
             return;
@@ -661,7 +659,7 @@ private void addTestOccurrenceToStat(TestOccurrence next, String normalizedBranc
             if (val == null)
                 val = new RunStat(key.getName());
 
-            val.addTestRun(testOccurrence);
+            val.addTestRun(testOccurrence, changesExist);
 
             entry.setValue(val);
 
@@ -692,7 +690,7 @@ private void migrateTestOneOcurrToAddToLatest(TestOccurrence next) {
             if (val == null)
                 val = new RunStat(key.name);
 
-            val.addTestRunToLatest(testOccurrence);
+            val.addTestRunToLatest(testOccurrence, RunStat.ChangesState.UNKNOWN);
 
             entry.setValue(val);
 
@@ -739,6 +737,18 @@ public String getThreadDumpCached(Integer buildId) {
         return logCheckResult.getLastThreadDump();
     }
 
+    /** {@inheritDoc} */
+    @Override public void calculateBuildStatistic(SingleBuildRunCtx ctx) {
+        if (calculatedStatistic().containsKey(ctx.buildId()))
+            return;
+
+        for (TestOccurrence testOccurrence : ctx.getTests()) {
+            addTestOccurrenceToStat(testOccurrence, normalizeBranch(ctx.getBuild()), !ctx.getChanges().isEmpty());
+        }
+
+        calculatedStatistic().put(ctx.buildId(), true);
+    }
+
     /**
      * @param cache
      * @param key
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/MultBuildRunCtx.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/MultBuildRunCtx.java
index 476048b..7b62e23 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/MultBuildRunCtx.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/MultBuildRunCtx.java
@@ -118,6 +118,10 @@ public String suiteName() {
         return firstBuildInfo.suiteName();
     }
 
+    public String buildTypeId() {
+        return firstBuildInfo.buildTypeId;
+    }
+
     public boolean hasNontestBuildProblem() {
         return problems != null && problems.stream().anyMatch(problem ->
             !problem.isFailedTests()
@@ -134,6 +138,10 @@ public boolean hasAnyBuildProblemExceptTestOrSnapshot() {
         return problems.stream().filter(p -> !p.isFailedTests() && !p.isShaphotDepProblem()).findAny();
     }
 
+    public List<SingleBuildRunCtx> getBuilds() {
+        return builds;
+    }
+
     public boolean hasTimeoutProblem() {
         return getExecutionTimeoutCount() > 0;
     }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/RunStat.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/RunStat.java
index 039d46c..ab02fd1 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/RunStat.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/RunStat.java
@@ -21,6 +21,8 @@
 import com.google.common.base.Objects;
 
 import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.annotation.Nullable;
 
 import org.apache.ignite.ci.db.Persisted;
@@ -29,33 +31,17 @@
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrence;
 import org.jetbrains.annotations.NotNull;
 
+import static org.apache.ignite.ci.analysis.RunStat.RunStatus.RES_CRITICAL_FAILURE;
+import static org.apache.ignite.ci.analysis.RunStat.RunStatus.RES_FAILURE;
+import static org.apache.ignite.ci.analysis.RunStat.RunStatus.RES_MUTED_FAILURE;
+import static org.apache.ignite.ci.analysis.RunStat.RunStatus.RES_OK;
+
 /**
  * Test or Build run statistics.
  */
 @Persisted
 public class RunStat {
     public static final int MAX_LATEST_RUNS = 100;
-    public static final int RES_OK = 0;
-
-    /**
-     * Result: general failure of test or suite.
-     */
-    public static final int RES_FAILURE = 1;
-
-    /**
-     * RES OK or RES FAILURE
-     */
-    public static final int RES_OK_OR_FAILURE = 10;
-
-    /**
-     * Result of test execution, muted failure found.
-     */
-    private static final int RES_MUTED_FAILURE = 2;
-
-    /**
-     * Result of suite: Critical failure, no results.
-     */
-    public static final int RES_CRITICAL_FAILURE = 3;
 
     /**
      * Runs registered all the times.
@@ -80,9 +66,16 @@
      */
     private String name;
 
+    /**
+     * @deprecated {@link #latestRuns} should be used.
+     */
+    @Deprecated
     @Nullable
     SortedMap<TestId, Integer> latestRunResults;
 
+    @Nullable
+    SortedMap<TestId, RunInfo> latestRuns;
+
     /**
      * @param name Name of test or suite.
      */
@@ -90,8 +83,8 @@ public RunStat(String name) {
         this.name = name;
     }
 
-    public void addTestRun(TestOccurrence testOccurrence) {
-        addTestRunToLatest(testOccurrence);
+    public void addTestRun(TestOccurrence testOccurrence, Boolean changesExist) {
+        addTestRunToLatest(testOccurrence, changesStatus(changesExist));
 
         runs++;
 
@@ -106,7 +99,14 @@ public void addTestRun(TestOccurrence testOccurrence) {
         lastUpdatedMs = System.currentTimeMillis();
     }
 
-    public void addTestRunToLatest(TestOccurrence testOccurrence) {
+    private ChangesState changesStatus(Boolean changesExist) {
+        if (changesExist == null)
+            return ChangesState.UNKNOWN;
+
+        return changesExist ? ChangesState.EXIST : ChangesState.NONE;
+    }
+
+    public void addTestRunToLatest(TestOccurrence testOccurrence, ChangesState changesState) {
         TestId id = extractFullId(testOccurrence.getId());
         if (id == null) {
             System.err.println("Unable to parse TestOccurrence.id: " + id);
@@ -114,7 +114,7 @@ public void addTestRunToLatest(TestOccurrence testOccurrence) {
             return;
         }
 
-        addRunToLatest(id, testToResCode(testOccurrence));
+        addRunToLatest(id, new RunInfo(testToResCode(testOccurrence), changesState));
     }
 
     private static TestId extractFullId(String id) {
@@ -147,29 +147,27 @@ public static Integer extractIdPrefixed(String id, String prefix, String postfix
             String substring = id.substring(absBuildIdx, absBuildIdx + buildIdEndIdx);
 
             return Integer.valueOf(substring);
-        } catch (Exception ignored) {
+        }
+        catch (Exception ignored) {
             return null;
         }
     }
 
-    private int testToResCode(TestOccurrence testOccurrence) {
-        int resCode;
-        if (testOccurrence.isFailedTest())
-            resCode = testOccurrence.isNotMutedOrIgnoredTest() ? RES_FAILURE : RES_MUTED_FAILURE;
-        else
-            resCode = RES_OK;
+    private RunStatus testToResCode(TestOccurrence testOccurrence) {
+        if (!testOccurrence.isFailedTest())
+            return RES_OK;
 
-        return resCode;
+        return testOccurrence.isNotMutedOrIgnoredTest() ? RES_FAILURE : RES_MUTED_FAILURE;
     }
 
-    private void addRunToLatest(TestId id, int resCode) {
-        if (latestRunResults == null)
-            latestRunResults = new TreeMap<>();
+    private void addRunToLatest(TestId id, RunInfo runInfo) {
+        if (latestRuns == null)
+            latestRuns = new TreeMap<>();
 
-        latestRunResults.put(id, resCode);
+        latestRuns.put(id, runInfo);
 
-        if (latestRunResults.size() > MAX_LATEST_RUNS)
-            latestRunResults.remove(latestRunResults.firstKey());
+        if (latestRuns.size() > MAX_LATEST_RUNS)
+            latestRuns.remove(latestRuns.firstKey());
     }
 
     public String name() {
@@ -210,7 +208,6 @@ public float getFailRate() {
         return 1.0f * getFailuresCount() / runs;
     }
 
-
     /**
      * @return float representing fail rate
      */
@@ -224,21 +221,21 @@ public float getCriticalFailRate() {
     }
 
     public int getFailuresCount() {
-        if (latestRunResults == null)
+        if (latestRuns == null)
             return 0;
 
-        return (int) latestRunResults.values().stream().filter(res -> res != RES_OK).count();
+        return (int)latestRuns.values().stream().filter(res -> res.status != RES_OK).count();
     }
 
     public int getCriticalFailuresCount() {
-        if (latestRunResults == null)
+        if (latestRuns == null)
             return 0;
 
-        return (int) latestRunResults.values().stream().filter(res -> res == RES_CRITICAL_FAILURE).count();
+        return (int)latestRuns.values().stream().filter(res -> res.status == RES_CRITICAL_FAILURE).count();
     }
 
     public int getRunsCount() {
-        return latestRunResults == null ? 0 : latestRunResults.size();
+        return latestRuns == null ? 0 : latestRuns.size();
     }
 
     public String getFailPercentPrintable() {
@@ -256,11 +253,12 @@ private static String getPercentPrintable(float percent) {
     public long getAverageDurationMs() {
         if (runsWithDuration == 0)
             return 0;
-        return (long) (1.0 * totalDurationMs / runsWithDuration);
+        return (long)(1.0 * totalDurationMs / runsWithDuration);
     }
 
     public void addBuildRun(Build build) {
         runs++;
+//        build.lastChanges
 
         //todo ? add duration from statistics
         /*
@@ -272,17 +270,17 @@ public void addBuildRun(Build build) {
         if (!build.isSuccess())
             failures++;
 
-        int resCode = build.isSuccess() ? RES_OK : RES_FAILURE;
+        RunStatus resCode = build.isSuccess() ? RES_OK : RES_FAILURE;
 
-        setBuildResCode(build.getId(), resCode);
+        setBuildResCode(build.getId(), new RunInfo(resCode, ChangesState.UNKNOWN));
     }
 
-    private void setBuildResCode(Integer buildId, int resCode) {
-        addRunToLatest(new TestId(buildId, 0), resCode);
+    private void setBuildResCode(Integer buildId, RunInfo runInfo) {
+        addRunToLatest(new TestId(buildId, 0), runInfo);
     }
 
     public void setBuildCriticalError(Integer bId) {
-        setBuildResCode(bId, RES_CRITICAL_FAILURE);
+        setBuildResCode(bId, new RunInfo(RES_CRITICAL_FAILURE, ChangesState.UNKNOWN));
     }
 
     /**
@@ -290,9 +288,9 @@ public void setBuildCriticalError(Integer bId) {
      */
     @Override public String toString() {
         return "RunStat{" +
-                "name='" + name + '\'' +
-                ", failRate=" + getFailPercentPrintable() + "%" +
-                '}';
+            "name='" + name + '\'' +
+            ", failRate=" + getFailPercentPrintable() + "%" +
+            '}';
     }
 
     /**
@@ -300,10 +298,10 @@ public void setBuildCriticalError(Integer bId) {
      */
     @Nullable
     public List<Integer> getLatestRunResults() {
-        if (latestRunResults == null)
+        if (latestRuns == null)
             return Collections.emptyList();
 
-        return new ArrayList<>(latestRunResults.values());
+        return latestRuns.values().stream().map(info -> info.status.code).collect(Collectors.toList());
     }
 
     private int[] concatArr(int[] arr1, int[] arr2) {
@@ -316,7 +314,7 @@ public void setBuildCriticalError(Integer bId) {
 
     @Nullable
     public TestId detectTemplate(EventTemplate t) {
-        if (latestRunResults == null)
+        if (latestRuns == null)
             return null;
 
         int centralEvtBuild = t.beforeEvent().length;
@@ -326,12 +324,12 @@ public TestId detectTemplate(EventTemplate t) {
         assert centralEvtBuild < template.length;
         assert centralEvtBuild >= 0;
 
-        Set<Map.Entry<TestId, Integer>> entries = latestRunResults.entrySet();
+        Set<Map.Entry<TestId, RunInfo>> entries = latestRuns.entrySet();
 
         if (entries.size() < template.length)
             return null;
 
-        List<Map.Entry<TestId, Integer>> histAsArr = new ArrayList<>(entries);
+        List<Map.Entry<TestId, RunInfo>> histAsArr = new ArrayList<>(entries);
 
         TestId detectedAt = null;
         if (t.shouldBeFirst()) {
@@ -352,18 +350,18 @@ public TestId detectTemplate(EventTemplate t) {
     }
 
     @Nullable
-    private TestId checkTemplateAtPos(int[] template, int centralEvtBuild, List<Map.Entry<TestId, Integer>> histAsArr,
+    private TestId checkTemplateAtPos(int[] template, int centralEvtBuild, List<Map.Entry<TestId, RunInfo>> histAsArr,
         int idx) {
         for (int tIdx = 0; tIdx < template.length; tIdx++) {
-            Integer curStatus = histAsArr.get(idx + tIdx).getValue();
+            RunInfo curStatus = histAsArr.get(idx + tIdx).getValue();
 
             if (curStatus == null)
                 break;
 
-            int tmpl = template[tIdx];
+            RunStatus tmpl = RunStatus.byCode(template[tIdx]);
 
-            if ((tmpl == RES_OK_OR_FAILURE && (curStatus == RES_OK || curStatus == RES_FAILURE))
-                || curStatus == tmpl) {
+            if ((tmpl == RunStatus.RES_OK_OR_FAILURE && (curStatus.status == RES_OK || curStatus.status == RES_FAILURE))
+                || curStatus.status == tmpl) {
                 if (tIdx == template.length - 1)
                     return histAsArr.get(idx + centralEvtBuild).getKey();
             }
@@ -380,24 +378,118 @@ public boolean isFlaky() {
 
     @Nullable
     public String getFlakyComments() {
-        if (latestRunResults == null)
+        if (latestRuns == null)
             return null;
 
         int statusChange = 0;
-        Integer prev = null;
-        for (Integer next : latestRunResults.values()) {
+
+        RunInfo prev = null;
+
+        for (RunInfo next : latestRuns.values()) {
             if (prev != null && next != null) {
-                if (!prev.equals(next))
+                if (!prev.status.equals(next.status)
+                    && next.changesState == ChangesState.NONE
+                    && prev.changesState != ChangesState.UNKNOWN)
                     statusChange++;
             }
             prev = next;
         }
 
-        if (statusChange <= 6)
+        if (statusChange < 1)
             return null;
 
         return "Test seems to be flaky: " +
-                    "change status [" + statusChange + "/" + latestRunResults.size() + "]";
+            "change status [" + statusChange + "/" + latestRuns.size() + "]";
+    }
+
+    /**
+     * Migrate data from latestRunResults to latestRuns.
+     *
+     * @deprecated need to be remove after migrate.
+     */
+    @Deprecated
+    public void migrateLatestRuns(){
+        if(latestRunResults == null)
+            return;
+
+        if (latestRuns == null)
+            latestRuns = new TreeMap<>();
+        else
+            latestRuns.clear();
+
+        for (Map.Entry<TestId, Integer> entry : latestRunResults.entrySet()) {
+            latestRuns.put(entry.getKey(), new RunInfo(RunStatus.byCode(entry.getValue()), ChangesState.UNKNOWN));
+        }
+    }
+
+    /**
+     * Status of run.
+     */
+    public enum RunStatus {
+        /** Result: success. */
+        RES_OK(0),
+        /** Result: general failure of test or suite. */
+        RES_FAILURE(1),
+        /** RES OK or RES FAILURE */
+        RES_OK_OR_FAILURE(10),
+        /** Result of test execution, muted failure found. */
+        RES_MUTED_FAILURE(2),
+        /** Result of suite: Critical failure, no results. */
+        RES_CRITICAL_FAILURE(3);
+
+        /** Mapping of status int -> object. */
+        private static Map<Integer, RunStatus> holder = Stream.of(values()).collect(Collectors.toMap(RunStatus::getCode, i -> i));
+
+        /** Represent status in int. */
+        int code;
+
+        /** */
+        RunStatus(int code) {
+            this.code = code;
+        }
+
+        /**
+         * @return Status as int.
+         */
+        public int getCode() {
+            return code;
+        }
+
+        /**
+         * @param code Status as int.
+         * @return Status of build.
+         */
+        public static RunStatus byCode(int code) {
+            return holder.get(code);
+        }
+    }
+
+    /** Changes state for run. */
+    public enum ChangesState {
+        /** Unknown number of changes for run. */
+        UNKNOWN,
+        /** Run without changes. */
+        NONE,
+        /** Run with changes. */
+        EXIST
+    }
+
+    /**
+     * Run info for storage in cache.
+     */
+    public static class RunInfo {
+        /** Status of run. */
+        RunStatus status;
+        /** State of changes for run. */
+        ChangesState changesState;
+
+        /**
+         *
+         */
+        public RunInfo(RunStatus status, ChangesState changesState) {
+            this.status = status;
+            this.changesState = changesState;
+        }
     }
 
     public static class TestId implements Comparable<TestId> {
@@ -425,9 +517,9 @@ public int getTestId() {
                 return true;
             if (o == null || getClass() != o.getClass())
                 return false;
-            TestId id = (TestId) o;
+            TestId id = (TestId)o;
             return buildId == id.buildId &&
-                    testId == id.testId;
+                testId == id.testId;
         }
 
         /**
@@ -457,9 +549,9 @@ public int getTestId() {
          */
         @Override public String toString() {
             return MoreObjects.toStringHelper(this)
-                    .add("buildId", buildId)
-                    .add("testId", testId)
-                    .toString();
+                .add("buildId", buildId)
+                .add("testId", testId)
+                .toString();
         }
     }
 
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/SingleBuildRunCtx.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/SingleBuildRunCtx.java
index af29a7c..8be28d5 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/SingleBuildRunCtx.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/analysis/SingleBuildRunCtx.java
@@ -28,6 +28,7 @@
 import org.apache.ignite.ci.tcmodel.changes.Change;
 import org.apache.ignite.ci.tcmodel.result.Build;
 import org.apache.ignite.ci.tcmodel.result.problems.ProblemOccurrence;
+import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrence;
 import org.apache.ignite.ci.util.FutureUtil;
 import org.jetbrains.annotations.Nullable;
 
@@ -43,10 +44,16 @@
 
     private List<Change> changes = new ArrayList<>();
 
+    private List<TestOccurrence> tests = new ArrayList<>();
+
     public SingleBuildRunCtx(Build build) {
         this.build = build;
     }
 
+    public Build getBuild() {
+        return build;
+    }
+
     public Integer buildId() {
         return build.getId();
     }
@@ -143,6 +150,14 @@ public void addChange(Change change) {
         return changes;
     }
 
+    public void setTests(List<TestOccurrence> tests) {
+        this.tests = tests;
+    }
+
+    public List<TestOccurrence> getTests() {
+        return tests;
+    }
+
     Stream<? extends Future<?>> getFutures() {
         if (logCheckResultsFut == null)
             return Stream.empty();
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/DbMigrations.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/DbMigrations.java
index e6f9817..e98865f 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/DbMigrations.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/DbMigrations.java
@@ -269,6 +269,24 @@ public void dataMigration(
             }
         });
 
+        applyMigration("latestRunResultsToLatestRuns", () -> {
+            System.out.println("Total entry for migrate : " + testHistCache.size());
+            int i = 0;
+            for (Cache.Entry<?, RunStat> next : testHistCache) {
+                TestInBranch key = (TestInBranch)next.getKey();
+                RunStat value = next.getValue();
+
+                value.migrateLatestRuns();
+
+                testHistCache.put(key, value);
+
+                if (i % 1000 == 0)
+                    System.out.println("Migrating entry: count : " + i);
+
+                i++;
+            }
+        });
+
         applyRemoveCache(GetTrackedBranchTestResults.ALL_TEST_FAILURES_SUMMARY);
         applyRemoveCache(Metrics.FAILURES_PUBLIC);
         applyRemoveCache(Metrics.FAILURES_PRIVATE);
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/issue/EventTemplates.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/issue/EventTemplates.java
index 973131d..2c1dab1 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/issue/EventTemplates.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/issue/EventTemplates.java
@@ -21,34 +21,37 @@
 import java.util.ArrayList;
 import org.apache.ignite.ci.analysis.RunStat;
 
+import static org.apache.ignite.ci.analysis.RunStat.RunStatus.RES_CRITICAL_FAILURE;
+import static org.apache.ignite.ci.analysis.RunStat.RunStatus.RES_FAILURE;
+import static org.apache.ignite.ci.analysis.RunStat.RunStatus.RES_OK;
+import static org.apache.ignite.ci.analysis.RunStat.RunStatus.RES_OK_OR_FAILURE;
+
 public class EventTemplates {
     public static final EventTemplate newFailure = new EventTemplate(
-            new int[]{RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK},
-            new int[]{RunStat.RES_FAILURE, RunStat.RES_FAILURE, RunStat.RES_FAILURE, RunStat.RES_FAILURE}
+            new int[]{RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode()},
+            new int[]{RES_FAILURE.getCode(), RES_FAILURE.getCode(), RES_FAILURE.getCode(), RES_FAILURE.getCode()}
     );
 
     public static final EventTemplate fixOfFailure = new EventTemplate(
-            new int[]{RunStat.RES_FAILURE, RunStat.RES_FAILURE, RunStat.RES_FAILURE},
-            new int[]{RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK}
+            new int[]{RES_FAILURE.getCode(), RES_FAILURE.getCode(), RES_FAILURE.getCode()},
+            new int[]{RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode()}
     );
 
     public static final EventTemplate newCriticalFailure = new EventTemplate(
-            new int[]{RunStat.RES_OK_OR_FAILURE, RunStat.RES_OK_OR_FAILURE, RunStat.RES_OK_OR_FAILURE,
-                    RunStat.RES_OK_OR_FAILURE, RunStat.RES_OK_OR_FAILURE},
-            new int[]{RunStat.RES_CRITICAL_FAILURE, RunStat.RES_CRITICAL_FAILURE,
-                    RunStat.RES_CRITICAL_FAILURE, RunStat.RES_CRITICAL_FAILURE}
+            new int[]{RES_OK_OR_FAILURE.getCode(), RES_OK_OR_FAILURE.getCode(), RES_OK_OR_FAILURE.getCode(), RES_OK_OR_FAILURE.getCode(), RES_OK_OR_FAILURE.getCode()},
+            new int[]{RES_CRITICAL_FAILURE.getCode(), RES_CRITICAL_FAILURE.getCode(), RES_CRITICAL_FAILURE.getCode(), RES_CRITICAL_FAILURE.getCode()}
     );
 
     public static final EventTemplate newContributedTestFailure = new EventTemplate(
             new int[]{},
-            new int[]{RunStat.RES_FAILURE, RunStat.RES_FAILURE, RunStat.RES_FAILURE, RunStat.RES_FAILURE}
+            new int[]{RES_FAILURE.getCode(), RES_FAILURE.getCode(), RES_FAILURE.getCode(), RES_FAILURE.getCode()}
     ).setShouldBeFirst(true);
 
     public static final EventTemplate newFailureForFlakyTest = new EventTemplate(
-            new int[]{RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK, RunStat.RES_OK},
-            new int[]{RunStat.RES_FAILURE, RunStat.RES_FAILURE, RunStat.RES_FAILURE,
-                    RunStat.RES_FAILURE, RunStat.RES_FAILURE, RunStat.RES_FAILURE,
-                    RunStat.RES_FAILURE, RunStat.RES_FAILURE}
+            new int[]{RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode(), RES_OK.getCode()},
+            new int[]{RES_FAILURE.getCode(), RES_FAILURE.getCode(), RES_FAILURE.getCode(),
+                    RES_FAILURE.getCode(), RES_FAILURE.getCode(), RES_FAILURE.getCode(),
+                    RES_FAILURE.getCode(), RES_FAILURE.getCode()}
     );
 
     public static ArrayList<EventTemplate> templates;
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/analysis/RunStatTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/analysis/RunStatTest.java
index 30f88bf..5c4ed46 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/analysis/RunStatTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/analysis/RunStatTest.java
@@ -20,6 +20,8 @@
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrence;
 import org.junit.Test;
 
+import static org.apache.ignite.ci.analysis.RunStat.ChangesState.UNKNOWN;
+
 public class RunStatTest {
     @Test
     public void testHistoricalIsExcluded() {
@@ -28,20 +30,20 @@ public void testHistoricalIsExcluded() {
         TestOccurrence occurrence = new TestOccurrence();
         occurrence.status = "SUCCESS";
         occurrence.setId("id:10231,build:(id:1103529)");
-        stat.addTestRunToLatest(occurrence);
+        stat.addTestRunToLatest(occurrence, UNKNOWN);
 
         assert stat.getLatestRunResults().contains(0);
 
         occurrence.status = "FAILED";
         occurrence.setId("id:10231,build:(id:1133529)");
-        stat.addTestRunToLatest(occurrence);
+        stat.addTestRunToLatest(occurrence, UNKNOWN);
         assert stat.getLatestRunResults().contains(0);
         assert stat.getLatestRunResults().contains(1);
 
         for (int i = 0; i < RunStat.MAX_LATEST_RUNS; i++) {
             occurrence.setId("id:10231,build:(id:" + 1133529 + i + ")");
 
-            stat.addTestRunToLatest(occurrence);
+            stat.addTestRunToLatest(occurrence, UNKNOWN);
         }
 
         assert !stat.getLatestRunResults().contains(0) : stat.getLatestRunResults();
@@ -52,7 +54,7 @@ public void testHistoricalIsExcluded() {
         for (int i = 0; i < RunStat.MAX_LATEST_RUNS; i++) {
             occurrence.setId("id:10231,build:(id:" + 1000 + i + ")");
             occurrence.status = "SUCCESS";
-            stat.addTestRunToLatest(occurrence);
+            stat.addTestRunToLatest(occurrence, UNKNOWN);
         }
 
         assert !stat.getLatestRunResults().contains(0) : stat.getLatestRunResults();
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/issue/DetectingFailureTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/issue/DetectingFailureTest.java
index 3e54bd3..3a3fe35 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/issue/DetectingFailureTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/issue/DetectingFailureTest.java
@@ -23,6 +23,7 @@
 import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
 
+import static org.apache.ignite.ci.analysis.RunStat.ChangesState.UNKNOWN;
 import static org.apache.ignite.ci.tcmodel.result.tests.TestOccurrence.STATUS_SUCCESS;
 import static org.junit.Assert.*;
 
@@ -34,14 +35,14 @@ public void detectFirstFailure() {
         TestOccurrence occurrence = new TestOccurrence().setStatus(STATUS_SUCCESS);
 
         for (int i = 0; i < 5; i++)
-            stat.addTestRunToLatest(occurrence.setId(fakeTestId(113 + i)));
+            stat.addTestRunToLatest(occurrence.setId(fakeTestId(113 + i)), UNKNOWN);
 
         occurrence.status = "FAILED";
 
         int firstFailedBuildId = 150;
 
         for (int i = 0; i < 5; i++)
-            stat.addTestRunToLatest(occurrence.setId(fakeTestId(firstFailedBuildId + i)));
+            stat.addTestRunToLatest(occurrence.setId(fakeTestId(firstFailedBuildId + i)), UNKNOWN);
 
         RunStat.TestId testId = stat.detectTemplate(EventTemplates.newFailure);
 
@@ -95,14 +96,14 @@ public void detectFlakyTest() {
         for (int i = 0; i < 50; i++) {
             occurrence.status = ints[i] == 0 ? Build.STATUS_SUCCESS : "FAILURE";
 
-            stat.addTestRunToLatest(occurrence.setId(fakeTestId(100 + i)));
+            stat.addTestRunToLatest(occurrence.setId(fakeTestId(100 + i)), UNKNOWN);
         }
 
         occurrence.status = "FAILED";
 
         int firstFailedBuildId = 150;
         for (int i = 0; i < 4; i++)
-            stat.addTestRunToLatest(occurrence.setId(fakeTestId(firstFailedBuildId + i)));
+            stat.addTestRunToLatest(occurrence.setId(fakeTestId(firstFailedBuildId + i)), UNKNOWN);
 
         assertTrue(stat.isFlaky());
 
@@ -124,20 +125,20 @@ public void detectNewContributedTestFailure() {
         for (int i = 0; i < results.length; i++) {
             statWithHist.addTestRunToLatest(occurrence
                 .setStatus(results[i] == 0 ? Build.STATUS_SUCCESS : "FAILURE")
-                .setId(fakeTestId(100 + i)));
+                .setId(fakeTestId(100 + i)), UNKNOWN);
         }
 
         occurrence.setStatus("FAILED");
 
         int firstFailedBuildId = 150;
         for (int i = 0; i < 15; i++)
-            statWithHist.addTestRunToLatest(occurrence.setId(fakeTestId(firstFailedBuildId + i)));
+            statWithHist.addTestRunToLatest(occurrence.setId(fakeTestId(firstFailedBuildId + i)), UNKNOWN);
 
         assertNull(statWithHist.detectTemplate(EventTemplates.newContributedTestFailure));
 
         RunStat contributedTestStat = new RunStat("");
         for (int i = 0; i < 5; i++)
-            contributedTestStat.addTestRunToLatest(occurrence.setId(fakeTestId(firstFailedBuildId + i)));
+            contributedTestStat.addTestRunToLatest(occurrence.setId(fakeTestId(firstFailedBuildId + i)), UNKNOWN);
 
         RunStat.TestId testId = contributedTestStat.detectTemplate(EventTemplates.newContributedTestFailure);
         assertNotNull(testId);


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services