ignite-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From GitBox <...@apache.org>
Subject [GitHub] ololo3000 closed pull request #25: IGNITE-9645 [TC Bot] Add comparison of failed tests lists in two date intervals
Date Thu, 11 Oct 2018 22:34:26 GMT
ololo3000 closed pull request #25: IGNITE-9645 [TC Bot] Add comparison of failed tests lists
in two date intervals
URL: https://github.com/apache/ignite-teamcity-bot/pull/25
 
 
   

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/ITeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
index 6aa3b60..b0157c8 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
@@ -36,15 +36,18 @@
 import org.apache.ignite.ci.tcmodel.conf.BuildType;
 import org.apache.ignite.ci.tcmodel.hist.BuildRef;
 import org.apache.ignite.ci.tcmodel.result.Build;
+import org.apache.ignite.ci.tcmodel.result.Configurations;
 import org.apache.ignite.ci.tcmodel.result.issues.IssuesUsagesList;
 import org.apache.ignite.ci.tcmodel.result.problems.ProblemOccurrences;
 import org.apache.ignite.ci.tcmodel.result.stat.Statistics;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrence;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrenceFull;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrences;
+import org.apache.ignite.ci.tcmodel.result.tests.TestRef;
 import org.apache.ignite.ci.tcmodel.user.User;
 import org.apache.ignite.ci.teamcity.pure.ITeamcityConn;
 import org.apache.ignite.ci.util.Base64Util;
+import org.apache.ignite.ci.web.rest.parms.FullQueryParams;
 import org.jetbrains.annotations.NotNull;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
@@ -152,8 +155,12 @@ default Build getBuild(int id) {
      */
     ProblemOccurrences getProblems(Build build);
 
+    ProblemOccurrences getProblems(BuildRef buildId);
+
     TestOccurrences getTests(String href, String normalizedBranch);
 
+    TestOccurrences getFailedUnmutedTestsNames(String href, int count, String normalizedBranch);
+
     Statistics getBuildStatistics(String href);
 
     CompletableFuture<TestOccurrenceFull> getTestFull(String href);
@@ -162,6 +169,10 @@ default Build getBuild(int id) {
 
     ChangesList getChangesList(String href);
 
+    CompletableFuture<TestRef> getTestRef(FullQueryParams key);
+
+    Configurations getConfigurations(FullQueryParams key);
+
     /**
      * List of build's related issues.
      *
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 d1e94e4..774e21b 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
@@ -66,16 +66,19 @@
 import org.apache.ignite.ci.tcmodel.conf.BuildType;
 import org.apache.ignite.ci.tcmodel.hist.BuildRef;
 import org.apache.ignite.ci.tcmodel.result.Build;
+import org.apache.ignite.ci.tcmodel.result.Configurations;
 import org.apache.ignite.ci.tcmodel.result.issues.IssuesUsagesList;
 import org.apache.ignite.ci.tcmodel.result.problems.ProblemOccurrences;
 import org.apache.ignite.ci.tcmodel.result.stat.Statistics;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrence;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrenceFull;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrences;
+import org.apache.ignite.ci.tcmodel.result.tests.TestRef;
 import org.apache.ignite.ci.tcmodel.user.User;
 import org.apache.ignite.ci.util.CacheUpdateUtil;
 import org.apache.ignite.ci.util.CollectionUtil;
 import org.apache.ignite.ci.util.ObjectInterner;
+import org.apache.ignite.ci.web.rest.parms.FullQueryParams;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -104,6 +107,8 @@
     private static final String BUILD_HIST_FINISHED = "buildHistFinished";
     private static final String BUILD_HIST_FINISHED_OR_FAILED = "buildHistFinishedOrFailed";
     public static final String BOT_DETECTED_ISSUES = "botDetectedIssues";
+    public static final String TEST_REFS = "testRefs";
+    public static final String CONFIGURATIONS = "configurations";
 
     //todo need separate cache or separate key for 'execution time' because it is placed
in statistics
     private static final String BUILDS_FAILURE_RUN_STAT = "buildsFailureRunStat";
@@ -125,6 +130,9 @@
      */
     private ConcurrentMap<String, CompletableFuture<TestOccurrenceFull>> testOccFullFutures
= new ConcurrentHashMap<>();
 
+    /** Cached loads of test refs.*/
+    private ConcurrentMap<String, CompletableFuture<TestRef>> testRefsFutures
= new ConcurrentHashMap<>();
+
     /** cached running builds for branch. */
     private ConcurrentMap<String, Expirable<List<BuildRef>>> queuedBuilds
= new ConcurrentHashMap<>();
 
@@ -162,7 +170,8 @@
                 buildProblemsCache(),
                 buildStatisticsCache(),
                 buildHistCache(),
-                buildHistIncFailedCache());
+                buildHistIncFailedCache(),
+                testRefsCache());
     }
 
     @Override
@@ -220,6 +229,20 @@ public User getUserByUsername(String username) {
         return getOrCreateCacheV2(ignCacheNme(TEST_FULL));
     }
 
+    /**
+     * @return {@link Configurations} instances cache, 32 parts.
+     */
+    private IgniteCache<String, Configurations> configurationsCache() {
+        return getOrCreateCacheV2(ignCacheNme(CONFIGURATIONS));
+    }
+
+    /**
+     * @return {@link TestRef} instances cache, 32 parts.
+     */
+    private IgniteCache<String, TestRef> testRefsCache() {
+        return getOrCreateCacheV2(ignCacheNme(TEST_REFS));
+    }
+
     /**
      * @return Build {@link ProblemOccurrences} instances cache, 32 parts.
      */
