This is an automated email from the ASF dual-hosted git repository. dpavlov pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git
The following commit(s) were added to refs/heads/master by this push: new a52f7cd IGNITE-10181 [Tc Bot] Add fail-rate handling for test's page - Fixes #63. a52f7cd is described below commit a52f7cd6e5fc3248e4c291eb9974ed9b4a70656d Author: ololo3000 <pmgheap....@gmail.com> AuthorDate: Sat Nov 10 20:07:51 2018 +0300 IGNITE-10181 [Tc Bot] Add fail-rate handling for test's page - Fixes #63. Signed-off-by: Dmitriy Pavlov <dpav...@apache.org> --- .../ignite/ci/web/model/hist/BuildsHistory.java | 47 ++++++----- .../src/main/webapp/comparison.html | 95 +++++++++++++--------- .../src/main/webapp/css/style-1.5.css | 62 +++++++++++++- 3 files changed, 140 insertions(+), 64 deletions(-) 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 index e85d34c..17abef0 100644 --- 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 @@ -73,7 +73,7 @@ public class BuildsHistory { private Date untilDateFilter; /** */ - private Map<String, Set<String>> mergedTestsBySuites = new ConcurrentHashMap<>(); + private Map<String, Map<String, Float>> mergedTestsBySuites = new ConcurrentHashMap<>(); /** */ private boolean skipTests; @@ -88,14 +88,14 @@ public class BuildsHistory { private static final Logger logger = LoggerFactory.getLogger(BuildsHistory.class); /** */ - public void initialize(ICredentialsProv prov, ServletContext context) { - final IStringCompactor compactor = CtxListener.getInjector(context).getInstance(IStringCompactor.class); + public void initialize(ICredentialsProv prov, ServletContext ctx) { + final IStringCompactor compactor = CtxListener.getInjector(ctx).getInstance(IStringCompactor.class); - ITcHelper tcHelper = CtxListener.getTcHelper(context); + ITcHelper tcHelper = CtxListener.getTcHelper(ctx); ITeamcity teamcity = tcHelper.server(srvId, prov); - ITeamcityIgnitedProvider tcIgnitedProv = CtxListener.getInjector(context) + ITeamcityIgnitedProvider tcIgnitedProv = CtxListener.getInjector(ctx) .getInstance(ITeamcityIgnitedProvider.class); ITeamcityIgnited ignitedTeamcity = tcIgnitedProv.server(srvId, prov); @@ -118,10 +118,10 @@ public class BuildsHistory { if (!skipTests) initFailedTests(teamcity, validBuilds); - ObjectMapper objectMapper = new ObjectMapper(); + ObjectMapper objMapper = new ObjectMapper(); try { - mergedTestsJson = objectMapper.writeValueAsString(mergedTestsBySuites); + mergedTestsJson = objMapper.writeValueAsString(mergedTestsBySuites); } catch (JsonProcessingException e) { throw new RuntimeException(e); } @@ -133,7 +133,7 @@ public class BuildsHistory { List<Future<BuildStatisticsSummary>> buildStaticsFutures = new ArrayList<>(); for (int buildId : buildIdsWithConditions.keySet()) { - Future<BuildStatisticsSummary> buildFuture = CompletableFuture.supplyAsync(() -> { + Future<BuildStatisticsSummary> buildFut = CompletableFuture.supplyAsync(() -> { BuildStatisticsSummary buildsStatistic = new BuildStatisticsSummary(buildId); buildsStatistic.isValid = buildIdsWithConditions.get(buildId); buildsStatistic.initialize(compactor, ignited); @@ -141,7 +141,7 @@ public class BuildsHistory { return buildsStatistic; }, teamcity.getExecutor()); - buildStaticsFutures.add(buildFuture); + buildStaticsFutures.add(buildFut); } buildStaticsFutures.forEach(new Consumer <Future<BuildStatisticsSummary>>() { @@ -192,7 +192,7 @@ public class BuildsHistory { List<Future<Void>> buildProcessorFutures = new ArrayList<>(); for (int buildId : buildIds) { - Future<Void> buildFuture = CompletableFuture.supplyAsync(() -> { + Future<Void> buildFut = CompletableFuture.supplyAsync(() -> { Map<Integer, String> configurations = getConfigurations(teamcity, buildId); Build build = teamcity.getBuild(teamcity.getBuildHrefById(buildId)); @@ -206,26 +206,31 @@ public class BuildsHistory { if(configurationName == null) continue; - Set<String> tests = mergedTestsBySuites.computeIfAbsent(configurationName, - k -> new HashSet<>()); + Map<String, Float> tests = mergedTestsBySuites.computeIfAbsent(configurationName, + k -> new HashMap<>()); - if (!tests.add(testOccurrence.getName())) - continue; + String testName = testOccurrence.getName(); + + if (!tests.containsKey(testName)) { + tests.put(testName, 0F); + + FullQueryParams key = new FullQueryParams(); - FullQueryParams key = new FullQueryParams(); + key.setServerId(srvId); + key.setProjectId(projectId); + key.setTestName(testOccurrence.getName()); + key.setSuiteId(configurationName); - key.setServerId(srvId); - key.setProjectId(projectId); - key.setTestName(testOccurrence.getName()); - key.setSuiteId(configurationName); + teamcity.getTestRef(key); + } - teamcity.getTestRef(key); + tests.put(testName, tests.get(testName) + 1F / buildIds.size()); } return null; }, teamcity.getExecutor()); - buildProcessorFutures.add(buildFuture); + buildProcessorFutures.add(buildFut); } buildProcessorFutures.forEach(v -> { diff --git a/ignite-tc-helper-web/src/main/webapp/comparison.html b/ignite-tc-helper-web/src/main/webapp/comparison.html index 4a5c37c..9d97977 100644 --- a/ignite-tc-helper-web/src/main/webapp/comparison.html +++ b/ignite-tc-helper-web/src/main/webapp/comparison.html @@ -152,13 +152,15 @@ <td class="total data 2" id="RunsCount2"></td> </tr> </table><br> -<table style="table-layout: fixed" id="testsTable" class="testsTable"> +<table id="testsTable" class="testsTable"> <tbody> <tr> - <th class="failedTestsHeader" width="13%">FAILED TESTS</th> - <th width="3%"><div class="switch-btn tests"></div></th> - <th width="42%"></th> - <th width="42%"></th> + <th class="testsTableTitleHeader">FAILED TESTS</th> + <th class="testsTableBtnHeader"><div class="switch-btn tests"></div></th> + <th class="testsTableDataHeader1"></th> + <th class="testsTableDataHeader2">Fail-rate treshold: + <input id="treshold" class="treshold" type="number" min="0" max="100" value="0" onchange="refreshTests()">% + </th> </tr> </tbody> </table> @@ -175,10 +177,16 @@ <div id="version"></div> <script> + const FAIL_RATE_DIFF_TRESHOLD = 0.1; + let invalidInclude = false, markBuildId = 0, testsTrigger = false; + function refreshTests() { + printTests(generateTestsResultsComparison(mergedTestsResults)); + } + function getDateFewWeeksAgo(numberOfWeeksAgo) { let date = new Date(); @@ -350,7 +358,7 @@ }); } - function mergeSuits(results) { + function mergeSuites(results) { let mergedSuites = new Set(); for (let key of Object.keys(results)) { @@ -364,19 +372,17 @@ function printTests(results) { $(TESTS_TABLE + " tr:not(:first-child)").remove(); - let markedRow = true; - - for (let suite of mergeSuits(results).sort()) { + for (let suite of mergeSuites(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; + let testsCnt = !obj.hasOwnProperty(suite) || Object.keys(obj[suite]).length == 0 ? + '' : Object.keys(obj[suite]).length; - testsCntCells = testsCntCells + '<td class="testsCntCell"><p id="' + suite + '">' + testLength + '</p></td>'; + testsCntCells = testsCntCells + '<td class="testsCntCell"><p id="' + suite + '">' + testsCnt + '</p></td>'; testsCells = testsCells + '<td class="testsCell">' + getSuiteTestsHtml(results, suite, key) + '</td>' } @@ -391,57 +397,68 @@ } } - function generateCompareTestsResults(results) { - let compareTestsResults = {}; + function generateTestsResultsComparison(results) { + let testsResultsComparison = {}; for (let key of Object.keys(results)) { - let uniqueObj = {}; + let obj = {}; for (let suite of Object.keys(results[key])) { - let allTests = []; + let otherTests = {}; - for (let key2 of Object.keys(results)) { - if (key == key2) + for (let otherKey of Object.keys(results)) { + if (key == otherKey) continue; - allTests = allTests.concat(results[key2][suite]); + otherTests = Object.assign({}, results[otherKey][suite]); } - let uniqTests = results[key][suite].filter(function(test) { - return allTests.indexOf(test) == -1; - }); + let newFailedTests = {}; - if (uniqTests.length != 0) - uniqueObj[suite] = uniqTests; + for (let testName of Object.keys(results[key][suite])) { + let failRate = results[key][suite][testName]; + let otherFailRate = otherTests[testName]; + + if ((otherFailRate == null || failRate - otherFailRate > FAIL_RATE_DIFF_TRESHOLD) + && failRate > $('#treshold').val()/100) + newFailedTests[testName] = failRate; + } + + if (Object.keys(newFailedTests).length != 0) + obj[suite] = newFailedTests; } - compareTestsResults[key] = uniqueObj; + testsResultsComparison[key] = obj; } - return compareTestsResults; + return testsResultsComparison; } function getSuiteTestsHtml(results, suite, key) { - if (!results[key].hasOwnProperty(suite) || results[key][suite].length === 0) + if (!results[key].hasOwnProperty(suite) || Object.keys(results[key][suite]).length == 0) return ''; - let res = '<body><div id="' + suite + key + '"style="cursor: default; margin-left: 10px;">'; + let res = '<table class="innerTestTable">\n'; - for (let test of results[key][suite].sort()) { - let list = test.toString().split("."); + for (let testName of Object.keys(results[key][suite]).sort()) { + let list = testName.toString().split("."); if (list.length < 2) - list = test.toString().split(":"); + list = testName.toString().split(":"); - let testName = list.pop(); + let testMethodName = list.pop(); let testClass = list.pop(); - - res += '<p align="left" title="' + test + '">' + testClass + '.' + testName + - '<a href="#" onclick="getTestRef(\'' + test + '\'' + ',\'' + suite + '\'); return false;">' + - ' >></a>' + '</p>' + let failRate = results[key][suite][testName]; + + res += '<tr>' + + '<td class="innerTestName"><p title="' + testName + '">' + testClass + '.' + testMethodName + '</p></td>' + + '<td class="innerFailRate"><p title="Test\'s fail-rate for corresponding date period">' + + Number((failRate * 100).toFixed(1)) + '%</p></td>' + + '<td class="innerTcLink"><a href="#" onclick="getTestRef(\'' + + testName + '\',\'' + suite + '\'); return false;"> >></a></td></tr>'; } - res += '</div></body>'; + res += '</table>'; return res; } @@ -571,7 +588,7 @@ printImportantMessage(num, "#ff0000", "Invalid server response. Unable to parse JSON"); } - printTests(generateCompareTestsResults(mergedTestsResults)); + printTests(generateTestsResultsComparison(mergedTestsResults)); }, error: showErrInLoadStatus, timeout: 1800000 @@ -1006,7 +1023,7 @@ updateColumn(2); if (isDefinedAndFilled(mergedTestsResults)) - printTests(generateCompareTestsResults(mergedTestsResults)); + printTests(generateTestsResultsComparison(mergedTestsResults)); } function multiFormat(date) { 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 7729192..13d83ed 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 @@ -313,6 +313,8 @@ form li:after } .testsCell { + padding-right: 2%; + padding-left: 2%; cursor: default; word-wrap: break-word; vertical-align: top; @@ -337,20 +339,72 @@ form li:after } .testsTable { + table-layout: fixed; width: 96%; border-collapse: collapse; margin-left: 2%; margin-right: 2%; } -.testsTable tr:nth-child(4n + 2) { - background-color: #fafaff; +.innerTestName { + width: 85%; + padding-right: 2%; + vertical-align: middle; + text-align: left; } -.failedTestsHeader { +.innerFailRate { + width: 10%; vertical-align: middle; + text-align: center; +} + +.innerTcLink { + width: 5%; + vertical-align: middle; + text-align: center; +} + +.testsTableBtnHeader { + width: 3%; + vertical-align: middle; +} + +.testsTableDataHeader1 { + width: 42%; + vertical-align: middle; + text-align: center; +} + +.testsTableDataHeader2 { + width: 42%; + vertical-align: middle; + text-align: right; +} + +.treshold { + width: 8%; +} + +.innerTestTable { + table-layout: fixed; + width: 96%; + border-collapse: collapse; + margin-left: 2%; + margin-right: 2%; +} + +.testsTable tr.testsCntRow:nth-child(4n + 2) { + background-color: #fafaff; +} + +.testsTableTitleHeader { text-align: left; - padding: 5px + width: 13%; + vertical-align: middle; + padding-left: 5px; + padding-bottom: 10px; + padding-top: 10px; } td.details-control {