Add an option to output the results of the graphics benchmark in JSON format
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 26 Oct 2015 16:44:54 +0000 (16:44 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 26 Oct 2015 16:44:54 +0000 (16:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=150484
<rdar://problem/23243721>

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2015-10-26
Reviewed by Darin Adler.

* Animometer/resources/extensions.js:
(ResultsDashboard): A new class to hold the iterations results.
(ResultsDashboard.prototype.push): Appends an iteration results;
(ResultsDashboard.prototype.toJSON): Converts the iterations results to JSON format.

(RecordTable.prototype.clear): Clears the results table.
(RecordTable.prototype._showTitles): Shows the header titles and appends the sub-titles to a queue.
(RecordTable.prototype._showHeader): Shows the table header titles.
(RecordTable.prototype._showEmpty): Shows an empty table cell.
(RecordTable.prototype._showValue): Shows a number value in the results table.
(RecordTable.prototype._showSamples): Shows a button for the sampled data graph.
(RecordTable.prototype._showTest): Shows the results of a single test.
(RecordTable.prototype._showSuite): Shows the results of a single suite.
(RecordTable.prototype.showRecord): Shows a single iteration for a single test.
(RecordTable.prototype.showIterations): Shows the results of all the suites of the iterations.

(ResultsTable): RecordTable was renamed to ResultsTable.
(ResultsTable.prototype.clear): Clears the table element.
(ResultsTable.prototype._showHeaderRow): Shows a row in the results table header.
(ResultsTable.prototype._showHeader): Shows the results table header.
(ResultsTable.prototype._showEmpty): Shows an empty table cell.
(ResultsTable.prototype._showText): Shows a string in a new table cell.
(ResultsTable.prototype._showFixedNumber): Shows a number in a new table cell.
(ResultsTable.prototype._showGraph): Shows a button for the sampled data graph.
(ResultsTable.prototype._showJSON): Shows a button for the sampled data JSON.
(ResultsTable.prototype._showTest): Shows the results of a single test.
(ResultsTable.prototype._showSuite): Shows the results of a single suite.
(ResultsTable.prototype._showIteration): Shows the results of a single iteration.
(ResultsTable.prototype.showRecord): Shows a single iteration for a single test.
(ResultsTable.prototype.showIterations): Shows all the results.
(RecordTable): Deleted.
(RecordTable.prototype.clear): Deleted.
(RecordTable.prototype._showTitles): Deleted.
(RecordTable.prototype._showHeader): Deleted.
(RecordTable.prototype._showEmpty): Deleted.
(RecordTable.prototype._showValue): Deleted.
(RecordTable.prototype._showSamples): Deleted.
(RecordTable.prototype._showTest): Deleted.
(RecordTable.prototype._showSuite): Deleted.
(RecordTable.prototype.showRecord): Deleted.
(RecordTable.prototype.showIterations): Deleted.

* Animometer/resources/sampler.js:
(Sampler.prototype.startSampling): Use forEach.
(Sampler.prototype.sample): Use forEach.
(Sampler.prototype.toJSON): Converts the sampler data to JSON format.

* Animometer/resources/strings.js: Added.
This new file will be used to associate the strings used by the benchmark
with IDs. A string can be changed in one place without having to change
all the instances of this string in multiple files. There two groups of
strings in this file. The first one is used by the UI elements and the second
group is used when constructing the results JSON.

* Animometer/runner/animometer.html:
* Animometer/runner/resources/animometer.css:

* Animometer/runner/resources/animometer.js:
(window.benchmarkRunnerClient.willAddTestFrame):
(window.benchmarkRunnerClient.willStartFirstIteration):
(window.benchmarkRunnerClient.didRunSuites):
(window.benchmarkRunnerClient.didFinishLastIteration):
Make benchmarkRunnerClient uses ResultsDashboard instead of _iterationsSamplers
Get the JSON from ResultsDashboard.toJSON() and pass it to ResultsTable.showIterations().
Also set the textContent of the "#json" textarea with the results JSON.

(showResults): Delete unneeded code.
(showJson): Shows the "json" section.
(showTestGraph): Rename showGraph() to be showTestGraph().
(showTestJSON): Shows the JSON of a single testResults.
(showGraph): Deleted.

* Animometer/runner/resources/tests.js:
Use the string table instead of putting literal strings.

* Animometer/tests/resources/stage.js:
(StageBenchmark.prototype.showResults):
Fix the parameters which are passed to RecordTable.showRecord().

* Animometer/tests/bouncing-particles/bouncing-canvas-images.html:
* Animometer/tests/bouncing-particles/bouncing-canvas-shapes.html:
* Animometer/tests/bouncing-particles/bouncing-css-images.html:
* Animometer/tests/bouncing-particles/bouncing-css-shapes.html:
* Animometer/tests/bouncing-particles/bouncing-svg-images.html:
* Animometer/tests/bouncing-particles/bouncing-svg-shapes.html:
* Animometer/tests/examples/canvas-electrons.html:
* Animometer/tests/examples/canvas-stars.html:
* Animometer/tests/simple/simple-canvas-paths.html:
* Animometer/tests/template/template-canvas.html:
* Animometer/tests/template/template-css.html:
* Animometer/tests/template/template-svg.html:
* Animometer/tests/text/layering-text.html:
Include the new strings.js file in all the test files.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@191586 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
PerformanceTests/Animometer/resources/extensions.js
PerformanceTests/Animometer/resources/sampler.js
PerformanceTests/Animometer/resources/strings.js [new file with mode: 0644]
PerformanceTests/Animometer/runner/animometer.html
PerformanceTests/Animometer/runner/resources/animometer.css
PerformanceTests/Animometer/runner/resources/animometer.js
PerformanceTests/Animometer/runner/resources/tests.js
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-canvas-images.html
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-canvas-shapes.html
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-css-images.html
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-css-shapes.html
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-svg-images.html
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-svg-shapes.html
PerformanceTests/Animometer/tests/examples/canvas-electrons.html
PerformanceTests/Animometer/tests/examples/canvas-stars.html
PerformanceTests/Animometer/tests/resources/stage.js
PerformanceTests/Animometer/tests/simple/simple-canvas-paths.html
PerformanceTests/Animometer/tests/template/template-canvas.html
PerformanceTests/Animometer/tests/template/template-css.html
PerformanceTests/Animometer/tests/template/template-svg.html
PerformanceTests/Animometer/tests/text/layering-text.html
PerformanceTests/ChangeLog

index 66b13b7..c700d14 100644 (file)
@@ -141,42 +141,96 @@ ProgressBar.prototype =
     }
 }
 
