[TestResultServer] Move the resource loading into a dedicated class
[WebKit-https.git] / Tools / TestResultServer / static-dashboards / loader.js
1 // Copyright (C) 2012 Google Inc. All rights reserved.
2 // Copyright (C) 2012 Zan Dobersek <zandobersek@gmail.com>
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //         * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //         * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //         * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 var loader = loader || {};
31
32 (function() {
33
34 var TEST_RESULTS_SERVER = 'http://test-results.appspot.com/';
35 var CHROMIUM_EXPECTATIONS_URL = 'http://svn.webkit.org/repository/webkit/trunk/LayoutTests/platform/chromium/TestExpectations';
36
37 function pathToBuilderResultsFile(builderName) {
38     return TEST_RESULTS_SERVER + 'testfile?builder=' + builderName +
39            '&master=' + builderMaster(builderName).name +
40            '&testtype=' + g_crossDashboardState.testType + '&name=';
41 }
42
43 loader.request = function(url, success, error, opt_isBinaryData)
44 {
45     var xhr = new XMLHttpRequest();
46     xhr.open('GET', url, true);
47     if (opt_isBinaryData)
48         xhr.overrideMimeType('text/plain; charset=x-user-defined');
49     xhr.onreadystatechange = function(e) {
50         if (xhr.readyState == 4) {
51             if (xhr.status == 200)
52                 success(xhr);
53             else
54                 error(xhr);
55         }
56     }
57     xhr.send();
58 }
59
60 loader.Loader = function()
61 {
62     this._loadingSteps = [
63         this._loadBuildersList,
64         this._loadResultsFiles,
65         this._loadExpectationsFiles,
66     ];
67 }
68
69 loader.Loader.prototype = {
70     load: function()
71     {
72         this._loadNext();
73     },
74     buildersListLoaded: function()
75     {
76         initBuilders();
77         this._loadNext();
78     },
79     _loadNext: function()
80     {
81         var loadingStep = this._loadingSteps.shift();
82         if (!loadingStep) {
83             resourceLoadingComplete();
84             return;
85         }
86         loadingStep.apply(this);
87     },
88     _loadBuildersList: function()
89     {
90         loadBuildersList(g_crossDashboardState.group, g_crossDashboardState.testType);
91     },
92     _loadResultsFiles: function()
93     {
94         parseParameters();
95
96         for (var builderName in g_builders)
97             this._loadResultsFileForBuilder(builderName);
98     },
99     _loadResultsFileForBuilder: function(builderName)
100     {
101         var resultsFilename;
102         if (isTreeMap())
103             resultsFilename = 'times_ms.json';
104         else if (g_crossDashboardState.showAllRuns)
105             resultsFilename = 'results.json';
106         else
107             resultsFilename = 'results-small.json';
108
109         var resultsFileLocation = pathToBuilderResultsFile(builderName) + resultsFilename;
110         loader.request(resultsFileLocation,
111                 partial(function(loader, builderName, xhr) {
112                     loader._handleResultsFileLoaded(builderName, xhr.responseText);
113                 }, this, builderName),
114                 partial(function(loader, builderName, xhr) {
115                     loader._handleResultsFileLoadError(builderName);
116                 }, this, builderName));
117     },
118     _handleResultsFileLoaded: function(builderName, fileData)
119     {
120         if (isTreeMap())
121             this._processTimesJSONData(builderName, fileData);
122         else
123             this._processResultsJSONData(builderName, fileData);
124
125         // We need this work-around for webkit.org/b/50589.
126         if (!g_resultsByBuilder[builderName]) {
127             this._handleResultsFileLoadError(builderName);
128             return;
129         }
130
131         this._handleResourceLoad();
132     },
133     _processTimesJSONData: function(builderName, fileData)
134     {
135         // FIXME: We should probably include the builderName in the JSON
136         // rather than relying on only loading one JSON file per page.
137         g_resultsByBuilder[builderName] = JSON.parse(fileData);
138     },
139     _processResultsJSONData: function(builderName, fileData)
140     {
141         var builds = JSON.parse(fileData);
142
143         var json_version = builds['version'];
144         for (var builderName in builds) {
145             if (builderName == 'version')
146                 continue;
147
148             // If a test suite stops being run on a given builder, we don't want to show it.
149             // Assume any builder without a run in two weeks for a given test suite isn't
150             // running that suite anymore.
151             // FIXME: Grab which bots run which tests directly from the buildbot JSON instead.
152             var lastRunSeconds = builds[builderName].secondsSinceEpoch[0];
153             if ((Date.now() / 1000) - lastRunSeconds > ONE_WEEK_SECONDS)
154                 continue;
155
156             if ((Date.now() / 1000) - lastRunSeconds > ONE_DAY_SECONDS)
157                 g_staleBuilders.push(builderName);
158
159             if (json_version >= 4)
160                 builds[builderName][TESTS_KEY] = flattenTrie(builds[builderName][TESTS_KEY]);
161             g_resultsByBuilder[builderName] = builds[builderName];
162         }
163     },
164     _handleResultsFileLoadError: function(builderName)
165     {
166         var error = 'Failed to load results file for ' + builderName + '.';
167
168         if (isLayoutTestResults()) {
169             console.error(error);
170             g_buildersThatFailedToLoad.push(builderName);
171         } else {
172             // Avoid to show error/warning messages for non-layout tests. We may be
173             // checking the builders that are not running the tests.
174             console.info('info:' + error);
175         }
176
177         // Remove this builder from builders, so we don't try to use the
178         // data that isn't there.
179         delete g_builders[builderName];
180
181         // Change the default builder name if it has been deleted.
182         if (g_defaultBuilderName == builderName) {
183             g_defaultBuilderName = null;
184             for (var availableBuilderName in g_builders) {
185                 g_defaultBuilderName = availableBuilderName;
186                 g_defaultDashboardSpecificStateValues.builder = availableBuilderName;
187                 break;
188             }
189             if (!g_defaultBuilderName) {
190                 var error = 'No tests results found for ' + g_crossDashboardState.testType + '. Reload the page to try fetching it again.';
191                 console.error(error);
192                 addError(error);
193             }
194         }
195
196         // Proceed as if the resource had loaded.
197         this._handleResourceLoad();
198     },
199     _handleResourceLoad: function()
200     {
201         if (this._haveResultsFilesLoaded())
202             this._loadNext();
203     },
204     _haveResultsFilesLoaded: function()
205     {
206         for (var builder in g_builders) {
207             if (!g_resultsByBuilder[builder])
208                 return false;
209         }
210         return true;
211     },
212     _loadExpectationsFiles: function()
213     {
214         if (!isFlakinessDashboard() && !g_crossDashboardState.useTestData) {
215             this._loadNext();
216             return;
217         }
218
219         var expectationsFilesToRequest = {};
220         traversePlatformsTree(function(platform, platformName) {
221             if (platform.fallbackPlatforms)
222                 platform.fallbackPlatforms.forEach(function(fallbackPlatform) {
223                     var fallbackPlatformObject = platformObjectForName(fallbackPlatform);
224                     if (fallbackPlatformObject.expectationsDirectory && !(fallbackPlatform in expectationsFilesToRequest))
225                         expectationsFilesToRequest[fallbackPlatform] = EXPECTATIONS_URL_BASE_PATH + fallbackPlatformObject.expectationsDirectory + '/TestExpectations';
226                 });
227
228             if (platform.expectationsDirectory)
229                 expectationsFilesToRequest[platformName] = EXPECTATIONS_URL_BASE_PATH + platform.expectationsDirectory + '/TestExpectations';
230         });
231
232         for (platformWithExpectations in expectationsFilesToRequest)
233             loader.request(expectationsFilesToRequest[platformWithExpectations],
234                     partial(function(loader, platformName, xhr) {
235                         g_expectationsByPlatform[platformName] = getParsedExpectations(xhr.responseText);
236
237                         delete expectationsFilesToRequest[platformName];
238                         if (!Object.keys(expectationsFilesToRequest).length)
239                             loader._loadNext();
240                     }, this, platformWithExpectations),
241                     partial(function(platformName, xhr) {
242                         console.error('Could not load expectations file for ' + platformName);
243                     }, platformWithExpectations));
244     }
245 }
246
247 })();