Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
4 "use strict";
6 // Load common code from ../mochitest/head.js
7 let mochitestDir = getRootDirectory(gTestPath).replace('/mochiperf', '/mochitest');
8 Services.scriptloader.loadSubScript(mochitestDir + "head.js", this);
10 // Misc. constants
11 const kInfoHeader = "PERF-TEST | ";
12 const kDeclareId = "DECLARE ";
13 const kResultsId = "RESULTS ";
15 // Mochitest log data format version
16 const kDataSetVersion = "1";
18 /*
19 * PerfTest - helper library for simple mochitest based performance tests.
20 */
22 var PerfTest = {
23 _userStartTime: 0,
24 _userStopTime: 0,
26 /******************************************************
27 * Declare and results
28 */
30 /*
31 * declareTest
32 *
33 * Declare a test which the graph server will pick up and track.
34 * Must be called by every test on startup. Graph server will
35 * search for result data between this declaration and the next.
36 *
37 * @param aUUID string for this particular test, case sensitive.
38 * @param aName The name of the test.
39 * @param aCategory Top level test calegory. For example 'General',
40 * 'Graphics', 'Startup', 'Jim's Tests'.
41 * @param aSubCategory (optional) sub category name with aCategory.
42 * @param aDescription A detailed description (sentence or two) of
43 * what the test does.
44 */
45 declareTest: function declareTest(aUUID, aName, aCategory, aSubCategory, aDescription) {
46 this._uid = aUUID;
47 this._print(kDeclareId, this._toJsonStr({
48 id: aUUID,
49 version: kDataSetVersion,
50 name: aName,
51 category: aCategory,
52 subcategory: aSubCategory,
53 description: aDescription,
54 buildid: Services.appinfo.appBuildID,
55 }));
56 },
58 /*
59 * declareNumericalResult
60 *
61 * Declare a simple numerical result.
62 *
63 * @param aValue numerical value to record
64 * @param aDescription string describing the value to display on the y axis.
65 */
66 declareNumericalResult: function declareNumericalResult(aValue, aDescription) {
67 this._print(kResultsId, this._toJsonStr({
68 id: this._uid,
69 version: kDataSetVersion,
70 results: {
71 r0: {
72 value: aValue,
73 desc: aDescription
74 }
75 },
76 }));
77 },
79 /*
80 * declareFrameRateResult
81 *
82 * Declare a frame rate for a result.
83 *
84 * @param aFrameCount numerical frame count
85 * @param aRunMs run time in miliseconds
86 * @param aDescription string describing the value to display on the y axis.
87 */
88 declareFrameRateResult: function declareFrameRateResult(aFrameCount, aRunMs, aDescription) {
89 this._print(kResultsId, this._toJsonStr({
90 id: this._uid,
91 version: kDataSetVersion,
92 results: {
93 r0: {
94 value: (aFrameCount / (aRunMs / 1000.0)),
95 desc: aDescription
96 }
97 },
98 }));
99 },
101 /*
102 * declareNumericalResults
103 *
104 * Declare a set of numerical results.
105 *
106 * @param aArray an array of datapoint objects of the form:
107 *
108 * [ { value: (value), desc: "description/units" }, .. ]
109 *
110 * optional values:
111 * shareAxis - the 0 based index of a previous data point this point
112 * should share a y axis with.
113 */
114 declareNumericalResults: function declareNumericalResults(aArray) {
115 let collection = new Object();
116 for (let idx = 0; idx < aArray.length; idx++) {
117 collection['r' + idx] = { value: aArray[idx].value, desc: aArray[idx].desc };
118 if (aArray[idx].shareAxis != undefined) {
119 collection['r' + idx].shareAxis = aArray[idx].shareAxis;
120 }
121 }
122 let dataset = {
123 id: this._uid,
124 version: kDataSetVersion,
125 results: collection
126 };
127 this._print(kResultsId, this._toJsonStr(dataset));
128 },
130 /******************************************************
131 * Perf tests
132 */
134 perfBoundsCheck: function perfBoundsCheck(aLow, aHigh, aValue, aTestMessage) {
135 ok(aValue < aLow || aValue > aHigh, aTestMessage);
136 },
138 /******************************************************
139 * Math utilities
140 */
142 computeMedian: function computeMedian(aArray, aOptions) {
143 aArray.sort(function (a, b) {
144 return a - b;
145 });
147 var idx = Math.floor(aArray.length / 2);
149 if(aArray.length % 2) {
150 return aArray[idx];
151 } else {
152 return (aArray[idx-1] + aArray[idx]) / 2;
153 }
154 },
156 computeAverage: function computeAverage(aArray, aOptions) {
157 let idx;
158 let count = 0, total = 0;
159 let highIdx = -1, lowIdx = -1;
160 let high = 0, low = 0;
161 if (aOptions.stripOutliers) {
162 for (idx = 0; idx < aArray.length; idx++) {
163 if (high < aArray[idx]) {
164 highIdx = idx;
165 high = aArray[idx];
166 }
167 if (low > aArray[idx]) {
168 lowIdx = idx;
169 low = aArray[idx];
170 }
171 }
172 }
173 for (idx = 0; idx < aArray.length; idx++) {
174 if (idx != high && idx != low) {
175 total += aArray[idx];
176 count++;
177 }
178 }
179 return total / count;
180 },
182 computeHighLowBands: function computeHighLow(aArray, aPercentage) {
183 let bandCount = Math.ceil(aArray.length * aPercentage);
184 let lowGroup = 0, highGroup = 0;
185 let idx;
187 function compareNumbers(a, b) {
188 return a - b;
189 }
190 aArray.sort(compareNumbers);
191 for (idx = 0; idx < bandCount; idx++) {
192 lowGroup += aArray[idx];
193 }
194 let top = aArray.length - 1;
195 for (idx = top; idx > (top - bandCount); idx--) {
196 highGroup += aArray[idx];
197 }
198 return {
199 low: lowGroup / bandCount,
200 high: highGroup / bandCount,
201 ave: this.computeAverage(aArray, {})
202 };
203 },
205 /******************************************************
206 * Internal
207 */
209 _print: function _print() {
210 let str = kInfoHeader;
211 for (let idx = 0; idx < arguments.length; idx++) {
212 str += arguments[idx];
213 }
214 info(str);
215 },
217 _toJsonStr: function _toJsonStr(aTable) {
218 return window.JSON.stringify(aTable);
219 },
220 };
222 /*
223 * StopWatch - timing helper
224 */
226 function StopWatch(aStart) {
227 if (aStart) {
228 this.start();
229 }
230 }
232 StopWatch.prototype = {
233 /*
234 * Start timing. Resets existing clock.
235 */
236 start: function start() {
237 this.reset();
238 this._userStartTime = window.performance.now();
239 },
241 /*
242 * Stop timing.
243 */
244 stop: function stop() {
245 this._userStopTime = window.performance.now();
246 return this.time();
247 },
249 /*
250 * Resets both start and end time.
251 */
252 reset: function reset() {
253 this._userStartTime = this._userStopTime = 0;
254 },
256 /*
257 * Returns the total time ellapsed in milliseconds. Returns zero if
258 * no time has been accumulated.
259 */
260 time: function time() {
261 if (!this._userStartTime) {
262 return 0;
263 }
264 if (!this._userStopTime) {
265 return (window.performance.now() - this._userStartTime);
266 }
267 return this._userStopTime - this._userStartTime;
268 },
269 };