-function RecordTable(element)
+function ResultsDashboard()
+{
+    this._iterationsSamplers = [];
+}
+
+ResultsDashboard.prototype =
+{
+    push: function(suitesSamplers)
+    {
+        this._iterationsSamplers.push(suitesSamplers);        
+    },
+    
+    toJSON: function(statistics, graph)
+    {
+        var iterationsResults = [];
+        var iterationsScores = [];
+        
+        this._iterationsSamplers.forEach(function(iterationSamplers, index) {
+            var suitesResults = {};
+            var suitesScores = [];
+        
+            for (var suiteName in iterationSamplers) {
+                var suite = suiteFromName(suiteName);
+                var suiteSamplers = iterationSamplers[suiteName];
+
+                var testsResults = {};
+                var testsScores = [];
+                
+                for (var testName in suiteSamplers) {
+                    var sampler = suiteSamplers[testName];
+                    testsResults[testName] = sampler.toJSON(statistics, graph);
+                    testsScores.push(testsResults[testName][Strings["JSON_SCORE"]]);
+                }
+
+                suitesResults[suiteName] =  {};
+                suitesResults[suiteName][Strings["JSON_SCORE"]] = Statistics.geometricMean(testsScores);
+                suitesResults[suiteName][Strings["JSON_RESULTS"][2]] = testsResults;
+                suitesScores.push(suitesResults[suiteName][Strings["JSON_SCORE"]]);
+            }
+            
+            iterationsResults[index] = {};
+            iterationsResults[index][Strings["JSON_SCORE"]] = Statistics.geometricMean(suitesScores);
+            iterationsResults[index][Strings["JSON_RESULTS"][1]] = suitesResults;
+            iterationsScores.push(iterationsResults[index][Strings["JSON_SCORE"]]);
+        });
+
+        var json = {};
+        json[Strings["JSON_SCORE"]] = Statistics.sampleMean(iterationsScores.length, iterationsScores.reduce(function(a, b) { return a * b; }));
+        json[Strings["JSON_RESULTS"][0]] = iterationsResults;
+        return json;
+    }
+}
+
+function ResultsTable(element, headers)
 {
     this.element = element;
+    this._headers = headers;
     this.clear();
 }
 