@@ -785,7 +808,26 @@ private Build realLoadBuild(String href1) {
             return new ProblemOccurrences();
     }
 
-    private void registerCriticalBuildProblemInStat(Build build, ProblemOccurrences problems)
{
+    /** {@inheritDoc}*/
+    @AutoProfiling
+    @Override public ProblemOccurrences getProblems(BuildRef buildRef) {
+        return loadIfAbsent(
+            buildProblemsCache(),
+            "app/rest/latest/problemOccurrences?locator=build:(id:" + buildRef.getId() +
")",
+            k -> {
+                ProblemOccurrences problems = teamcity.getProblems(buildRef);
+
+                registerCriticalBuildProblemInStat(buildRef, problems);
+
+                return problems;
+            });
+    }
+
+
+
+
+
+    private void registerCriticalBuildProblemInStat(BuildRef build, ProblemOccurrences problems)
{
         boolean criticalFail = problems.getProblemsNonNull().stream().anyMatch(occurrence
->
             occurrence.isExecutionTimeout() || occurrence.isJvmCrash());
 
@@ -827,6 +869,23 @@ private void registerCriticalBuildProblemInStat(Build build, ProblemOccurrences
             hrefIgnored -> teamcity.getTests(href, normalizedBranch));
     }
 
+    /** {@inheritDoc} */
+    @AutoProfiling
+    @Override public TestOccurrences getFailedUnmutedTestsNames(String href, int count, String
normalizedBranch) {
+        return getTests(href + ",muted:false,status:FAILURE,count:" + count + "&fields=testOccurrence(id,name)",
normalizedBranch);
+    }
+
+    /** {@inheritDoc} */
+    @AutoProfiling
+    @Override public Configurations getConfigurations(FullQueryParams key) {
+        return loadIfAbsent(configurationsCache(),
+            key.toString(),
+            k -> {
+                    return teamcity.getConfigurations(key);
+            });
+
+    }
+
     private void addTestOccurrencesToStat(TestOccurrences val) {
         for (TestOccurrence next : val.getTests())
             addTestOccurrenceToStat(next, ITeamcity.DEFAULT, null);
@@ -874,6 +933,16 @@ private void addTestOccurrencesToStat(TestOccurrences val) {
             });
     }
 
