You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by dp...@apache.org on 2018/10/24 12:15:50 UTC
[ignite-teamcity-bot] 01/02: IGNITE-9645 Add comparison of failed
tests lists in two date intervals - Fixes #36.
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
commit 3f369509c308163a6495b5e6569773d8d39ad711
Author: ololo3000 <pm...@gmail.com>
AuthorDate: Wed Oct 24 15:15:15 2018 +0300
IGNITE-9645 Add comparison of failed tests lists in two date intervals - Fixes #36.
Signed-off-by: Dmitriy Pavlov <dp...@apache.org>
---
.../main/java/org/apache/ignite/ci/ITeamcity.java | 14 +-
.../apache/ignite/ci/IgnitePersistentTeamcity.java | 80 +++--
.../apache/ignite/ci/IgniteTeamcityConnection.java | 48 ++-
.../java/org/apache/ignite/ci/db/DbMigrations.java | 4 +-
.../ignite/ci/tcmodel/result/Configurations.java | 54 ++++
.../ci/tcmodel/result/tests/TestOccurrence.java | 8 +
.../ignite/ci/tcmodel/result/tests/TestRef.java | 2 +
.../web/model/current/BuildStatisticsSummary.java | 16 +-
.../ignite/ci/web/model/hist/BuildsHistory.java | 334 +++++++++++++++++++++
.../ci/web/rest/build/GetBuildTestFailures.java | 110 +++----
.../ignite/ci/web/rest/parms/FullQueryParams.java | 32 +-
.../src/main/webapp/comparison.html | 240 ++++++++++++++-
.../src/main/webapp/css/style-1.5.css | 128 ++++++++
13 files changed, 954 insertions(+), 116 deletions(-)
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..628581f 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
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -36,15 +37,18 @@ import org.apache.ignite.ci.tcmodel.changes.ChangesList;
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;
@@ -150,10 +154,12 @@ public interface ITeamcity extends ITeamcityConn {
* @param build
* @return
*/
- ProblemOccurrences getProblems(Build build);
+ ProblemOccurrences getProblems(BuildRef build);
TestOccurrences getTests(String href, String normalizedBranch);
+ TestOccurrences getFailedTests(String href, int count, String normalizedBranch);
+
Statistics getBuildStatistics(String href);
CompletableFuture<TestOccurrenceFull> getTestFull(String href);
@@ -162,6 +168,10 @@ public interface ITeamcity extends ITeamcityConn {
ChangesList getChangesList(String href);
+ CompletableFuture<TestRef> getTestRef(FullQueryParams key);
+
+ Configurations getConfigurations(FullQueryParams key);
+
/**
* List of build's related issues.
*
@@ -251,6 +261,8 @@ public interface ITeamcity extends ITeamcityConn {
void setExecutor(ExecutorService pool);
+ Executor getExecutor();
+
/**
* @param tok TeamCity authorization token.
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..60f61ba 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
@@ -32,6 +32,7 @@ import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -66,16 +67,19 @@ import org.apache.ignite.ci.tcmodel.changes.ChangesList;
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 +108,8 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
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 +131,9 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
*/
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 +171,8 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
buildProblemsCache(),
buildStatisticsCache(),
buildHistCache(),
- buildHistIncFailedCache());
+ buildHistIncFailedCache(),
+ testRefsCache());
}
@Override
@@ -221,6 +231,20 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
}
/**
+ * @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.
*/
private IgniteCache<String, ProblemOccurrences> buildProblemsCache() {
@@ -766,26 +790,20 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
/** {@inheritDoc}*/
@AutoProfiling
- @Override public ProblemOccurrences getProblems(Build build) {
- if (build.problemOccurrences != null) {
- String href = build.problemOccurrences.href;
-
- return loadIfAbsent(
- buildProblemsCache(),
- href,
- k -> {
- ProblemOccurrences problems = teamcity.getProblems(build);
+ @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(build, problems);
+ registerCriticalBuildProblemInStat(buildRef, problems);
- return problems;
- });
- }
- else
- return new ProblemOccurrences();
+ return problems;
+ });
}
- private void registerCriticalBuildProblemInStat(Build build, ProblemOccurrences problems) {
+ private void registerCriticalBuildProblemInStat(BuildRef build, ProblemOccurrences problems) {
boolean criticalFail = problems.getProblemsNonNull().stream().anyMatch(occurrence ->
occurrence.isExecutionTimeout() || occurrence.isJvmCrash());
@@ -827,6 +845,20 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
hrefIgnored -> teamcity.getTests(href, normalizedBranch));
}
+ /** {@inheritDoc} */
+ @AutoProfiling
+ @Override public TestOccurrences getFailedTests(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 -> teamcity.getConfigurations(key));
+ }
+
private void addTestOccurrencesToStat(TestOccurrences val) {
for (TestOccurrence next : val.getTests())
addTestOccurrenceToStat(next, ITeamcity.DEFAULT, null);
@@ -876,6 +908,16 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
/** {@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) {
return loadIfAbsentV2(CHANGE_INFO_FULL, href, href1 -> {
try {
@@ -1119,6 +1161,10 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
});
}
+ public Executor getExecutor() {
+ return this.teamcity.getExecutor();
+ }
+
/** {@inheritDoc} */
@Override public void setExecutor(ExecutorService executor) {
this.teamcity.setExecutor(executor);
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..892fd54 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.conf.bt.BuildTypeFull;
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.HttpUtil;
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;
@@ -122,6 +125,12 @@ public class IgniteTeamcityConnection implements ITeamcity {
/** Build logger processing running. */
private ConcurrentHashMap<Integer, CompletableFuture<LogCheckTask>> buildLogProcessingRunning = new ConcurrentHashMap<>();
+ private static int MAX_CFG_CNT = 500;
+
+ public Executor getExecutor() {
+ return executor;
+ }
+
/** {@inheritDoc} */
public void init(@Nullable String tcName) {
this.tcName = tcName;
@@ -422,18 +431,14 @@ public class IgniteTeamcityConnection implements ITeamcity {
return getJaxbUsingHref(href, Build.class);
}
- @Override
@AutoProfiling
- public ProblemOccurrences getProblems(Build build) {
- if (build.problemOccurrences != null) {
- ProblemOccurrences coll = getJaxbUsingHref(build.problemOccurrences.href, ProblemOccurrences.class);
+ @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 = build);
+ coll.getProblemsNonNull().forEach(p -> p.buildRef = buildRef);
- return coll;
- }
- else
- return new ProblemOccurrences();
+ return coll;
}
/** {@inheritDoc} */
@@ -456,6 +461,31 @@ public class IgniteTeamcityConnection implements ITeamcity {
/** {@inheritDoc} */
@AutoProfiling
+ @Override public TestOccurrences getFailedTests(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) {
+ Configurations configurations = getJaxbUsingHref("app/rest/latest/builds?locator=snapshotDependency:(to:(id:" + key.getBuildId()
+ + "),includeInitial:true),defaultFilter:false,count:" + MAX_CFG_CNT, Configurations.class);
+
+ return configurations.setBuild(key.getBuildId());
+ }
+
+
+ /** {@inheritDoc} */
+ @AutoProfiling
@Override public Change getChange(String href) {
return getJaxbUsingHref(href, Change.class);
}
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.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.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 class DbMigrations {
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..eb62c46
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/result/Configurations.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+ }
+
+ /** BuildId which that configurations belong to. */
+ private Integer build;
+
+ /** */
+ public Configurations setBuild(Integer build) {
+ this.build = build;
+
+ return this;
+ }
+
+ /** */
+ public Integer getBuild() {
+ return build;
+ }
+}
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..9a3c807 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
@@ -20,6 +20,7 @@ package org.apache.ignite.ci.tcmodel.result.tests;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
+import org.apache.ignite.ci.analysis.RunStat;
/**
* Test occurrence. Can be provided by build as list of occurrences.
@@ -98,4 +99,11 @@ public class TestOccurrence {
return this;
}
+
+ /**
+ * @return BuildId which that test occurrence belongs to
+ */
+ public Integer getBuildId() {
+ return RunStat.extractIdPrefixed(id, "build:(id:", ")");
+ }
}
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.TestOccurrencesRef;
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 @@ public class BuildStatisticsSummary extends UpdateInfo implements IBackgroundUpd
for (BuildRef buildRef : builds)
problemOccurrences.addAll(teamcity
- .getProblems(teamcity.getBuild(buildRef.href))
+ .getProblems(buildRef)
.getProblemsNonNull());
return problemOccurrences;
@@ -145,17 +146,12 @@ public class BuildStatisticsSummary extends UpdateInfo implements IBackgroundUpd
* @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..90b78f0
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/hist/BuildsHistory.java
@@ -0,0 +1,334 @@
+/*
+ * 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.io.UncheckedIOException;
+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 java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.function.Consumer;
+import javax.servlet.ServletContext;
+import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
+import org.apache.ignite.ci.ITcHelper;
+import org.apache.ignite.ci.ITeamcity;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 ConcurrentHashMap<>();
+
+ /** */
+ private boolean skipTests;
+
+ /** */
+ public List<BuildStatisticsSummary> buildsStatistics = new ArrayList<>();
+
+ /** */
+ public String mergedTestsJson;
+
+ /** */
+ private static final Logger logger = LoggerFactory.getLogger(BuildsHistory.class);
+
+ /** */
+ 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);
+
+ initStatistics(teamcity, finishedBuildsIds);
+
+ if (!skipTests) {
+ initFailedTests(teamcity, finishedBuildsIds);
+ }
+
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ try {
+ mergedTestsJson = objectMapper.writeValueAsString(mergedTestsBySuites);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** */
+ private void initStatistics(IAnalyticsEnabledTeamcity teamcity, int[] buildIds) {
+ List<Future<BuildStatisticsSummary>> buildStatiscsFutures = new ArrayList<>();
+
+ for (int buildId : buildIds) {
+ Future<BuildStatisticsSummary> buildFuture = CompletableFuture.supplyAsync(() -> {
+ BuildStatisticsSummary buildsStatistic = new BuildStatisticsSummary(buildId);
+
+ buildsStatistic.initialize(teamcity);
+
+ return buildsStatistic;
+
+ }, teamcity.getExecutor());
+
+ buildStatiscsFutures.add(buildFuture);
+ }
+
+ buildStatiscsFutures.forEach(new Consumer<Future<BuildStatisticsSummary>>() {
+ @Override public void accept(Future<BuildStatisticsSummary> v) {
+ try {
+ BuildStatisticsSummary buildsStatistic = v.get();
+
+ if (buildsStatistic != null && !buildsStatistic.isFakeStub)
+ buildsStatistics.add(buildsStatistic);
+ }
+ catch (ExecutionException e) {
+ if (e.getCause() instanceof UncheckedIOException)
+ logger.error(e.getStackTrace().toString());
+
+ else
+ throw new RuntimeException(e);
+ }
+ catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ /** */
+ private Map<Integer, String> getConfigurations(ITeamcity teamcity, int buildId) {
+ 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);
+ });
+
+ return configurations;
+ }
+
+ /** */
+ private void initFailedTests(IAnalyticsEnabledTeamcity teamcity, int[] buildIds) {
+ List<Future<Void>> buildProcessorFutures = new ArrayList<>();
+
+ for (int buildId : buildIds) {
+ Future<Void> buildFuture = CompletableFuture.supplyAsync(() -> {
+ Map<Integer, String> configurations = getConfigurations(teamcity, buildId);
+
+ Build build = teamcity.getBuild(teamcity.getBuildHrefById(buildId));
+
+ TestOccurrences testOccurrences = teamcity.getFailedTests(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;
+
+ FullQueryParams key = new FullQueryParams();
+
+ key.setServerId(srvId);
+ key.setProjectId(projectId);
+ key.setTestName(testOccurrence.getName());
+ key.setSuiteId(configurationName);
+
+ teamcity.getTestRef(key);
+ }
+
+ return null;
+ }, teamcity.getExecutor());
+
+ buildProcessorFutures.add(buildFuture);
+ }
+
+ buildProcessorFutures.forEach(v -> {
+ try {
+ v.get();
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof UncheckedIOException)
+ logger.error(e.getStackTrace().toString());
+
+ else
+ throw new RuntimeException(e);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ /** */
+ 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.FullChainRunCtx;
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.Produces;
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 class GetBuildTestFailures {
}
@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 @@ public class FullQueryParams {
/** TC identified base branch: null means the same as <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 class FullQueryParams {
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 class FullQueryParams {
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 class FullQueryParams {
.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..392e71c 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,64 @@
<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="myModal" class="modal">
+
+ <!-- Modal content -->
+ <div class="modal-content">
+ <span class="close">×</span>
+ <p id="tooltipText"></p>
+ </div>
+
+</div>
+
<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 showTooltip(message) {
+ $("#tooltipText").html(message);
+ modal.show();
+ }
+
+ 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 +273,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;">' +
+ ' >></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) {
+ showTooltip('Link to TC test history is not ready yet. Try later.');
+
+ return;
+ }
+
+ window.open(res);
+ }
+
function printStatistics(num, map, sinceDate, untilDate) {
clearBackgroundFromAllDataCells();
clearGraphs(num);
@@ -325,8 +498,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 +526,7 @@
function fillAllDataCells(num, message) {
$('.data' + num).html(message);
+ $('.testsCntCell').html(message);
}
function clearGraphs(num) {
@@ -375,16 +549,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 +582,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);
});
});
@@ -545,6 +743,18 @@
}
});
}
+
+ // Get the modal
+ var modal = $('#myModal');
+
+ // Get the <span> element that closes the modal
+ var span = $(".close");
+
+ // When the user clicks on <span> (x), close the modal
+ span.click(function() {
+ modal.hide();
+ })
+
</script>
<div style="visibility:hidden"><div id="modalDialog" title="Information"></div></div>
</body>
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..c5aa999 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,85 @@ 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);
+}
+
+.modal {
+ display: none;
+ position: fixed;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.modal-content {
+ margin-top: 100px;
+ margin-left: auto;
+ margin-right: auto;
+ text-align: center;
+ background-color: #fefefe;
+ padding: 10px;
+ border: 5px solid #12AD5E;
+ width: 30%;
+}
+
+.close {
+ color: #12AD5E;
+ float: right;
+ font-size: 28px;
+ font-weight: bold;
+}
+
+.close:hover,
+.close:focus {
+ color: black;
+ text-decoration: none;
+ cursor: pointer;
+}
+