Do not announce robot account token before account ID is available
[chromium-blink-merge.git] / chrome / test / data / dromaeo / webrunner.js
blob8b0e346003255fbb495f305cc9a4fe9fde0bd7d5
1 (function(){
2         
3         // Populated from: http://www.medcalc.be/manual/t-distribution.php
4         // 95% confidence for N - 1 = 4
5         var tDistribution = 2.776;
6         
7         // The number of individual test iterations to do
8         var numTests = 5;
9         
10         // The type of run that we're doing (options are "runs/s" or "ms")
11         var runStyle = "runs/s";
12         
13         // A rough estimate, in seconds, of how long it'll take each test
14         // iteration to run
15         var timePerTest = runStyle === "runs/s" ? 1 : 0.5;
16         
17         // Initialize a batch of tests
18         //  name = The name of the test collection
19         this.startTest = function(name, version){
20                 numloaded++;
21                 if ( numloaded == totalTests )
22                         setTimeout( init, 100 );
24                 testName = name;
25                 if ( !queues[testName] ) return;
26                 testID = testName;
27                 testNames[testID] = testName;
28                 testVersions[testID] = version || 0;
29                 testSummary[testID] = testSummaryNum[testID] = testDone[testID] = testNum[testID] = 0;
31                 queues[testID].push(function(){
32                         summary = 0;
33                         dequeue();
34                 });
35         };
37         // Anything that you want to have run in order, but not actually test
38         this.prep = function(fn){
39                 if ( !queues[testName] ) return;
40                 queues[testID].push(function(){
41                         fn();
42                         dequeue();
43                 });
44         };
46         // End the tests and finalize the report
47         this.endTest = function(){
48                 if ( !queues[testName] ) return;
49                 // Save the summary output until all the test are complete
50                 queues[testID].push(function(){
51                         dequeue();
52                 });
53         };
55         // Run a new test
56         //  name = The unique name of the test
57         //  num = The 'length' of the test (length of string, # of tests, etc.)
58         //  fn = A function holding the test to run
59         this.test = function(name, num, fn){
60                 if ( !queues[testName] ) return;
61                 // Save the summary output until all the test are complete
62                 var curTest = testName, curID = testID;
64                 if ( arguments.length === 3 ) {
65                         if ( !nameDone[name] )
66                                 nameDone[name] = 0;
67                         nameDone[name]++;
68         
69                         if ( nameDone[name] != 3 )
70                                 return; 
71                 } else {
72                         fn = num;
73                         num = 1;
74                 }
76                 time += timePerTest * numTests;
78                 testNum[curID]++;
80                 // Don't execute the test immediately
81                 queues[testID].push(function(){
82                         title = name;
83                         var times = [], start, pos = 0, cur;
84                         
85                         setTimeout(function(){
86                                 // run tests
87                                 try {
88                                         if ( doShark(name) ) {
89                                                 connectShark();
90                                                 startShark();
91                                         }
92                                         
93                                         start = (new Date()).getTime();
94                                         
95                                         if ( runStyle === "runs/s" ) {
96                                                 var runs = 0;
97                                                 
98                                                 cur = (new Date()).getTime();
99                                                 
100                                                 while ( (cur - start) < 1000 ) {
101                                                         fn();
102                                                         cur = (new Date()).getTime();
103                                                         runs++;
104                                                 }
105                                         } else {
106                                                 fn();
107                                                 cur = (new Date()).getTime();
108                                         }
110                                         if ( doShark(name) ) {
111                                                 stopShark();
112                                                 disconnectShark();
113                                         }
114                                         
115                                         // For making Median and Variance
116                                         if ( runStyle === "runs/s" ) {
117                                                 times.push( (runs * 1000) / (cur - start) );
118                                         } else {
119                                                 times.push( cur - start );
120                                         }
121                                 } catch( e ) {
122                                         alert("FAIL " + name + " " + num + e);
123                                         return;
124                                 }
126                                 if ( pos < numTests ) {
127                                         updateTime();
128                                         updateTestPos({curID: curID, collection: testNames[curID], version: testVersions[curID]});
129                                 }
131                                 if ( ++pos < numTests ) {
132                                         setTimeout( arguments.callee, 1 );
133                                 
134                                 } else {
135                                         var data = compute( times, numTests );
137                                         data.curID = curID;
138                                         data.collection = testNames[curID];
139                                         data.version = testVersions[curID];
140                                         data.name = title;
141                                         data.scale = num;
142                                                                 
143                                         logTest(data);
144                         
145                                         dequeue();
146                                 }
147                         }, 1);
148                 });
150                 function compute(times, runs){
151                         var results = {runs: runs}, num = times.length;
153                         times = times.sort(function(a,b){
154                                 return a - b;
155                         });
157                         // Make Sum
158                         results.sum = 0;
160                         for ( var i = 0; i < num; i++ )
161                                 results.sum += times[i];
163                         // Make Min
164                         results.min = times[0];
165                                           
166                         // Make Max
167                         results.max = times[ num - 1 ];
169                         // Make Mean
170                         results.mean = results.sum / num;
171                         
172                         // Make Median
173                         results.median = num % 2 == 0 ?
174                                 (times[Math.floor(num/2)] + times[Math.ceil(num/2)]) / 2 :
175                                 times[Math.round(num/2)];
176                         
177                         // Make Variance
178                         results.variance = 0;
180                         for ( var i = 0; i < num; i++ )
181                                 results.variance += Math.pow(times[i] - results.mean, 2);
183                         results.variance /= num - 1;
184                                         
185                         // Make Standard Deviation
186                         results.deviation = Math.sqrt( results.variance );
188                         // Compute Standard Errors Mean
189                         results.sem = (results.deviation / Math.sqrt(results.runs)) * tDistribution;
191                         // Error
192                         results.error = ((results.sem / results.mean) * 100) || 0;
194                         return results;
195                 }
196         };
197         
198         // All the test data
199         var tests;
200         
201         // The number of test files to load
202         var totalTests = 0;
203         
204         // The number of test files loaded
205         var numloaded = 0;
206         
207         // Queue of functions to run
208         var queue = [];
209         var queues = {};
211         var catnames = {
212                 dromaeo: "Dromaeo JavaScript Tests",
213                 sunspider: "SunSpider JavaScript Tests",
214                 "v8": "V8 JavaScript Tests",
215                 dom: "DOM Core Tests",
216                 jslib: "JavaScript Library Tests",
217                 cssquery: "CSS Selector Tests"
218         };
220         
221         var testElems = {};
222         var testNum = {};
223         var testDone = {};
224         var testNames = {};
225         var testVersions = {};
226         var dataStore = [];
227         var names = [];
228         var interval;
229         var totalTime = 0;
230         var time = 0;
231         var title, testName, testID, testSummary = {} , testSummaryNum = {}, maxTotal = 0, maxTotalNum = 0;
232         var nameDone = {};
233         var automated = false;
234         var post_json = false;
235         
236         // Query String Parsing
237         var search = window.limitSearch || (window.location.search || "?").substr(1);
239         search = search.replace(/&runStyle=([^&]+)/, function(all, type){
240                 runStyle = type;
241                 return "";
242         });
244         var parts = search.split("&");
246         if ( parts[0] === "recommended" ) {
247                 parts[0] = "dromaeo|sunspider|v8|dom|jslib";
248         }
250         var none = !parts[0] || parts[0].match(/=/);
251         var filter = parts.length && !parts[0].match(/=/) && parts[0] !== "all" ?
252                 new RegExp(parts.shift(), "i") :
253                 /./;
255         // To enable shark debugging add &shark to the end of the URL
256         var doShark = function(name) { return false; };
257         for ( var i = 0; i < parts.length; i++ ) {
258                 var m = /^shark(?:=(.*))?$/.exec(parts[i]);
259                 if (m) {
260                         if (m[1] === undefined) {
261                                 doShark = function(name) { return true; };
262                         }
263                         else {
264                                 var sharkMatch = new RegExp(m[1]);
265                                 doShark = function(name) {
266                                         return sharkMatch.test(name);
267                                 };
268                         }
269                 }
271                 m = /^numTests=(\d+)$/.exec(parts[i]);
272                 if (m)
273                         numTests = Number(m[1]);
275                 if (/^automated$/.exec(parts[i]))
276                         automated = true;
277                 if (/^post_json$/.exec(parts[i]))
278                         post_json = true;
279         }
281         jQuery(function(){
282                 var id = search.match(/id=([\d,]+)/);
284                 if ( none && !id ) {
285                         $("#overview").hide();
286                         return;
287                 } 
289                 var cat = filter.toString().slice(1,-2);
291                 if ( catnames[cat] ) {
292                         $("#overview span:first").html( catnames[cat] );
294                         if ( catnames[cat].length > 22 ) {
295                                 $("#overview span:first").css("font-size", 22);
296                         }
297                 }
299                 $("#tests").hide();
301                 jQuery.getJSON("tests/MANIFEST.json", function(json){
302                         tests = json;
304                         names = [];
306                         for ( var name in tests )
307                                 // Don't load tests that we aren't looking for
308                                 if ( filter.test( name ) )
309                                         names.push( name );
311                         names = names.sort(function(a, b){
312                                 return tests[a].name < tests[b].name ?  -1 :
313                                         tests[a].name == tests[b].name ?  0 : 1;
314                         });
316                         // Check if we're loading a specific result set
317                         // ?id=NUM
318                         if ( id ) {
319                                 jQuery.ajax({
320                                         url: "store.php?id=" + id[1],
321                                         dataType: "json",
322                                         success: function(data){
323                                                 resultsLoaded(id[1], data);
324                                         }
325                                 });
327                         // Otherwise we're loading a normal set of tests
328                         } else {
329                                 $("#wrapper").append("<br style='clear:both;'/><center><a href='?" + names.join("|") + "'>Re-run tests</a></center>");
331                                 for ( var i = 0; i < names.length; i++ ) (function(name){
332                                         var test = tests[name];
334                                         queues[name] = [];
335                                         makeElem(name);
336                                         initTest(name);
337                                         
338                                         totalTests++;
339                                         
340                                         // Check if we're loading an HTML file
341                                         if ( test.file.match(/html$/) ) {
342                                                 var iframe = document.createElement("iframe");
343                                                 iframe.style.height = "1px";
344                                                 iframe.style.width = "1px";
345                                                 iframe.src = "tests/" + test.file;
346                                                 document.body.appendChild( iframe );
347                                         
348                                         // Otherwise we're loading a pure-JS test
349                                         } else {
350                                                 jQuery.getScript("tests/" + test.file);
351                                         }
352                                 })(names[i]);
353                         }
354                 });
355         });
357         // Remove the next test from the queue and execute it
358         function dequeue(){
359                 if ( interval && queue.length ) {
360                         queue.shift()();
361                         
362                 } else if ( queue.length == 0 ) {
363                         interval = false;
364                         time = 0;
365                         
366                         $("#overview input").remove();
367                         updateTimebar();
369                         if ( window.limitSearch ) {
370                                 var summary = (runStyle === "runs/s" ? Math.pow(Math.E, maxTotal / maxTotalNum) : maxTotal).toFixed(2);
372                                 if ( typeof tpRecordTime !== "undefined" ) {
373                                         tpRecordTime( summary );
375                                 } else {
376                                         var pre = document.createElement("pre");
377                                         pre.style.display = "none";
378                                         pre.innerHTML = "__start_report" + summary + "__end_report";
379                                         document.body.appendChild( pre );
380                                 }
382                                 if ( typeof goQuitApplication !== "undefined" ) {
383                                         goQuitApplication();
384                                 }
385         
386                         } else if ( dataStore && dataStore.length ) {
387                                 if (!automated) {
388                                         $("body").addClass("alldone");
389                                         var div = jQuery("<div class='results'>Saving...</div>").insertBefore("#overview");
390                                         jQuery.ajax({
391                                                 type: "POST",
392                                                 url: "store.php",
393                                                 data: "data=" + encodeURIComponent(JSON.stringify(dataStore)) + "&style=" + runStyle,
394                                                 success: function(id){
395                                                         var url = window.location.href.replace(/\?.*$/, "") + "?id=" + id;
396                                                         div.html("Results saved. You can access them at a later time at the following URL:<br/><strong><a href='" + url + "'>" + url + "</a></strong></div>");
397                                                 }
398                                         });
399                                 } else if (post_json) {
400                                         jQuery.ajax({
401                                                 type: "POST",
402                                                 url: "store.php",
403                                                 data: "data=" + encodeURIComponent(JSON.stringify(window.automation.GetResults()))
404                                         });
405                                 }
406                                 else {
407                                         window.automation.SetDone();
408                                 }
409                         }
410                 }
411         }
412         
413         function updateTimebar(){
414                 $("#timebar").html("<span><strong>" + (runStyle === "runs/s" ? Math.pow(Math.E, maxTotal / maxTotalNum) : maxTotal).toFixed(2) + "</strong>" + runStyle + " (Total)</span>");
415         }
416         
417         // Run once all the test files are fully loaded
418         function init(){
419                 for ( var n = 0; n < names.length; n++ ) {
420                         queue = queue.concat( queues[ names[n] ] );
421                 }
423                 totalTime = time;
424                 time += timePerTest;
425                 updateTime();
426                 
427                 if (!automated) {
428                         $("#pause")
429                                 .val("Run")
430                                 .click(function(){
431                                         if ( interval ) {
432                                                 interval = null;
433                                                 this.value = "Run";
434                                         } else {
435                                                 if ( !interval ) {
436                                                         interval = true;
437                                                         dequeue();
438                                                 }
439                                                 this.value = "Pause";
440                                         }
441                                 });
442                 } else {
443                         $("#pause")
444                                 .val("Automated")
445                                 .click(function(){});
446                         interval = true;
447                         dequeue();
448                 }
450                 if ( window.limitSearch ) {
451                         $("#pause").click();
452                 }
453         }
455         function initTest(curID){
456                 $("<div class='result-item'></div>")
457                         .append( testElems[ curID ] )
458                         .append( "<p>" + (tests[curID] ? tests[ curID ].desc : "") + "<br/><a href='" + 
459                                 (tests[curID] && tests[curID].origin ? tests[ curID ].origin[1] : "") + "'>Origin</a>, <a href='tests/" +
460                                 (tests[curID] ? tests[ curID ].file : "") + "'>Source</a>, <b>Tests:</b> " +
461                                 (tests[curID] && tests[curID].tags ? tests[ curID ].tags.join(", ") : "") + "</p>" )
462                         .append( "<ol class='results'></ol>" )
463                         .appendTo("#main");
464         }
465         
466         function resultsLoaded(id, datas){
467                 var results = {};
468                 var runs = {};
469                 var output = "";
470                 var excluded = [];
471                 var overview = document.getElementById("overview");
473                 for ( var d = 0; d < datas.length; d++ ) {
474                         var data = datas[d];
475                         
476                         runStyle = data.style;
478                         if ( datas.length == 1 ) {
479                                 $("#overview").before("<div class='results'>Viewing test run #" + id +
480                                         ", run on: " + data.created_at + " by:<br>" + data.useragent + "</div>");
481                         }
483                         runs[data.id] = data;
484                         runs[data.id].mean = 0;
485                         runs[data.id].error = 0;
486                         runs[data.id].num = 0;
487                         runs[data.id].name = (data.useragent.match(/(MSIE [\d.]+)/) ||
488                                 data.useragent.match(/((?:WebKit|Firefox|Shiretoko|Opera)\/[\w.]+)/) || [0,data.id])[1];
490                         for ( var i = 0; i < data.results.length; i++ ) {
491                                 var result = data.results[i];
492                                 var curID = result.collection;
493                                 var run = result.run_id;
494                                 
495                                 result.version += data.style;
497                                 if ( !results[curID] )
498                                         results[curID] = {tests:{}, total:{}, version: result.version};
500                                 if ( results[curID].version == result.version ) {
501                                         if ( !results[curID].total[run] ) {
502                                                 results[curID].total[run] = {max:0, mean:0, median:0, min:0, deviation:0, error:0, num:0};
503                                                 results[curID].tests[run] = [];
504                                         }
505                                         
506                                         result.error = ((((result.deviation / Math.sqrt(result.runs)) * tDistribution) / result.mean) * 100) || 0;
507                                         results[curID].tests[run].push( result );
509                                         var error = (parseFloat(result.error) / 100) * parseFloat(result.mean);
510                                         error = (runStyle === "ms" ? error : error == 0 ? 0 : Math.log(error));
511                                         var total = results[curID].total[run];
512                                         total.num++;
514                                         for ( var type in total ) {
515                                                 if ( type == "error" ) {
516                                                         total.error += error;
517                                                 } else if ( type == "mean" ) {
518                                                         total.mean += (runStyle === "ms" ? parseFloat(result.mean) : Math.log(parseFloat(result.mean)));
519                                                 } else if ( type !== "num" ) {
520                                                         total[type] += parseFloat(result[type]);
521                                                 }
522                                         }
523                                         
524                                         runs[run].num++;
525                                         runs[run].mean += runStyle === "ms" ? parseFloat(result.mean) : Math.log(parseFloat(result.mean));
526                                         runs[run].error += error;
527                                 }
528                         }
529                 }
531                 var runTests = [];
533                 if ( datas.length == 1 ) {
534                         $("body").addClass("alldone");
536                         for ( var i = 0; i < data.results.length; i++ ) {
537                                 var item = data.results[i];
538                                 var result = item.curID = item.collection;
540                                 if ( !filter.test(result) )
541                                         continue;
543                                 if ( !testElems[result] ) {
544                                         runTests.push(result);
545                                         makeElem( result );
546                                         initTest( result );
547                                 }
549                                 // Compute Standard Errors Mean
550                                 item.sem = (item.deviation / Math.sqrt(item.runs)) * tDistribution;
552                                 // Error
553                                 item.error = ((item.sem / item.mean) * 100) || 0;
555                                 logTest( item );
557                                 // testDone, testNum, testSummary
558                                 testDone[ result ] = numTests - 1;
559                                 testNum[ result ] = 1;
561                                 updateTestPos( item );
562                         }
564                         $("div.result-item").addClass("done");
566                         totalTime = time = timePerTest;
567                         updateTime();
569                         $("#overview input").remove();
570                         updateTimebar();
571                 } else {
572                         // Remove results where there is only one comparison set
573                         for ( var id in results ) {
574                                 var num = 0;
575                                 
576                                 for ( var ntest in results[id].tests ) {
577                                         num++;
578                                         if ( num > 1 )
579                                                 break;
580                                 }
581                                 
582                                 if ( num <= 1 ) {
583                                         excluded.push( id );
584                                         delete results[id];
585                                 }
586                         }
587                 
588                         var preoutput = "<tr><td></td>";
589                         for ( var run in runs )
590                                 preoutput += "<th><a href='?id=" + run + "'>" + runs[run].name + "</a></th>";
591                         //preoutput += "<th>Winning %</th></tr>";
592                         preoutput += "</tr>";
594                         for ( var result in results ) {
595                                 // Skip results that we're filtering out
596                                 if ( !filter.test(result) )
597                                         continue;
599                                 runTests.push(result);
601                                 if ( runStyle === "runs/s" ) {
602                                         for ( var run in runs ) {
603                                                 var mean = results[result].total[run].mean - 0;
604                                                 var error = results[result].total[run].error - 0;
606                                                 mean = Math.pow(Math.E, mean / results[result].total[run].num);
607                                                 error = Math.pow(Math.E, error / results[result].total[run].num);
608                                                 results[result].total[run].mean = mean;
609                                                 results[result].total[run].error = error;
610                                         }
611                                 }
613                                 var name = tests[result] ? tests[result].name : result;
614                                 var tmp = processWinner(results[result].total);
616                                 output += "<tr><th class='name'><span onclick='toggleResults(this.nextSibling);'>&#9654; </span>" +
617                                         "<a href='' onclick='return toggleResults(this);'>" + name + "</a></th>";
619                                 for ( var run in runs ) {
620                                         var mean = results[result].total[run].mean - 0;
621                                         var error = results[result].total[run].error - 0;
622                 
623                                         output += "<td class='" + (tmp[run] || '') + "'>" + mean.toFixed(2) + "<small>" + runStyle + " &#177;" + ((error / mean) * 100).toFixed(2) + "%</small></td>";
624                                 }
625                                 
626                                 //showWinner(tmp);
627                                 output += "</tr>";
629                                 var _tests = results[result].tests, _data = _tests[run], _num = _data.length;
630                                 for ( var i = 0; i < _num; i++ ) {
631                                         output += "<tr class='onetest hidden'><td><small>" + _data[i].name + "</small></td>";
632                                         for ( var run in runs ) {
633                                                 output += "<td>" + (_tests[run][i].mean - 0).toFixed(2) + "<small>" + runStyle + " &#177;" + (_tests[run][i].error - 0).toFixed(2) + "%</small></td>";
634                                         }
635                                         output += "<td></td></tr>";
636                                 }
637                         }
639                         if ( runStyle === "runs/s" ) {
640                                 for ( var run in runs ) {
641                                         runs[run].mean = Math.pow(Math.E, runs[run].mean / runs[run].num);
642                                         runs[run].error = Math.pow(Math.E, runs[run].error / runs[run].num);
643                                 }
644                         }
645         
646                         var tmp = processWinner(runs);
647                         var totaloutput = "";
649                         if ( runStyle === "ms" ) {
650                                 totaloutput += "<tr><th class='name'>Total:</th>";
651                         } else {
652                                 totaloutput += "<tr><th class='name'>Total Score:</th>";
653                         }
655                         for ( var run in runs ) {
656                                 totaloutput += "<th class='name " + (tmp[run] || '') + "' title='" + (tmp[run + "title"] || '') + "'>" + runs[run].mean.toFixed(2) + "<small>" + runStyle + " &#177;" + ((runs[run].error / runs[run].mean) * 100).toFixed(2) + "%</small></th>";
657                         }
659                         //showWinner(tmp);
660                         totaloutput += "</tr>";
662                         overview.className = "";
663                         overview.innerHTML = "<div class='resultwrap'><table class='results'>" + preoutput + totaloutput + output + totaloutput + "</table>" + (excluded.length ? "<div style='text-align:left;'><small><b>Excluded Tests:</b> " + excluded.sort().join(", ") + "</small></div>" : "") + "</div>";
664                 }
666                 $("#wrapper").append("<center><a href='?" + runTests.join("|") + "'>Re-run tests</a></center>");
667                 
668                 function showWinner(tmp){
669                         if ( datas.length > 1 ) {
670                                 if ( tmp.tie )
671                                         output += "<th>Tie</th>";
672                                 else
673                                         output += "<th>" + tmp.diff + "%</th>";
674                         }
675                 }
676         }
678         this.toggleResults = function(elem){
679                 var span = elem.previousSibling;
681                 elem.blur();
682                 elem = elem.parentNode.parentNode.nextSibling;
684                 span.innerHTML = elem.className.indexOf("hidden") < 0 ? "&#9654; " : "&#9660; ";
686                 while ( elem && elem.className.indexOf("onetest") >= 0 ) {
687                         elem.className = "onetest" + (elem.className.indexOf("hidden") >= 0 ? " " : " hidden");
688                         elem = elem.nextSibling;
689                 }
691                 return false;
692         };
694         function updateTime(){
695                 time -= timePerTest;
696                 $("#left").html(Math.floor(time / 60) + ":" + (time % 60 < 10 ? "0" : "" ) + Math.floor(time % 60));
698                 var w = ((totalTime - time) / totalTime) * 100;
700                 $("#timebar").width((w < 1 ? 1 : w) + "%");
701         }
702         
703         function logTest(data){
704                 // Keep a running summary going
705                 data.mean = parseFloat(data.mean);
706                 var mean = (runStyle === "runs/s" ? Math.log(data.mean) : data.mean);
707                 testSummary[data.curID] = (testSummary[data.curID] || 0) + mean;
708                 testSummaryNum[data.curID] = (testSummaryNum[data.curID] || 0) + 1;
709                 
710                 maxTotal += mean;
711                 maxTotalNum++;
713                 testDone[data.curID]--;
714                 updateTestPos(data);
716                 testElems[data.curID].next().next().append("<li><b>" + data.name + 
717                         ":</b> " + data.mean.toFixed(2) + "<small>" + runStyle + " &#177;" + data.error.toFixed(2) + "%</small></li>");
719                 dataStore.push(data);
720         }
722         function updateTestPos(data, update){
723                 if ( !update )
724                         testDone[data.curID]++;
726                 var per = (testDone[data.curID] / (testNum[data.curID] * numTests)) * 100;
728                 if ( update )
729                         per = 1;
730                 
731                 var mean = (runStyle === "runs/s" ?
732                         Math.pow(Math.E, testSummary[data.curID] / testSummaryNum[data.curID]) :
733                         testSummary[data.curID]);
735                 testElems[data.curID].html("<b>" + (tests[data.curID] ? tests[data.curID].name : data.curID) +
736                         ":</b> <div class='bar'><div style='width:" +
737                         per + "%;'>" + (per >= 100 ? "<span>" + mean.toFixed(2) + runStyle + "</span>" : "") + "</div></div>");
739                 if ( per >= 100 && testSummary[data.curID] > 0 ) {
740                         testElems[data.curID].parent().addClass("done");
741                 }
742         }
743         
744         function processWinner(data){
745                 var minVal = -1, min2Val = -1, min, min2;
747                 for ( var i in data ) {
748                         var total = data[i].mean;
749                         if ( minVal == -1 || (runStyle === "ms" && total <= minVal || runStyle === "runs/s" && total >= minVal) ) {
750                                 min2Val = minVal;
751                                 min2 = min;
752                                 minVal = total;
753                                 min = i;
754                         } else if ( min2Val == -1 || (runStyle === "ms" && total <= minVal || runStyle === "runs/s" && total >= min2Val) ) {
755                                 min2Val = total;
756                                 min2 = i;
757                         }
758                 }
760                 var tieVal = (runStyle === "ms" ? minVal : min2Val) + data[min].error + data[min2].error;
762                 var ret = {
763                         winner: min,
764                         diff: runStyle === "ms" ?
765                                 -1 * Math.round((1 - (min2Val / minVal)) * 100) :
766                                 Math.round(((minVal / min2Val) - 1) * 100),
767                         tie: minVal == min2Val || (runStyle === "ms" ? tieVal >= min2Val : tieVal >= minVal)
768                 };
770                 ret.tie = ret.tie || ret.diff == 0;
772                 if ( ret.tie ) {
773                         ret[ min ] = 'tie';
774                         ret[ min2 ] = 'tie';
775                         ret[ min + 'title' ] = "Tied with another run.";
776                         ret[ min2 + 'title' ] = "Tied with another run.";
777                 } else {
778                         ret[ min ] = 'winner';
779                         if ( min2Val > -1 ) {
780                                 ret[ min + 'title' ] = "Won by " + ret.diff + "%.";
781                         }
782                 }
784                 return ret;
785         }
786         
787         function makeElem(testID){
789                 if ( tests[testID] ) {
790                         var cat = tests[testID].category, catsm = cat.replace(/[^\w]/g, "-");
791                         if ( !$("#" + catsm).length ) {
792                                 $("#main").append("<h2 id='" + catsm + "' class='test'><a href='?cat=" + cat +"'>" + cat + '</a><div class="bar"><div id="timebar" style="width:25%;"><span class="left">Est.&nbsp;Time:&nbsp;<strong id="left">0:00</strong></span></div></div>');
793                         }
794                 }
796                 
797                 testElems[testID] = $("<div class='test'></div>")
798                         .click(function(){
799                                 var next = jQuery(this).next().next();
800                                 if ( next.children().length == 0 ) return;
801                                 var display = next.css("display");
802                                 next.css("display", display == 'none' ? 'block' : 'none');
803                         });
805                 updateTestPos({curID: testID, collection: tests[testID] ? tests[testID].name : testID, version: testVersions[testID]}, true);
806         }
808         if (automated) {
809                 // Add some more stuff if running in automated mode.
810                 window.automation = {}
811                 window.automation.SetDone = function() {
812                         console.log("Total: " + this.GetScore());
813                         window.document.cookie = "__done=1; path=/";
814                 }
815                 window.automation.GetScore = function() {
816                         return (runStyle === "runs/s" ? Math.pow(Math.E, maxTotal / maxTotalNum) : maxTotal).toString();
817                 }
818                 window.automation.GetResults = function() {
819                         var results = {};
820                         var aggregated = {};
821                         function normalizeName(name) {
822                                 // At least for ui_tests, dots are not allowed.
823                                 return name.replace(".", "_");
824                         }
825                         function appendToAggregated(name, value) {
826                                 name = normalizeName(name);
827                                 (aggregated[name] || (aggregated[name] = [])).push(Math.log(value));
828                         }
830                         for (var i = 0; i < dataStore.length; i++) {
831                                 var data = dataStore[i];
832                                 var topName = data.collection.split("-", 1)[0];
833                                 appendToAggregated(topName, data.mean);
834                                 appendToAggregated(data.collection, data.mean);
835                                 results[normalizeName(data.collection + "/" + data.name)] = data.mean.toString();
836                         }
838                         for (var name in aggregated) {
839                                 var means = aggregated[name];
840                                 var sum = 0;
841                                 for (var i = 0; i < means.length; i++) sum += means[i];
842                                 results[name] = Math.pow(Math.E, sum/means.length).toString();
843                         }
845                         return results;
846                 }
847         }
848 })();