1// Copyright (C) 2013 Google Inc. All rights reserved. 2// 3// Redistribution and use in source and binary forms, with or without 4// modification, are permitted provided that the following conditions are 5// met: 6// 7// * Redistributions of source code must retain the above copyright 8// notice, this list of conditions and the following disclaimer. 9// * Redistributions in binary form must reproduce the above 10// copyright notice, this list of conditions and the following disclaimer 11// in the documentation and/or other materials provided with the 12// distribution. 13// * Neither the name of Google Inc. nor the names of its 14// contributors may be used to endorse or promote products derived from 15// this software without specific prior written permission. 16// 17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29var overview = overview || {}; 30 31(function() { 32 33overview._resultsByTestType = {}; 34overview._testTypeIndex = 0; 35 36// FIXME: This is a gross hack to make it so that changing the test type in loadNextTestType doesn't reload the page. 37history.reloadRequiringParameters = history.reloadRequiringParameters.filter(function(item) { return item != 'testType'; }); 38 39overview.loadNextTestType = function(historyInstance) 40{ 41 if (overview._testTypeIndex == builders.testTypes.length) { 42 overview._generatePage(); 43 return; 44 } 45 46 historyInstance.crossDashboardState.testType = builders.testTypes[overview._testTypeIndex++]; 47 48 $('content').innerHTML = (overview._testTypeIndex - 1) + '/' + builders.testTypes.length + ' loaded. Loading ' + historyInstance.crossDashboardState.testType + '...'; 49 50 // FIXME: Gross hack to allow loading all the builders for different test types. 51 // Change loader.js to allow you to pass in the state that it fills instead of setting globals. 52 g_resultsByBuilder = {}; 53 overview._resultsByTestType[historyInstance.crossDashboardState.testType] = g_resultsByBuilder; 54 new loader.Loader().load(); 55} 56 57overview._getFlakyData = function(allTestTypes, resultsByTestType, flipCountThreshold) 58{ 59 var flakyData = {}; 60 allTestTypes.forEach(function(testType) { 61 flakyData[testType] = { 62 flakyBelowThreshold: {}, 63 flaky: {}, 64 testCount: 0 65 } 66 67 var resultsByBuilder = resultsByTestType[testType]; 68 for (var builder in resultsByBuilder) { 69 var totalTestCount = results.testCounts(resultsByBuilder[builder][results.NUM_FAILURES_BY_TYPE]).totalTests[0]; 70 flakyData[testType].testCount = Math.max(totalTestCount, flakyData[testType].testCount); 71 72 var allTestsForThisBuilder = resultsByBuilder[builder].tests; 73 for (var test in allTestsForThisBuilder) { 74 var resultsForTest = {}; 75 var testData = resultsByBuilder[builder].tests[test].results; 76 var failureMap = resultsByBuilder[builder][results.FAILURE_MAP]; 77 results.determineFlakiness(failureMap, testData, resultsForTest); 78 79 if (resultsForTest.isFlaky) 80 flakyData[testType].flaky[test] = true; 81 82 if (!resultsForTest.isFlaky || resultsForTest.flipCount <= flipCountThreshold) 83 continue; 84 flakyData[testType].flakyBelowThreshold[test] = true; 85 } 86 } 87 }); 88 return flakyData; 89} 90 91overview._generatePage = function() 92{ 93 var flipCountThreshold = Number(g_history.dashboardSpecificState.flipCount); 94 var flakyData = overview._getFlakyData(builders.testTypes, overview._resultsByTestType, flipCountThreshold); 95 $('content').innerHTML = overview._htmlForFlakyTests(flakyData, g_history.crossDashboardState.group) + 96 '<div>*Tests that fail due to a bad patch being committed are counted as flaky.</div>'; 97} 98 99overview._htmlForFlakyTests = function(flakyData, group) 100{ 101 var html = '<table><tr><th>Test type</th><th>flaky count / total count</th><th>percent</th><th></th></tr>'; 102 103 Object.keys(flakyData).forEach(function(testType) { 104 var testCount = flakyData[testType].testCount; 105 if (!testCount) 106 return; 107 108 // We want the list of tests to stay stable as you drag the flakiness slider, so only 109 // exclude tests that never flake, even at the lowest flakiness threshold. 110 var flakeCountIgnoringThreshold = Object.keys(flakyData[testType].flaky).length; 111 if (!g_history.dashboardSpecificState.showNoFlakes && !flakeCountIgnoringThreshold) 112 return; 113 114 var tests = Object.keys(flakyData[testType].flakyBelowThreshold); 115 var flakyCount = tests.length; 116 var percentage = Math.round(100 * flakyCount / testCount); 117 html += '<tr>' + 118 '<td><a href="flakiness_dashboard.html#group=' + group + '&testType=' + testType + '&tests=' + tests.join(',') + '" target=_blank>' + 119 testType + 120 '</a></td>' + 121 '<td>' + flakyCount + ' / ' + testCount + '</td>' + 122 '<td>' + percentage + '%</td>' + 123 '<td><div class="flaky-bar" style="width:' + percentage * 5 + 'px"></div>' 124 '</tr>'; 125 }); 126 127 return html + '</table>'; 128} 129 130overview.handleValidHashParameter = function(historyInstance, key, value) { 131 switch(key) { 132 case 'flipCount': 133 return history.validateParameter(historyInstance.dashboardSpecificState, key, value, 134 function() { 135 return !isNaN(Number(value)); 136 }); 137 138 case 'showNoFlakes': 139 historyInstance.dashboardSpecificState[key] = value == 'true'; 140 return true; 141 142 default: 143 return false; 144 } 145} 146 147overview._htmlForNavBar = function(flipCount, showNoFlakes) 148{ 149 return ui.html.navbar(ui.html.select('Group', 'group', builders.getAllGroupNames())) + 150 '<div id=flip-slider-container>' + 151 ui.html.range('flipCount', 'Flakiness threshold (low-->high):', 1, 50, flipCount) + 152 ui.html.checkbox('showNoFlakes', 'Show test suites with no flakes', showNoFlakes) + 153 '</div>'; 154} 155 156// FIXME: Once dashboard_base, loader and ui stop using the g_history global, we can stop setting it here. 157g_history = new history.History({ 158 defaultStateValues: { 159 flipCount: 1, 160 showNoFlakes: false 161 }, 162 generatePage: overview.loadNextTestType, 163 handleValidHashParameter: overview.handleValidHashParameter, 164}); 165g_history.parseCrossDashboardParameters(); 166 167window.addEventListener('load', function() { 168 // FIXME: Come up with a better way to do this. This early return is just to avoid 169 // executing this code when it's loaded in the unittests. 170 if (!$('navbar')) 171 return; 172 173 // Need to parseParameters so that flipCount has the correct value. 174 g_history.parseParameters(); 175 $('navbar').innerHTML = overview._htmlForNavBar(g_history.dashboardSpecificState.flipCount); 176 overview.loadNextTestType(g_history); 177}, false); 178 179})(); 180