+    /** {@inheritDoc} */
+    @AutoProfiling
+    @Override public CompletableFuture<TestRef> getTestRef(FullQueryParams key) {
+        return CacheUpdateUtil.loadAsyncIfAbsent(
+            testRefsCache(),
+            key.toString(),
+            testRefsFutures,
+            k -> teamcity.getTestRef(key));
+    }
+
     /** {@inheritDoc} */
     @AutoProfiling
     @Override public Change getChange(String href) {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityConnection.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityConnection.java
index 0cb684b..41d79cd 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityConnection.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityConnection.java
@@ -62,11 +62,13 @@
 import org.apache.ignite.ci.tcmodel.hist.BuildRef;
 import org.apache.ignite.ci.tcmodel.hist.Builds;
 import org.apache.ignite.ci.tcmodel.result.Build;
+import org.apache.ignite.ci.tcmodel.result.Configurations;
 import org.apache.ignite.ci.tcmodel.result.issues.IssuesUsagesList;
 import org.apache.ignite.ci.tcmodel.result.problems.ProblemOccurrences;
 import org.apache.ignite.ci.tcmodel.result.stat.Statistics;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrenceFull;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrences;
+import org.apache.ignite.ci.tcmodel.result.tests.TestRef;
 import org.apache.ignite.ci.tcmodel.user.User;
 import org.apache.ignite.ci.tcmodel.user.Users;
 import org.apache.ignite.ci.teamcity.pure.ITeamcityHttpConnection;
@@ -75,6 +77,7 @@
 import org.apache.ignite.ci.util.UrlUtil;
 import org.apache.ignite.ci.util.XmlUtil;
 import org.apache.ignite.ci.util.ZipUtil;
+import org.apache.ignite.ci.web.rest.parms.FullQueryParams;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -422,6 +425,15 @@ public Build getBuild(String href) {
         return getJaxbUsingHref(href, Build.class);
     }
 
+    @AutoProfiling
+    @Override public ProblemOccurrences getProblems(BuildRef buildRef) {
+        ProblemOccurrences coll = getJaxbUsingHref("app/rest/latest/problemOccurrences?locator=build:(id:"
+ buildRef.getId() + ")", ProblemOccurrences.class);
+
+        coll.getProblemsNonNull().forEach(p -> p.buildRef = buildRef);
+
+        return coll;
+    }
+
     @Override
     @AutoProfiling
     public ProblemOccurrences getProblems(Build build) {
@@ -454,6 +466,30 @@ public ProblemOccurrences getProblems(Build build) {
         return supplyAsync(() -> getJaxbUsingHref(href, TestOccurrenceFull.class), executor);
     }
 
+    /** {@inheritDoc} */
+    @AutoProfiling
+    @Override public TestOccurrences getFailedUnmutedTestsNames(String href, int count, String
normalizedBranch) {
+        return getTests(href + ",muted:false,status:FAILURE,count:" + count + "&fields=testOccurrence(id,name)",
normalizedBranch);
+    }
+
+    /** {@inheritDoc} */
+    @AutoProfiling
+    @Override public CompletableFuture<TestRef> getTestRef(FullQueryParams key) {
+        return supplyAsync(() -> {
+            return getJaxbUsingHref("app/rest/latest/tests/name:" + key.getTestName(), TestRef.class);
+        }, executor);
+    }
+
+
+    /** {@inheritDoc} */
+    @AutoProfiling
+    @Override public Configurations getConfigurations(FullQueryParams key) {
+        return getJaxbUsingHref("app/rest/latest/builds?locator=snapshotDependency:(to:(id:"
+ key.getBuildId()
+                + "),includeInitial:true),defaultFilter:false,count:500",
+            Configurations.class);
+    }
+
+
     /** {@inheritDoc} */
     @AutoProfiling
     @Override public Change getChange(String href) {
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 1acb643..19f807b 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
@@ -42,6 +42,7 @@
 import org.apache.ignite.ci.tcmodel.result.stat.Statistics;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrenceFull;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrences;
+import org.apache.ignite.ci.tcmodel.result.tests.TestRef;
 import org.apache.ignite.ci.web.rest.Metrics;
 import org.apache.ignite.ci.web.rest.build.GetBuildTestFailures;
 import org.apache.ignite.ci.web.rest.pr.GetPrTestFailures;
@@ -113,7 +114,8 @@ public void dataMigration(
         Cache<String, ProblemOccurrences> problemsCache,
         Cache<String, Statistics> buildStatCache,
         Cache<SuiteInBranch, Expirable<List<BuildRef>>> buildHistCache,
-        Cache<SuiteInBranch, Expirable<List<BuildRef>>> buildHistInFailedCache)
{
+        Cache<SuiteInBranch, Expirable<List<BuildRef>>> buildHistInFailedCache,
+        Cache<String, TestRef> testRefsCache) {
 
         doneMigrations = doneMigrationsCache();
 
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/Configurations.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/Configurations.java
new file mode 100644
index 0000000..098a38d
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/Configurations.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ci.tcmodel.result;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.ignite.ci.tcmodel.hist.BuildRef;
+
+/**
+ */
+@XmlRootElement(name = "builds")
+public class Configurations {
+    /** */
+    @XmlElement(name = "build")
+    private List<BuildRef> builds;
+
+    /** */
+    public List<BuildRef> getBuilds() {
+        return builds == null ? new ArrayList<>() : builds;
+    }
+}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/tests/TestOccurrence.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/tests/TestOccurrence.java
index 43c91a7..f8de6da 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/tests/TestOccurrence.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/tests/TestOccurrence.java
@@ -98,4 +98,18 @@ public TestOccurrence setStatus(String status) {
 
         return this;
     }
+
+    /**
+     * @return BuildId which that test occurrence belongs to
+     */
+    public Integer getBuildId() {
+        if (id == null)
+            return null;
+
+        String[] list = id.split(":");
+
+        String str = list[list.length - 1];
+
+        return Integer.valueOf(str.substring(0, str.length() - 1));
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/tests/TestRef.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/tests/TestRef.java
index 29d369b..76f6a13 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/tests/TestRef.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/tests/TestRef.java
@@ -18,11 +18,13 @@
 package org.apache.ignite.ci.tcmodel.result.tests;
 
 import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
 import org.apache.ignite.ci.tcmodel.result.AbstractRef;
 
 /**
  * Reference to particular test
  */
+@XmlRootElement(name = "test")
 public class TestRef extends AbstractRef {
     @XmlAttribute public Long id;
     @XmlAttribute public String name;
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/BuildStatisticsSummary.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/BuildStatisticsSummary.java
index d4a7bf6..d3cd968 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/BuildStatisticsSummary.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/BuildStatisticsSummary.java
@@ -34,6 +34,7 @@
 import org.apache.ignite.ci.tcmodel.result.problems.ProblemOccurrence;
 import org.apache.ignite.ci.util.TimeUtil;
 import org.apache.ignite.ci.web.IBackgroundUpdatable;
+import org.apache.ignite.ci.web.rest.parms.FullQueryParams;
 
 /**
  * Summary of build statistics.
@@ -132,7 +133,7 @@ private long getOomeProblemCount(String buildTypeId) {
 
         for (BuildRef buildRef : builds)
             problemOccurrences.addAll(teamcity
-                .getProblems(teamcity.getBuild(buildRef.href))
+                .getProblems(buildRef)
                 .getProblemsNonNull());
 
         return problemOccurrences;
@@ -145,17 +146,12 @@ private long getOomeProblemCount(String buildTypeId) {
      * @param buildRef Build reference.
      */
     private List<BuildRef> getSnapshotDependencies(@Nonnull final ITeamcity teamcity,
BuildRef buildRef){
-        List<BuildRef> snapshotDependencies = new ArrayList<>();
+        FullQueryParams key = new FullQueryParams();
 
-        if (buildRef.isComposite()){
-            Build build = teamcity.getBuild(buildRef.href);
+        key.setServerId(teamcity.serverId());
+        key.setBuildId(buildRef.getId());
 
-            for (BuildRef snDep : build.getSnapshotDependenciesNonNull())
-                snapshotDependencies.addAll(getSnapshotDependencies(teamcity, snDep));
-        } else
-            snapshotDependencies.add(buildRef);
-
-        return snapshotDependencies;
+        return teamcity.getConfigurations(key).getBuilds();
     }
 
     /**
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/hist/BuildsHistory.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/hist/BuildsHistory.java
new file mode 100644
index 0000000..e4a309f
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/hist/BuildsHistory.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ci.web.model.hist;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.ServletContext;
+import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
+import org.apache.ignite.ci.ITcHelper;
+import org.apache.ignite.ci.tcbot.chain.BuildChainProcessor;
+import org.apache.ignite.ci.tcmodel.result.Build;
+import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrence;
+import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrences;
+import org.apache.ignite.ci.user.ICredentialsProv;
+import org.apache.ignite.ci.web.CtxListener;
+import org.apache.ignite.ci.web.model.current.BuildStatisticsSummary;
+import org.apache.ignite.ci.web.rest.exception.ServiceUnauthorizedException;
+import org.apache.ignite.ci.web.rest.parms.FullQueryParams;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * Builds History: includes statistic for every build and merged failed unmuted tests in
specified time interval.
+ */
+public class BuildsHistory {
+    /** */
+    private String srvId;
+
+    /** */
+    private String projectId;
+
+    /** */
+    private String buildTypeId;
+
+    /** */
+    private String branchName;
+
+    /** */
+    private Date sinceDateFilter;
+
+    /** */
+    private Date untilDateFilter;
+
+    /** */
+    private Map<String, Set<String>> mergedTestsBySuites = new HashMap<>();
+
+    /** */
+    private boolean skipTests;
+
+    /** */
+    public List<BuildStatisticsSummary> buildsStatistics = new ArrayList<>();
+
+    /** */
+    public String mergedTestsJson;
+
+    /** */
+    public void initialize(ICredentialsProv prov, ServletContext context) {
+        if (!prov.hasAccess(srvId))
+            throw ServiceUnauthorizedException.noCreds(srvId);
+
+        ITcHelper tcHelper = CtxListener.getTcHelper(context);
+
+        IAnalyticsEnabledTeamcity teamcity = tcHelper.server(srvId, prov);
+
+        int[] finishedBuildsIds = teamcity.getBuildNumbersFromHistory(buildTypeId, branchName,
+            sinceDateFilter, untilDateFilter);
+
+        initBuildsStatistics(teamcity, finishedBuildsIds);
+
+        if (!skipTests) {
+            initBuildsMergedFailedTests(teamcity, finishedBuildsIds);
+        }
+
+        ObjectMapper objectMapper = new ObjectMapper();
+
+        try {
+            mergedTestsJson = objectMapper.writeValueAsString(mergedTestsBySuites);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** */
+    private void initBuildsStatistics(IAnalyticsEnabledTeamcity teamcity, int[] buildIds)
{
+        for (int buildId : buildIds) {
+            BuildStatisticsSummary buildsStatistic = new BuildStatisticsSummary(buildId);
+
+            buildsStatistic.initialize(teamcity);
+
+            if (buildsStatistic != null && !buildsStatistic.isFakeStub)
+                buildsStatistics.add(buildsStatistic);
+        }
+    }
+
+
+    /** */
+    private void initBuildsMergedFailedTests(IAnalyticsEnabledTeamcity teamcity, int[] buildIds)
{
+        for (int buildId : buildIds) {
+            Map<Integer, String> configurations = new HashMap<>();
+
+            FullQueryParams key = new FullQueryParams();
+
+            key.setServerId(teamcity.serverId());
+            key.setBuildId(buildId);
+
+            teamcity.getConfigurations(key).getBuilds().forEach(buildRef -> {
+                Integer id = buildRef.getId();
+
+                String configurationName = buildRef.buildTypeId;
+
+                if (id != null && configurationName != null)
+                    configurations.put(id, configurationName);
+
+            });
+
+            Build build = teamcity.getBuild(teamcity.getBuildHrefById(buildId));
+
+            TestOccurrences testOccurrences = teamcity.getFailedUnmutedTestsNames(build.testOccurrences.href,
+                build.testOccurrences.failed, BuildChainProcessor.normalizeBranch(build.branchName));
+
+            for (TestOccurrence testOccurrence : testOccurrences.getTests()) {
+                String configurationName = configurations.get(testOccurrence.getBuildId());
+
+                if(configurationName == null)
+                    continue;
+
+                Set<String> tests = mergedTestsBySuites.computeIfAbsent(configurationName,
+                    k -> new HashSet<>());
+
+                if (!tests.add(testOccurrence.getName()))
+                    continue;
+
+                key = new FullQueryParams();
+
+                key.setServerId(srvId);
+                key.setProjectId(projectId);
+                key.setTestName(testOccurrence.getName());
+                key.setSuiteId(build.buildTypeId);
+
+                teamcity.getTestRef(key);
+            }
+        }
+    }
+
+    /** */
+    public BuildsHistory(Builder builder) {
+        this.skipTests = builder.skipTests;
+        this.srvId = builder.srvId;
+        this.buildTypeId = builder.buildTypeId;
+        this.branchName = builder.branchName;
+        this.sinceDateFilter = builder.sinceDate;
+        this.untilDateFilter = builder.untilDate;
+        this.projectId = builder.projectId;
+    }
+
+    /** */
+    public static class Builder {
+        /** */
+        private boolean skipTests = false;
+
+        /** */
+        private String projectId = "IgniteTests24Java8";
+
+        /** */
+        private String srvId = "apache";
+
+        /** */
+        private String buildTypeId = "IgniteTests24Java8_RunAll";
+
+        /** */
+        private String branchName = "refs/heads/master";
+
+        /** */
+        private Date sinceDate = null;
+
+        /** */
+        private Date untilDate = null;
+
+        /** */
+        private DateFormat dateFormat = new SimpleDateFormat("ddMMyyyyHHmmss");
+
+        /** */
+        public Builder server(String srvId) {
+            if (!isNullOrEmpty(srvId))
+                this.srvId = srvId;
+
+            return this;
+        }
+
+        /** */
+        public Builder buildType(String buildType) {
+            if (!isNullOrEmpty(buildType))
+                this.buildTypeId = buildType;
+
+            return this;
+        }
+
+        /** */
+        public Builder project(String projectId) {
+            if (!isNullOrEmpty(projectId))
+                this.projectId = projectId;
+
+            return this;
+        }
+
+        /** */
+        public Builder branch(String branchName) {
+            if (!isNullOrEmpty(branchName))
+                this.branchName = branchName;
+
+            return this;
+        }
+
+        /** */
+        public Builder sinceDate(String sinceDate) throws ParseException {
+            if (!isNullOrEmpty(sinceDate))
+                this.sinceDate = dateFormat.parse(sinceDate);
+
+            return this;
+        }
+
+        /** */
+        public Builder untilDate(String untilDate) throws ParseException {
+            if (!isNullOrEmpty(untilDate))
+                this.untilDate = dateFormat.parse(untilDate);
+
+            return this;
+        }
+
+        /** */
+        public Builder skipTests() {
+            this.skipTests = true;
+
+            return this;
+        }
+
+
+        /** */
+        public BuildsHistory build() {
+            return new BuildsHistory(this);
+        }
+    }
+}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetBuildTestFailures.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetBuildTestFailures.java
index 15692c8..fdfe218 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetBuildTestFailures.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetBuildTestFailures.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.ci.web.rest.build;
 
+import java.text.ParseException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import com.google.inject.Injector;
 import org.apache.ignite.ci.tcbot.chain.BuildChainProcessor;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
@@ -26,10 +29,11 @@
 import org.apache.ignite.ci.analysis.mode.LatestRebuildMode;
 import org.apache.ignite.ci.analysis.mode.ProcessLogsMode;
 import org.apache.ignite.ci.tcmodel.hist.BuildRef;
+import org.apache.ignite.ci.tcmodel.result.tests.TestRef;
 import org.apache.ignite.ci.user.ICredentialsProv;
+import org.apache.ignite.ci.web.model.hist.BuildsHistory;
 import org.apache.ignite.ci.web.BackgroundUpdater;
 import org.apache.ignite.ci.web.CtxListener;
-import org.apache.ignite.ci.web.model.current.BuildStatisticsSummary;
 import org.apache.ignite.ci.web.model.current.ChainAtServerCurrentStatus;
 import org.apache.ignite.ci.web.model.current.TestFailuresSummary;
 import org.apache.ignite.ci.web.model.current.UpdateInfo;
@@ -46,16 +50,8 @@
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
-import java.util.Date;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
 
 @Path(GetBuildTestFailures.BUILD)
 @Produces(MediaType.APPLICATION_JSON)
@@ -159,75 +155,65 @@ public TestFailuresSummary getBuildTestFails(
     }
 
     @GET
-    @Path("history")
-    public List<BuildStatisticsSummary> getBuildsHistory(
+    @Produces(MediaType.TEXT_PLAIN)
+    @Path("testRef")
+    public String getTestRef(
+        @NotNull @QueryParam("testName") String name,
+        @NotNull @QueryParam("suiteName") String suiteName,
         @Nullable @QueryParam("server") String srv,
-        @Nullable @QueryParam("buildType") String buildType,
-        @Nullable @QueryParam("branch") String branch,
-        @Nullable @QueryParam("sinceDate") String sinceDate,
-        @Nullable @QueryParam("untilDate") String untilDate)
-        throws ServiceUnauthorizedException {
-        String srvId = isNullOrEmpty(srv) ? "apache" : srv;
-        String buildTypeId = isNullOrEmpty(buildType) ? "IgniteTests24Java8_RunAll" : buildType;
-        String branchName = isNullOrEmpty(branch) ? "refs/heads/master" : branch;
-        Date sinceDateFilter = isNullOrEmpty(sinceDate) ? null : dateParse(sinceDate);
-        Date untilDateFilter = isNullOrEmpty(untilDate) ? null : dateParse(untilDate);
-
-        final BackgroundUpdater updater = CtxListener.getBackgroundUpdater(ctx);
-
-        final ITcHelper tcHelper = CtxListener.getTcHelper(ctx);
+        @Nullable @QueryParam("projectId") String projectId)
+        throws InterruptedException, ExecutionException, ServiceUnauthorizedException {
+        final ITcHelper helper = CtxListener.getTcHelper(ctx);
 
         final ICredentialsProv prov = ICredentialsProv.get(req);
 
-        IAnalyticsEnabledTeamcity teamcity = tcHelper.server(srvId, prov);
-
-        int[] finishedBuilds = teamcity.getBuildNumbersFromHistory(buildTypeId, branchName,
sinceDateFilter, untilDateFilter);
-
-        List<BuildStatisticsSummary> buildsStatistics = new ArrayList<>();
+        String project = projectId == null ? "IgniteTests24Java8" : projectId;
 
-        for (int i = 0; i < finishedBuilds.length; i++) {
-            int buildId = finishedBuilds[i];
+        String srvId = srv == null ? "apache" : srv;
 
-            FullQueryParams param = new FullQueryParams();
-            param.setBuildId(buildId);
-            param.setBranch(branchName);
-            param.setServerId(srvId);
+        if (!prov.hasAccess(srvId))
+            throw ServiceUnauthorizedException.noCreds(srvId);
 
-            BuildStatisticsSummary buildsStatistic = updater.get(
-                BUILDS_STATISTICS_SUMMARY_CACHE_NAME, prov, param,
-                (k) -> getBuildStatisticsSummaryNoCache(srvId, buildId), false);
+        IAnalyticsEnabledTeamcity teamcity = helper.server(srvId, prov);
 
-            if (!buildsStatistic.isFakeStub)
-                buildsStatistics.add(buildsStatistic);
-        }
+        FullQueryParams key = new FullQueryParams();
 
-        return buildsStatistics;
-    }
+        key.setTestName(name);
+        key.setProjectId(project);
+        key.setServerId(srvId);
+        key.setSuiteId(suiteName);
 
-    private Date dateParse(String date){
-        DateFormat dateFormat = new SimpleDateFormat("ddMMyyyyHHmmss");
+        CompletableFuture<TestRef> ref = teamcity.getTestRef(key);
 
-        try {
-            return dateFormat.parse(date);
-        }
-        catch (ParseException e) {
-            return null;
-        }
+        return ref.isDone() && !ref.isCompletedExceptionally() ? teamcity.host()
+ "project.html?"
+            + "projectId=" + project
+            + "&testNameId=" + ref.get().id
+            + "&tab=testDetails" : null;
     }
 
-    private BuildStatisticsSummary getBuildStatisticsSummaryNoCache(String server, int buildId)
{
-        String srvId = isNullOrEmpty(server) ? "apache" : server;
-
-        final ITcHelper tcHelper = CtxListener.getTcHelper(ctx);
-
-        final ICredentialsProv creds = ICredentialsProv.get(req);
+    @GET
+    @Path("history")
+    public BuildsHistory getBuildsHistory(
+        @Nullable @QueryParam("server") String srvId,
+        @Nullable @QueryParam("buildType") String buildType,
+        @Nullable @QueryParam("branch") String branch,
+        @Nullable @QueryParam("sinceDate") String sinceDate,
+        @Nullable @QueryParam("untilDate") String untilDate,
+        @Nullable @QueryParam("skipTests") String skipTests)  throws ParseException {
+        BuildsHistory.Builder builder = new BuildsHistory.Builder()
+            .branch(branch)
+            .server(srvId)
+            .buildType(buildType)
+            .sinceDate(sinceDate)
+            .untilDate(untilDate);
 
-        IAnalyticsEnabledTeamcity teamcity = tcHelper.server(srvId, creds);
+        if (Boolean.valueOf(skipTests))
+            builder.skipTests();
 
-        BuildStatisticsSummary stat = new BuildStatisticsSummary(buildId);
+        BuildsHistory buildsHistory = builder.build();
 
-        stat.initialize(teamcity);
+        buildsHistory.initialize(ICredentialsProv.get(req), ctx);
 
-        return stat;
+        return buildsHistory;
     }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/parms/FullQueryParams.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/parms/FullQueryParams.java
index 1026fb4..47fac6f 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/parms/FullQueryParams.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/parms/FullQueryParams.java
@@ -60,6 +60,12 @@
     /** TC identified base branch: null means the same as &lt;default>, master. For
not tracked branches. */
     @Nullable @QueryParam("baseBranchForTc") private String baseBranchForTc;
 
+    /** TC project identifier */
+    @Nullable @QueryParam("projectId") String projectId;
+
+    /** TC test name  */
+    @Nullable @QueryParam("testName") String testName;
+
     public FullQueryParams() {
     }
 
@@ -97,6 +103,14 @@ public FullQueryParams(String serverId, String suiteId, String branchForTc,
Stri
         return count;
     }
 
+    @Nullable public String getTestName() {
+        return testName;
+    }
+
+    @Nullable public String getProjectId() {
+        return projectId ;
+    }
+
     @Nullable public Boolean getCheckAllLogs() {
         return checkAllLogs;
     }
@@ -118,13 +132,15 @@ public FullQueryParams(String serverId, String suiteId, String branchForTc,
Stri
             Objects.equal(count, param.count) &&
             Objects.equal(checkAllLogs, param.checkAllLogs) &&
             Objects.equal(buildId, param.buildId) &&
+            Objects.equal(projectId, param.projectId) &&
+            Objects.equal(testName, param.testName) &&
             Objects.equal(baseBranchForTc, param.baseBranchForTc);
     }
 
     /** {@inheritDoc} */
     @Override public int hashCode() {
         return Objects.hashCode(branch, serverId, suiteId, branchForTc, action, count, checkAllLogs,
buildId,
-            baseBranchForTc);
+            baseBranchForTc, testName, projectId);
     }
 
     public void setBranch(@Nullable String branch) {
@@ -146,13 +162,27 @@ public void setCheckAllLogs(@Nullable Boolean checkAllLogs) {
             .add("checkAllLogs", checkAllLogs)
             .add("buildId", buildId)
             .add("baseBranchForTc", baseBranchForTc)
+            .add("projectId", projectId)
+            .add("testName", testName)
             .toString();
     }
 
+    public void setSuiteId(@Nonnull String suiteId) {
+        this.suiteId = suiteId;
+    }
+
     public void setCount(@Nullable int count) {
         this.count = count;
     }
 
+    public void setProjectId(@Nullable String projectId) {
+        this.projectId = projectId;
+    }
+
+    public void setTestName(@Nullable String testName) {
+        this.testName = testName;
+    }
+
     public void setBuildId(Integer buildId) {
         this.buildId = buildId;
     }
diff --git a/ignite-tc-helper-web/src/main/webapp/comparison.html b/ignite-tc-helper-web/src/main/webapp/comparison.html
index fd62a47..66ccd0f 100644
--- a/ignite-tc-helper-web/src/main/webapp/comparison.html
+++ b/ignite-tc-helper-web/src/main/webapp/comparison.html
@@ -17,7 +17,7 @@
 <body>
 <br>
 <br>
-<table class="compare"  width="100%">
+<table style="table-layout: fixed" class="compare">
     <tr>
         <th class="section"  width="15%">DATE INTERVAL</th>
         <th width="5%"></th>
@@ -148,15 +148,49 @@
         <td class="t2 data2" id="RunsCount2"></td>
     </tr>
 </table><br>
+<table style="table-layout: fixed" id="testsTable" class="testsTable">
+    <tbody>
+    <tr>
+        <th class="failedTestsHeader" width="15%">FAILED TESTS</th>
+        <th width="5%">
+            <label class="switch">
+                <input type="checkbox" onclick="toggleTests()">
+                <span class="slider"></span>
+            </label>
+        </th>
+        <th width="40%"></th>
+        <th width="40%"></th>
+        </tr>
+    </tbody>
+</table>
+
 <div id="version"></div>
 <script>
-    let oneWeekAgo = new Date(),
-        twoWeekAgo = new Date(),
-        g_updTimer = null;
+    const TESTS_TABLE = '#testsTable';
+    const SKIP_TESTS = 'skipTests=true';
+    let oneWeekAgo = new Date();
+    let twoWeekAgo = new Date();
+    let g_updTimer = null;
+    let testsTrigger = false;
+
+    /** Structure for storing tests by suites parsed response for every date interval. */
+    let mergedTestsResults = {1 : {}, 2 : {} };
+
 
     oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
     twoWeekAgo.setDate(twoWeekAgo.getDate() - 14);
 
+    let dateIntervals = {1: {start: moment(oneWeekAgo), end: moment()},
+        2: {start: moment(twoWeekAgo), end: moment(oneWeekAgo)}};
+
+    function toggleTests() {
+        testsTrigger = !testsTrigger;
+
+        loadData(1, dateIntervals[1].start, dateIntervals[1].end, testsTrigger);
+
+        loadData(2, dateIntervals[2].start, dateIntervals[2].end, testsTrigger);
+    }
+
     const parseTime = d3.timeParse("%d-%m-%YT%H:%M:%S"),
         formatTime = d3.timeFormat("%d-%m-%Y %H:%M:%S"),
         formatMillisecond = d3.timeFormat(".%L"),
@@ -224,6 +258,130 @@
             return parseFloat(stringMedian);
     }
 
+    var changeDisplay = function(id) {
+        $('*[id=' + id + ']').each(function() {
+            $(this).toggle();
+        });
+    }
+
+    function mergeSuits(results) {
+        let mergedSuites = new Set();
+
+        for (let key of Object.keys(results)) {
+            for (let suite of Object.keys(results[key]))
+                mergedSuites.add(suite);
+        }
+
+        return Array.from(mergedSuites);
+    }
+
+    function printTests(results) {
+        $(TESTS_TABLE + " tr:not(:first-child)").remove();
+
+        let markedRow = true;
+
+        for (let suite of mergeSuits(results).sort()) {
+            let suiteName = suite.split('_').filter((value, index) => index != 0).join('_');
+            let testsCntCells = '';
+            let testsCells = '';
+
+            for (let key of Object.keys(results)) {
+                let obj = results[key];
+                let testLength = !obj.hasOwnProperty(suite) || obj[suite].length == 0 ?
+                    '' : obj[suite].length;
+
+                testsCntCells = testsCntCells + '<td class="testsCntCell"><p id="'
+ suite + '">' + testLength + '</p></td>';
+
+                testsCells = testsCells + '<td class="testsCell">' + getSuiteTestsHtml(results,
suite, key) + '</td>'
+            }
+
+            $(TESTS_TABLE + " > tbody:last-child").append('<tr class="testsCntRow"
onclick="changeDisplay(\'' + suite + '\')">' +
+                '<td class = "suiteCell">' + suiteName + '</td>' +
+                '<td></td>' + testsCntCells + '</tr>');
+
+            $(TESTS_TABLE + " > tbody:last-child").append('<tr class="testsRow" id="'
+ suite + '">' +
+                '<td class="testsSuiteCell" onclick="changeDisplay(\'' + suite + '\')"></td>'
+
+                '<td></td>' + testsCells + '</tr>');
+        }
+    }
+
+    function generateCompareTestsResults(results) {
+        let compareTestsResults = {};
+
+        for (let key of  Object.keys(results)) {
+            let uniqueObj = {};
+
+            for (let suite of  Object.keys(results[key])) {
+                let allTests = [];
+
+                for (let key2 of Object.keys(results)) {
+                    if (key == key2)
+                        continue;
+
+                    allTests = allTests.concat(results[key2][suite]);
+                }
+
+                let uniqTests = results[key][suite].filter(function(test) {
+                    return allTests.indexOf(test) == -1;
+                });
+
+                if (uniqTests.length != 0)
+                    uniqueObj[suite] = uniqTests;
+            }
+
+            compareTestsResults[key] = uniqueObj;
+        }
+
+        return compareTestsResults;
+    }
+
+    function getSuiteTestsHtml(results, suite, key) {
+        if (!results[key].hasOwnProperty(suite) || results[key][suite].length == 0)
+            return '';
+
+        let res = '<body><div  id="' + suite + key + '"style="cursor: default; margin-left:
10px;">';
+
+        for (let test of results[key][suite].sort()) {
+            let list = test.toString().split(".");
+
+            if (list.length < 2)
+                list = test.toString().split(":");
+
+            let testName = list.pop();
+            let testClass = list.pop();
+
+            res += '<p align="left" title="' + test + '">' + testClass + '.' + testName
+
+                '<a href="#" onclick="getTestRef(\'' + test + '\'' + ',\'' + suite + '\');
return false;">' +
+                ' &gt&gt</a>' + '</p>'
+        }
+
+        res += '</div></body>';
+
+        return res;
+    }
+
+    function getTestRef(testName, suite) {
+        let res = null;
+
+        $.ajax({
+                async: false,
+                url: 'rest/build/testRef?testName=' + testName + '&suiteName=' + suite,
+                success: function (result) {
+                    res = result;
+                },
+                error: showErrInLoadStatus
+            }
+        );
+
+        if (res == null) {
+            alert("No available data for that test. Try later.");
+
+            return;
+        }
+
+        window.open(res);
+    }
+
     function printStatistics(num, map, sinceDate, untilDate) {
         clearBackgroundFromAllDataCells();
         clearGraphs(num);
@@ -325,8 +483,8 @@
     }
 
     $(document).ready(function() {
-        loadData(1, moment(oneWeekAgo), moment());
-        loadData(2, moment(twoWeekAgo), moment(oneWeekAgo));
+        loadData(1, moment(oneWeekAgo), moment(), testsTrigger);
+        loadData(2, moment(twoWeekAgo), moment(oneWeekAgo), testsTrigger);
 
         $.ajax({ url: "rest/branches/version",  success: showVersionInfo, error: showErrInLoadStatus
});
 
@@ -353,6 +511,7 @@
     
     function fillAllDataCells(num, message) {
         $('.data' + num).html(message);
+        $('.testsCntCell').html(message);
     }
 
     function clearGraphs(num) {
@@ -375,16 +534,32 @@
         $('#showInfo').css('display', '');
     }
 
-    function loadData(num, sinceDate, untilDate) {
+    function loadData(num, sinceDate, untilDate, testsTrigger) {
         loadGif(num);
-        $.ajax(
-            {
-                url: 'rest/build/history?sinceDate=' + sinceDate.format("DDMMYYYY") +
-                '000001&untilDate=' + untilDate.format("DDMMYYYY") + '235959',
+
+        mergedTestsResults[num] = {};
+
+        let url = 'rest/build/history?sinceDate=' + sinceDate.format("DDMMYYYY") +
+            '000001&untilDate=' + untilDate.format("DDMMYYYY") + '235959';
+
+        if (!testsTrigger)
+            url = url + '&' + SKIP_TESTS;
+
+        $.ajax({
+                url: url,
                 success: function (result) {
-                    printStatistics(num, result, sinceDate, untilDate);
+                    printStatistics(num, result.buildsStatistics, sinceDate, untilDate);
+
+                    try {
+                        mergedTestsResults[num] = JSON.parse(result.mergedTestsJson);
+                    } catch (e) {
+                        printImportantMessage(num, "#ff0000", "Invalid server response. Unable
to parse JSON");
+                    }
+
+                    printTests(generateCompareTestsResults(mergedTestsResults));
                 },
-                error: showErrInLoadStatus
+                error: showErrInLoadStatus,
+                timeout: 1800000
             }
         );
     }
@@ -392,14 +567,22 @@
     $(function() {
         $('input[name="daterange1"]').daterangepicker(
             dateRangePickerParam(oneWeekAgo, new Date()), function (start, end, label) {
-                loadData(1, start, end);
+                dateIntervals[1].start = start;
+
+                dateIntervals[1].end = end;
+
+                loadData(1, start, end, testsTrigger);
             });
     });
 
     $(function() {
         $('input[name="daterange2"]').daterangepicker(
             dateRangePickerParam(twoWeekAgo, oneWeekAgo), function (start, end, label) {
-                loadData(2, start, end);
+                dateIntervals[2].start = start;
+
+                dateIntervals[2].end = end;
+
+                loadData(2, start, end, testsTrigger);
             });
     });
 
diff --git a/ignite-tc-helper-web/src/main/webapp/css/style-1.5.css b/ignite-tc-helper-web/src/main/webapp/css/style-1.5.css
index ca6d537..f06b8e4 100644
--- a/ignite-tc-helper-web/src/main/webapp/css/style-1.5.css
+++ b/ignite-tc-helper-web/src/main/webapp/css/style-1.5.css
@@ -249,6 +249,52 @@ form li:after
 	background-color: #fafaff;
 }
 
+.testsCntCell {
+	cursor: pointer;
+	text-align: center;
+	vertical-align: middle;
+}
+
+.testsCell {
+	cursor: default;
+	word-wrap: break-word;
+	vertical-align: top;
+}
+
+.testsRow {
+	display: none;
+}
+
+.suiteCell {
+	cursor: default;
+	vertical-align: middle;
+	padding: 15px;
+}
+
+.testsSuiteCell {
+	cursor: pointer;
+}
+
+.testsCntRow {
+	padding: 10px
+}
+
+.testsTable {
+	width: 96%;
+	border-collapse: collapse;
+	margin-left: 2%;
+	margin-right: 2%;
+}
+
+.testsTable tr:nth-child(4n + 2) {
+	background-color: #fafaff;
+}
+
+.failedTestsHeader {
+	vertical-align: middle;
+	text-align: left;
+	padding: 5px
+}
 
 td.details-control {
 	//background: url('../resources/details_open.png') no-repeat center center;
@@ -306,3 +352,50 @@ div.tooltip {
 	height: auto;
 }
 
+.switch {
+	position: relative;
+	display: inline-block;
+	width: 60px;
+	height: 34px;
+}
+
+.switch input {display:none;}
+
+.slider {
+	position: absolute;
+	cursor: pointer;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	background-color: #ccc;
+	-webkit-transition: .4s;
+	transition: .4s;
+}
+
+.slider:before {
+	position: absolute;
+	content: "";
+	height: 26px;
+	width: 26px;
+	left: 4px;
+	bottom: 4px;
+	background-color: white;
+	-webkit-transition: .4s;
+	transition: .4s;
+}
+
+input:checked + .slider {
+	background-color: #12AD5E;
+}
+
+input:focus + .slider {
+	box-shadow: 0 0 1px #12AD5E;
+}
+
+input:checked + .slider:before {
+	-webkit-transform: translateX(26px);
+	-ms-transform: translateX(26px);
+	transform: translateX(26px);
+}
+


 

----------------------------------------------------------------
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

Mime
View raw message