-RecordTable.prototype =
+ResultsTable.prototype =
 {
     clear: function()
     {
         this.element.innerHTML = "";
     },
-    
-    _showTitles: function(row, queue, titles, message)
+
+    _showHeaderRow: function(row, queue, headers, message)
     {
-        titles.forEach(function (title) {
+        headers.forEach(function (header) {
             var th = document.createElement("th");
-            th.textContent = title.text;
+            th.textContent = header.text;
             if (typeof message != "undefined" && message.length) {
                 th.appendChild(document.createElement('br'));
                 th.appendChild(document.createTextNode('[' + message +']'));
                 message = "";
             }
-            if ("width" in title)
-                th.width = title.width + "%";
+            if ("width" in header)
+                th.width = header.width + "%";
             row.appendChild(th);
-            queue.push({element: th, titles: title.children });
+            queue.push({element: th, headers: header.children });
         });
     },
-    
-    _showHeader: function(suiteName, titles, message)
+
+    _showHeader: function(message)
     {
         var row = document.createElement("tr");
 
         var queue = [];
-        this._showTitles(row, queue, titles, message);
+        this._showHeaderRow(row, queue, this._headers, message);
         this.element.appendChild(row);
 
         while (queue.length) {
@@ -186,7 +240,7 @@ RecordTable.prototype =
             for (var i = 0, len = queue.length; i < len; ++i) {
                 var entry = queue.shift();
 
-                if (!entry.titles.length) {
+                if (!entry.headers.length) {
                     entries.push(entry.element);
                     continue;
                 }
@@ -194,8 +248,8 @@ RecordTable.prototype =
                 if (!row)
                     var row = document.createElement("tr");
 
-                this._showTitles(row, queue, entry.titles, "");
-                entry.element.colSpan = entry.titles.length;
+                this._showHeaderRow(row, queue, entry.headers, "");
+                entry.element.colSpan = entry.headers.length;
             }
 
             if (row) {
@@ -207,100 +261,132 @@ RecordTable.prototype =
         }
     },
     
-    _showEmpty: function(row, testName)
+    _showEmpty: function(row)
     {
         var td = document.createElement("td");
         row.appendChild(td);
     },
-    
-    _showValue: function(row, testName, value)
+
+    _showText: function(row, text)
     {
         var td = document.createElement("td");
-        td.textContent = value.toFixed(2);
+        td.textContent = text;
+        row.appendChild(td);
+    },
+
+    _showFixedNumber: function(row, value, digits)
+    {
+        var td = document.createElement("td");
+        td.textContent = value.toFixed(digits || 2);
         row.appendChild(td);
     },
     
-    _showSamples: function(row, testName, axes, samples, samplingTimeOffset)
+    _showGraph: function(row, testName, testResults)
     {
+        var data = testResults[Strings["JSON_SAMPLES"][0]];
+        if (!data) {
+            this._showEmpty(row);
+            return;
+        }
+        
         var td = document.createElement("td");
         var button = document.createElement("div");
         button.className = "small-button";
-            
+
         button.addEventListener("click", function() {
-            window.showGraph(testName, axes, samples, samplingTimeOffset);
+            var samples = data[Strings["JSON_GRAPH"][0]];
+            var samplingTimeOffset = data[Strings["JSON_GRAPH"][1]];
+            var axes = Strings["TEXT_EXPERIMENTS"];
+            window.showTestGraph(testName, axes, samples, samplingTimeOffset);
         });
             
-        button.textContent = "Graph...";
+        button.textContent = Strings["TEXT_RESULTS"][1] + "...";
         td.appendChild(button);
         row.appendChild(td);
     },
-    
-    _showTest: function(testName, titles, sampler, finalResults)
+
+    _showJSON: function(row, testName, testResults)
     {
-        var row = document.createElement("tr");
-        var td = document.createElement("td");
+        var data = testResults[Strings["JSON_SAMPLES"][0]];
+        if (!data) {
+            this._showEmpty(row);
+            return;
+        }
         
-        td.textContent = testName;
+        var td = document.createElement("td");
+        var button = document.createElement("div");
+        button.className = "small-button";
+
+        button.addEventListener("click", function() {
+            window.showTestJSON(testName, testResults);
+        });
+            
+        button.textContent = Strings["TEXT_RESULTS"][2] + "...";
+        td.appendChild(button);
         row.appendChild(td);
+    },
+
+    _showTest: function(testName, testResults)
+    {
+        var row = document.createElement("tr");
         
-        var axes = [];
-        sampler.experiments.forEach(function(experiment, index) {
-            this._showValue(row, testName, experiment.mean());
-            this._showValue(row, testName, experiment.concern(Experiment.defaults.CONCERN));
-            this._showValue(row, testName, experiment.standardDeviation());
-            this._showValue(row, testName, experiment.percentage());
-            axes.push(titles[index + 1].text);
-            
-        }, this);
+        for (var index = 0; index < this._headers.length; ++index) {
 
-        this._showValue(row, testName, sampler.experiments[0].score(Experiment.defaults.CONCERN));
+            switch (index) {
+            case 0:
+                this._showText(row, testName);
+                break;
 
-        if (finalResults)
-            this._showSamples(row, testName, axes, sampler.samples, sampler.samplingTimeOffset);
-        else
-            this._showEmpty(row, testName);
-            
+            case 1:
+                var data = testResults[Strings["JSON_SCORE"][0]];
+                this._showFixedNumber(row, data, 2);
+                break;
+
+            case 2:
+            case 3:
+                var data = testResults[Strings["JSON_EXPERIMENTS"][index - 2]];
+                for (var measurement in data)
+                    this._showFixedNumber(row, data[measurement], 2);
+                break;
+                
+            case 4:
+                this._showGraph(row, testName, testResults);
+                this._showJSON(row, testName, testResults);
+                break;
+            }
+        }
+        
         this.element.appendChild(row);
     },
-    
-    _showSuite: function(suite, suiteSamplers)
+
+    _showSuite: function(suiteName, suiteResults)
     {
-        var scores = [];        
-        for (var testName in suiteSamplers) {
-            var test = testFromName(suite, testName);
-            var sampler = suiteSamplers[testName]; 
-            this._showTest(testName, suite.titles, sampler, true);
-            scores.push(sampler.experiments[0].score(Experiment.defaults.CONCERN));
+        for (var testName in suiteResults[Strings["JSON_RESULTS"][2]]) {
+            this._showTest(testName, suiteResults[Strings["JSON_RESULTS"][2]][testName]);
         }
-        return scores;
     },
     
-    showRecord: function(suite, test, sampler, message)
+    _showIteration : function(iterationResults)
     {
-        this.clear();        
-        this._showHeader("", suite.titles, message);
-        this._showTest(test.name, suite.titles, sampler, false);        
+        for (var suiteName in iterationResults[Strings["JSON_RESULTS"][1]]) {
+            this._showSuite(suiteName, iterationResults[Strings["JSON_RESULTS"][1]][suiteName]);
+        }
     },
     
-    showIterations: function(iterationsSamplers)
+    showRecord: function(testName, message, testResults)
     {
         this.clear();
+        this._showHeader(message);
+        this._showTest(testName, testResults);
+    },
 
-        var scores = [];
-        var titles = null;
-        iterationsSamplers.forEach(function(suitesSamplers) {
-            for (var suiteName in suitesSamplers) {
-                var suite = suiteFromName(suiteName);
-                if (titles != suite.titles) {
-                    titles = suite.titles;
-                    this._showHeader(suiteName, titles, "");
-                }
-
-                var suiteScores = this._showSuite(suite, suitesSamplers[suiteName]);
-                scores.push.apply(scores, suiteScores);
-            }
+    showIterations: function(iterationsResults)
+    {
+        this.clear();
+        this._showHeader("");
+        
+        iterationsResults.forEach(function(iterationResults) {
+            this._showIteration(iterationResults);
         }, this);
-
-        return Statistics.geometricMean(scores);
     }
 }
index dcf9550..99d5f2d 100644 (file)
@@ -103,22 +103,49 @@ function Sampler(count)
 
 Sampler.prototype =
 {
-    startSampling: function(timeOffset)
+    startSampling: function(samplingTimeOffset)
     {
-        for (var index = 0; index < this.experiments.length; ++index)
-            this.experiments[index].startSampling();
+        this.experiments.forEach(function(experiment) {
+            experiment.startSampling();
+        });
             
-        this.samplingTimeOffset = timeOffset / 1000;
+        this.samplingTimeOffset = samplingTimeOffset / 1000;
     },
     
     sample: function(timeOffset, values)
     {
         if (values.length < this.experiments.length)
             throw "Not enough sample points";
+
+        this.experiments.forEach(function(experiment, index) {
+            experiment.sample(values[index]);
+        });
                     
-        for (var index = 0; index < this.experiments.length; ++index)
-            this.experiments[index].sample(values[index]);
-            
         this.samples.push({ timeOffset: timeOffset / 1000, values: values });
+    },
+    
+    toJSON: function(statistics, graph)
+    {
+        var results = {};
+         
+        results[Strings["JSON_SCORE"]] = this.experiments[0].score(Experiment.defaults.CONCERN);
+           
+        if (statistics) {
+            this.experiments.forEach(function(experiment, index) {
+                results[Strings["JSON_EXPERIMENTS"][index]] = {};
+                results[Strings["JSON_EXPERIMENTS"][index]][Strings["JSON_MEASUREMENTS"][0]] = experiment.mean();
+                results[Strings["JSON_EXPERIMENTS"][index]][Strings["JSON_MEASUREMENTS"][1]] = experiment.concern(Experiment.defaults.CONCERN);
+                results[Strings["JSON_EXPERIMENTS"][index]][Strings["JSON_MEASUREMENTS"][2]] = experiment.standardDeviation();
+                results[Strings["JSON_EXPERIMENTS"][index]][Strings["JSON_MEASUREMENTS"][3]] = experiment.percentage();
+            });
+        }
+        
+        if (graph) {
+            results[Strings["JSON_SAMPLES"][0]] = {};
+            results[Strings["JSON_SAMPLES"][0]][Strings["JSON_GRAPH"][0]] = this.samples;
+            results[Strings["JSON_SAMPLES"][0]][Strings["JSON_GRAPH"][1]] = this.samplingTimeOffset;
+        }
+        
+        return results;
     }
 }
diff --git a/PerformanceTests/Animometer/resources/strings.js b/PerformanceTests/Animometer/resources/strings.js
new file mode 100644 (file)
index 0000000..0a7ee22
--- /dev/null
@@ -0,0 +1,15 @@
+var Strings = {
+    TEXT_TESTNAME:       [ "Test Name" ],
+    TEXT_EXPERIMENTS:    [ "Complexity", "FPS" ],
+    TEXT_MEASUREMENTS:   [ "Avg.", "W.5%", "Std.", "%" ],
+    TEXT_SCORE:          [ "Score" ],
+    TEXT_SAMPLES:        [ "Samples" ],
+    TEXT_RESULTS:        [ "Results", "Graph", "JSON" ],
+    
+    JSON_EXPERIMENTS:    [ "complexity", "frameRate" ],
+    JSON_MEASUREMENTS:   [ "average", "concern", "stdev", "percent" ],
+    JSON_SCORE:          [ "score" ],
+    JSON_SAMPLES:        [ "samples" ],
+    JSON_GRAPH:          [ "points", "samplingTimeOffset" ],
+    JSON_RESULTS:        [ "iterationsResults", "suitesResults", "testsResults" ],
+};
index d8d586c..1c3e31a 100644 (file)
@@ -2,6 +2,7 @@
 <html>
 <head>
     <link rel="stylesheet" href="resources/animometer.css">
+    <script src="../resources/strings.js" defer></script>
     <script src="../resources/sampler.js" defer></script>
     <script src="../resources/extensions.js" defer></script>
     <script src="resources/tests.js" defer=""></script>
             </div>
         </section>
         <section id="results">
-            <h1>Results</h1>
+            <h1>Results:</h1>
             <table class="results-table"></table>
             <div class="buttons">
+                <button onclick="showJson()">JSON</button>
                 <button onclick="startTest()">Test Again</button>
             </div>
         </section>  
-        <section id="graph">
-            <h1>Graph</h1>
+        <section id="json">
+            <h1>JSON:</h1>
+            <textarea class="results-json"></textarea>
+            <div class="buttons">
+                <button onclick="showResults()">Results</button>
+                <button onclick="startTest()">Test Again</button>
+            </div>
+        </section>  
+        <section id="test-graph">
+            <h1>Graph:</h1>
             <div id="graphContainer"></div>
             <div class="buttons">
                 <button onclick="showResults()">Results</button>
                 <button onclick="startTest()">Test Again</button>        
             </div>
         </section>  
+        <section id="test-json">
+            <h1>JSON:</h1>
+            <textarea class="results-json"></textarea>
+            <div class="buttons">
+                <button onclick="showResults()">Results</button>
+                <button onclick="startTest()">Test Again</button>        
+            </div>
+        </section>  
     </main>
 </body>
 </html>
index 1da3360..af79626 100644 (file)
@@ -117,6 +117,17 @@ section#results > table th {
     text-align: center;
 }
 
+section#test-json > textarea,
+section#json > textarea {
+    width: 800px;
+    height: 460px;
+    background-color: rgb(128, 128, 128);
+    border: 1px solid rgb(235, 235, 235);
+    color: white;
+    white-space: pre;
+    overflow: scroll;
+}
+
 .options {
     margin:0 auto;    
     margin-top: 30px;
index d8b3825..aaea4f1 100644 (file)
@@ -5,7 +5,7 @@ window.benchmarkRunnerClient = {
     recordTable: null,
     options: { testInterval: 30000, frameRate: 50, estimatedFrameRate: true, fixTestComplexity : false },
     score: 0,
-    _iterationsSamplers: [],
+    _resultsDashboard: null,
     _resultsTable: null,
     
     willAddTestFrame: function (frame)
@@ -23,21 +23,31 @@ window.benchmarkRunnerClient = {
     
     willStartFirstIteration: function ()
     {
-        this._iterationsSamplers = [];
-        this._resultsTable = new RecordTable(document.querySelectorAll(".results-table")[0]);
+        this._resultsDashboard = new ResultsDashboard();
+        this._resultsTable = new ResultsTable(document.querySelector(".results-table"), Headers);
         
         this.progressBar = new ProgressBar(document.getElementById("progress-completed"), this.testsCount);
-        this.recordTable = new RecordTable(document.querySelectorAll(".record-table")[0]);
+        this.recordTable = new ResultsTable(document.querySelector(".record-table"), Headers);
     },
     
     didRunSuites: function (suitesSamplers)
     {
-        this._iterationsSamplers.push(suitesSamplers);
+        this._resultsDashboard.push(suitesSamplers);
     },
     
     didFinishLastIteration: function ()
     {
-        this.score = this._resultsTable.showIterations(this._iterationsSamplers, "");
+        var json = this._resultsDashboard.toJSON(true, true);
+        this._resultsTable.showIterations(json[Strings["JSON_RESULTS"][0]]);
+        
+        var element = document.querySelector("#json > textarea");
+        element.innerHTML = JSON.stringify(json[Strings["JSON_RESULTS"][0]][0], function(key, value) { 
+            if (typeof value == "number")
+                return value.toFixed(2);
+            return value;
+        }, 4);
+        
+        this.score = json[Strings["JSON_SCORE"]];
         showResults();
     }
 }
@@ -92,10 +102,10 @@ function startTest()
     startBenchmark();
 }
 
-function showResults(score)
+function showResults()
 {
     var element = document.querySelector("#results > h1");
-    element.textContent = "Results:"
+    element.textContent = Strings["TEXT_RESULTS"][0] + ":";
     
     var score = benchmarkRunnerClient.score.toFixed(2);
     element.textContent += " [Score = " + score + "]";
@@ -103,16 +113,45 @@ function showResults(score)
     showSection("results", true);
 }
 
-function showGraph(testName, axes, samples, samplingTimeOffset)
+function showJson()
+{
+    var element = document.querySelector("#json > h1");
+    element.textContent = Strings["TEXT_RESULTS"][2] + ":";
+    
+    var score = benchmarkRunnerClient.score.toFixed(2);
+    element.textContent += " [Score = " + score + "]";
+
+    showSection("json", true);
+}
+
+function showTestGraph(testName, axes, samples, samplingTimeOffset)
 {
-    var element = document.querySelector("#graph > h1");
-    element.textContent = "Graph:"
+    var element = document.querySelector("#test-graph > h1");
+    element.textContent = Strings["TEXT_RESULTS"][1] + ":";
 
     if (testName.length)
         element.textContent += " [test = " + testName + "]";
             
     graph("#graphContainer", new Point(700, 400), new Insets(20, 50, 20, 50), axes, samples, samplingTimeOffset);
-    showSection("graph", true);    
+    showSection("test-graph", true);    
+}
+
+function showTestJSON(testName, testResults)
+{
+    var element = document.querySelector("#test-json > h1");
+    element.textContent = Strings["TEXT_RESULTS"][2] + ":";
+
+    if (testName.length)
+        element.textContent += " [test = " + testName + "]";
+            
+    var element = document.querySelector("#test-json > textarea");
+    element.innerHTML = JSON.stringify(testResults, function(key, value) { 
+        if (typeof value == "number")
+            return value.toFixed(2);
+        return value;
+    }, 4);
+
+    showSection("test-json", true);    
 }
 
 function populateSettings() {
index 1f715ff..c90467e 100644 (file)
@@ -1,46 +1,49 @@
-var Titles = [
+var Headers = [
     {
-        text: "Test Name",
-        width: 28,
+        text: Strings["TEXT_TESTNAME"][0],
+        width: 27,
         children: []
     },
     {
-        text: "Animated Items",
-        width: 28,
+        text: Strings["TEXT_SCORE"][0],
+        width: 7,
+        children: []
+    },
+    {
+        text: Strings["TEXT_EXPERIMENTS"][0],
+        width: 27,
         children:
         [
-            { text:   "Avg.", width: 7, children: [] },
-            { text:   "W.5%", width: 7, children: [] },
-            { text:   "Std.", width: 7, children: [] },
-            { text:      "%", width: 7, children: [] },
+            { text: Strings["TEXT_MEASUREMENTS"][0], width: 7, children: [] },
+            { text: Strings["TEXT_MEASUREMENTS"][1], width: 7, children: [] },
+            { text: Strings["TEXT_MEASUREMENTS"][2], width: 7, children: [] },
+            { text: Strings["TEXT_MEASUREMENTS"][3], width: 6, children: [] },
         ]
     },
     {
-        text: "FPS",
-        width: 28,
+        text: Strings["TEXT_EXPERIMENTS"][1],
+        width: 24,
         children:
         [
-            { text:   "Avg.", width: 7, children: [] },
-            { text:   "W.5%", width: 7, children: [] },
-            { text:   "Std.", width: 7, children: [] },
-            { text:      "%", width: 7, children: [] },
+            { text: Strings["TEXT_MEASUREMENTS"][0], width: 6, children: [] },
+            { text: Strings["TEXT_MEASUREMENTS"][1], width: 6, children: [] },
+            { text: Strings["TEXT_MEASUREMENTS"][2], width: 6, children: [] },
+            { text: Strings["TEXT_MEASUREMENTS"][3], width: 6, children: [] },
         ]
     },
     {
-        text: "Score",
-        width: 8,
-        children: []
-    },
-    {
-        text: "Samples",
-        width: 8,
-        children: []
+        text: Strings["TEXT_SAMPLES"][0],
+        width: 15,
+        children:
+        [
+            { text: Strings["TEXT_RESULTS"][1], width: 8, children: [] },
+            { text: Strings["TEXT_RESULTS"][2], width: 7, children: [] },
+        ]
     }
 ];
 
 var Suite = function(name, tests) {
     this.name = name;
-    this.titles = Titles;
     this.tests = tests;
 };
 Suite.prototype.prepare = function(runner, contentWindow, contentDocument)
@@ -54,7 +57,6 @@ Suite.prototype.run = function(contentWindow, test, options, recordTable, progre
     return contentWindow.runBenchmark(this, test, options, recordTable, progressBar);
 };
 
-
 var Suites = [];
 
 Suites.push(new Suite("HTML suite",
index 9e5886c..45baf4d 100644 (file)
@@ -9,6 +9,7 @@
     </style>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index 4b1bc35..7255873 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index f236420..dfb5ba6 100644 (file)
@@ -8,6 +8,7 @@
     </style>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index 6693638..0cfc6fa 100644 (file)
@@ -19,6 +19,7 @@
     </style>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index 1cd90cf..5a8ce43 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index 3c069fd..55b8edb 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index 22c20a6..cc87891 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index abf6ea7..382748a 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index 4763619..07fa661 100644 (file)
@@ -174,11 +174,11 @@ StageBenchmark.prototype.clear = function()
 
 StageBenchmark.prototype.showResults = function(message, progress)
 {
-    if (!this._recordTable || !this._progressBar || !this._suite || !this._test)
+    if (!this._recordTable || !this._progressBar || !this._test)
         return;
 
     if (this.options.showRunningResults)
-        this._recordTable.showRecord(this._suite, this._test, this._sampler, message);
+        this._recordTable.showRecord(this._test.name, message, this._sampler.toJSON(true, false));
 
     this._progressBar.setPos(progress);
 }
index bf61846..64d7109 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index bbf9695..6ce5dab 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index e7d246d..2aa80db 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index cfaf70e..b9c8fce 100644 (file)
@@ -3,6 +3,7 @@
 <head>
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index d7e3d05..f58ef4c 100644 (file)
@@ -13,6 +13,7 @@
     </style>  
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/strings.js"></script>
     <script src="../../resources/sampler.js"></script>
     <script src="../../resources/extensions.js"></script>
     <script src="../resources/math.js"></script>
index 0463633..3e0a7f0 100644 (file)
@@ -1,3 +1,105 @@
+2015-10-26  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Add an option to output the results of the graphics benchmark in JSON format
+        https://bugs.webkit.org/show_bug.cgi?id=150484
+        <rdar://problem/23243721>
+
+        Reviewed by Darin Adler.
+
+        * Animometer/resources/extensions.js:
+        (ResultsDashboard): A new class to hold the iterations results.
+        (ResultsDashboard.prototype.push): Appends an iteration results;
+        (ResultsDashboard.prototype.toJSON): Converts the iterations results to JSON format.
+
+        (RecordTable.prototype.clear): Clears the results table.
+        (RecordTable.prototype._showTitles): Shows the header titles and appends the sub-titles to a queue.
+        (RecordTable.prototype._showHeader): Shows the table header titles.
+        (RecordTable.prototype._showEmpty): Shows an empty table cell.
+        (RecordTable.prototype._showValue): Shows a number value in the results table.
+        (RecordTable.prototype._showSamples): Shows a button for the sampled data graph.
+        (RecordTable.prototype._showTest): Shows the results of a single test.
+        (RecordTable.prototype._showSuite): Shows the results of a single suite.
+        (RecordTable.prototype.showRecord): Shows a single iteration for a single test.
+        (RecordTable.prototype.showIterations): Shows the results of all the suites of the iterations. 
+        
+        (ResultsTable): RecordTable was renamed to ResultsTable.
+        (ResultsTable.prototype.clear): Clears the table element.
+        (ResultsTable.prototype._showHeaderRow): Shows a row in the results table header.
+        (ResultsTable.prototype._showHeader): Shows the results table header.
+        (ResultsTable.prototype._showEmpty): Shows an empty table cell.
+        (ResultsTable.prototype._showText): Shows a string in a new table cell.
+        (ResultsTable.prototype._showFixedNumber): Shows a number in a new table cell.
+        (ResultsTable.prototype._showGraph): Shows a button for the sampled data graph.
+        (ResultsTable.prototype._showJSON): Shows a button for the sampled data JSON.
+        (ResultsTable.prototype._showTest): Shows the results of a single test.
+        (ResultsTable.prototype._showSuite): Shows the results of a single suite.
+        (ResultsTable.prototype._showIteration): Shows the results of a single iteration.
+        (ResultsTable.prototype.showRecord): Shows a single iteration for a single test.
+        (ResultsTable.prototype.showIterations): Shows all the results.
+        (RecordTable): Deleted.
+        (RecordTable.prototype.clear): Deleted.
+        (RecordTable.prototype._showTitles): Deleted.
+        (RecordTable.prototype._showHeader): Deleted.
+        (RecordTable.prototype._showEmpty): Deleted.
+        (RecordTable.prototype._showValue): Deleted.
+        (RecordTable.prototype._showSamples): Deleted.
+        (RecordTable.prototype._showTest): Deleted.
+        (RecordTable.prototype._showSuite): Deleted.
+        (RecordTable.prototype.showRecord): Deleted.
+        (RecordTable.prototype.showIterations): Deleted.
+        
+        * Animometer/resources/sampler.js:
+        (Sampler.prototype.startSampling): Use forEach.
+        (Sampler.prototype.sample): Use forEach.
+        (Sampler.prototype.toJSON): Converts the sampler data to JSON format.
+        
+        * Animometer/resources/strings.js: Added.
+        This new file will be used to associate the strings used by the benchmark
+        with IDs. A string can be changed in one place without having to change
+        all the instances of this string in multiple files. There two groups of
+        strings in this file. The first one is used by the UI elements and the second
+        group is used when constructing the results JSON.
+        
+        * Animometer/runner/animometer.html:
+        * Animometer/runner/resources/animometer.css:
+        
+        * Animometer/runner/resources/animometer.js:
+        (window.benchmarkRunnerClient.willAddTestFrame):
+        (window.benchmarkRunnerClient.willStartFirstIteration):
+        (window.benchmarkRunnerClient.didRunSuites):
+        (window.benchmarkRunnerClient.didFinishLastIteration):
+        Make benchmarkRunnerClient uses ResultsDashboard instead of _iterationsSamplers
+        Get the JSON from ResultsDashboard.toJSON() and pass it to ResultsTable.showIterations().
+        Also set the textContent of the "#json" textarea with the results JSON.
+        
+        (showResults): Delete unneeded code.
+        (showJson): Shows the "json" section.
+        (showTestGraph): Rename showGraph() to be showTestGraph().
+        (showTestJSON): Shows the JSON of a single testResults.
+        (showGraph): Deleted.
+        
+        * Animometer/runner/resources/tests.js:
+        Use the string table instead of putting literal strings.
+
+        * Animometer/tests/resources/stage.js:
+        (StageBenchmark.prototype.showResults):
+        Fix the parameters which are passed to RecordTable.showRecord().
+        
+        * Animometer/tests/bouncing-particles/bouncing-canvas-images.html:
+        * Animometer/tests/bouncing-particles/bouncing-canvas-shapes.html:
+        * Animometer/tests/bouncing-particles/bouncing-css-images.html:
+        * Animometer/tests/bouncing-particles/bouncing-css-shapes.html:
+        * Animometer/tests/bouncing-particles/bouncing-svg-images.html:
+        * Animometer/tests/bouncing-particles/bouncing-svg-shapes.html:
+        * Animometer/tests/examples/canvas-electrons.html:
+        * Animometer/tests/examples/canvas-stars.html:
+        * Animometer/tests/simple/simple-canvas-paths.html:
+        * Animometer/tests/template/template-canvas.html:
+        * Animometer/tests/template/template-css.html:
+        * Animometer/tests/template/template-svg.html:
+        * Animometer/tests/text/layering-text.html:
+        Include the new strings.js file in all the test files.
+
 2015-10-12  Jon Lee  <jonlee@apple.com>
 
         Add canvas line dash test