• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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