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 29 var overview = overview || {}; 30 31 (function() { 32 33 overview._resultsByTestType = {}; 34 overview._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. 37 history.reloadRequiringParameters = history.reloadRequiringParameters.filter(function(item) { return item != 'testType'; }); 38 39 overview.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 57 overview._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 91 overview._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 99 overview._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 130 overview.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 147 overview._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. 157 g_history = new history.History({ 158 defaultStateValues: { 159 flipCount: 1, 160 showNoFlakes: false 161 }, 162 generatePage: overview.loadNextTestType, 163 handleValidHashParameter: overview.handleValidHashParameter, 164 }); 165 g_history.parseCrossDashboardParameters(); 166 167 window.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