1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
3 * ------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 * This class allows one to create a hierarchy of tests and iterate over them.
23 * It replaces TestCase and TestCaseGroup classes.
26 goog
.provide('framework.common.tcuTestCase');
27 goog
.require('framework.common.tcuSkipList');
29 goog
.scope(function() {
31 var tcuTestCase
= framework
.common
.tcuTestCase
;
32 var tcuSkipList
= framework
.common
.tcuSkipList
;
34 tcuTestCase
.getQueryVal = function(key
) {
35 const queryVars
= window
.location
.search
.substring(1).split('&');
36 for (let kv
of queryVars
) {
38 if (decodeURIComponent(kv
[0]) === key
)
39 return decodeURIComponent(kv
[1]);
44 tcuTestCase
.isQuickMode
= () => tcuTestCase
.getQueryVal('quick') === '1';
45 tcuTestCase
.isQuietMode
= () => tcuTestCase
.getQueryVal('quiet') === '1';
48 * Reads the filter parameter from the URL to filter tests.
51 tcuTestCase
.getFilter
= () => tcuTestCase
.getQueryVal('filter');
54 * Indicates the state of an iteration operation.
57 tcuTestCase
.IterateResult
= {
62 /****************************************
64 ***************************************/
67 * A simple state machine.
68 * The purpose of this this object is to break
69 * long tests into small chunks that won't cause a timeout
72 tcuTestCase
.Runner = function() {
73 /** @type {tcuTestCase.DeqpTest} */ this.currentTest
= null;
74 /** @type {tcuTestCase.DeqpTest} */ this.nextTest
= null;
75 /** @type {tcuTestCase.DeqpTest} */ this.testCases
= null;
76 /** @type {?string } */ this.filter
= tcuTestCase
.getFilter();
80 * @param {tcuTestCase.DeqpTest} root The root test of the test tree.
82 tcuTestCase
.Runner
.prototype.setRoot = function(root
) {
83 this.currentTest
= null;
84 this.testCases
= root
;
87 tcuTestCase
.Runner
.prototype.setRange = function(range
) {
92 * Searches the test tree for the next executable test
93 * @return {?tcuTestCase.DeqpTest }
95 tcuTestCase
.Runner
.prototype.next = function() {
97 // First time? Use root test
98 if (!this.currentTest
) {
99 this.currentTest
= this.testCases
;
101 // Root is executable? Use it
102 if (this.currentTest
.isExecutable())
103 return this.currentTest
;
106 // Should we proceed with the next test?
107 if (tcuTestCase
.lastResult
== tcuTestCase
.IterateResult
.STOP
) {
108 // Look for next executable test
111 this.currentTest
= this.currentTest
.nextInRange(this.filter
, this.range
);
113 this.currentTest
= this.currentTest
.next(this.filter
);
114 } while (this.currentTest
&& !this.currentTest
.isExecutable());
117 return this.currentTest
;
121 * Schedule the callback to be run ASAP
122 * @param {function()} callback Callback to schedule
124 tcuTestCase
.Runner
.prototype.runCallback = function(callback
) {
125 setTimeout(function() {
131 * Call this function at the end of the test
133 tcuTestCase
.Runner
.prototype.terminate = function() {
135 if (!tcuTestCase
.isQuietMode()) {
136 console
.log('finishTest() after (in ms):', performance
.now());
140 tcuTestCase
.runner
= new tcuTestCase
.Runner();
142 /** @type {tcuTestCase.IterateResult} */ tcuTestCase
.lastResult
= tcuTestCase
.IterateResult
.STOP
;
144 /***************************************
146 ***************************************/
149 * Assigns name, description and specification to test
151 * @param {?string} name
152 * @param {?string} description
153 * @param {Object=} spec
155 tcuTestCase
.DeqpTest = function(name
, description
, spec
) {
156 this.name
= name
|| '';
157 this.description
= description
|| '';
159 this.currentTestNdx
= 0;
160 this.parentTest
= null;
161 this.childrenTests
= [];
162 this.executeAlways
= false;
166 * Abstract init function(each particular test will implement it, or not)
168 tcuTestCase
.DeqpTest
.prototype.init = function() {};
171 * Abstract deinit function(each particular test will implement it, or not)
173 tcuTestCase
.DeqpTest
.prototype.deinit = function() {};
176 * Abstract iterate function(each particular test will implement it, or not)
177 * @return {tcuTestCase.IterateResult}
179 tcuTestCase
.DeqpTest
.prototype.iterate = function() { return tcuTestCase
.IterateResult
.STOP
; };
182 * Checks if the test is executable
185 tcuTestCase
.DeqpTest
.prototype.isExecutable = function() {
186 return this.childrenTests
.length
== 0 || this.executeAlways
;
190 * Checks if the test is a leaf
192 tcuTestCase
.DeqpTest
.prototype.isLeaf = function() {
193 return this.childrenTests
.length
== 0;
197 * Marks the test as always executable
199 tcuTestCase
.DeqpTest
.prototype.makeExecutable = function() {
200 this.executeAlways
= true;
204 * Adds a child test to the test's children
205 * @param {tcuTestCase.DeqpTest} test
207 tcuTestCase
.DeqpTest
.prototype.addChild = function(test
) {
208 test
.parentTest
= this;
209 this.childrenTests
.push(test
);
213 * Sets the whole children tests array
214 * @param {Array<tcuTestCase.DeqpTest>} tests
216 tcuTestCase
.DeqpTest
.prototype.setChildren = function(tests
) {
217 for (var test
in tests
)
218 tests
[test
].parentTest
= this;
219 this.childrenTests
= tests
;
223 * Returns the next test in the hierarchy of tests
225 * @param {?string } pattern Optional pattern to search for
226 * @return {tcuTestCase.DeqpTest}
228 tcuTestCase
.DeqpTest
.prototype.next = function(pattern
) {
229 return this._nextHonoringSkipList(pattern
);
233 * Returns the next test in the hierarchy of tests, honoring the
234 * skip list, and reporting skipped tests.
236 * @param {?string } pattern Optional pattern to search for
237 * @return {tcuTestCase.DeqpTest}
239 tcuTestCase
.DeqpTest
.prototype._nextHonoringSkipList = function(pattern
) {
240 var tryAgain
= false;
244 test
= this._nextIgnoringSkipList(pattern
);
246 // See whether the skip list vetoes the execution of
248 var fullTestName
= test
.fullName();
249 var skipDisposition
= tcuSkipList
.getSkipStatus(fullTestName
);
250 if (skipDisposition
.skip
) {
252 setCurrentTestName(fullTestName
);
253 checkMessage(false, 'Skipping test due to tcuSkipList: ' + fullTestName
);
262 * Returns the next test in the hierarchy of tests, ignoring the
265 * @param {?string } pattern Optional pattern to search for
266 * @return {tcuTestCase.DeqpTest}
268 tcuTestCase
.DeqpTest
.prototype._nextIgnoringSkipList = function(pattern
) {
270 return this._findIgnoringSkipList(pattern
);
274 //Look for the next child
275 if (this.currentTestNdx
< this.childrenTests
.length
) {
276 test
= this.childrenTests
[this.currentTestNdx
];
277 this.currentTestNdx
++;
280 // If no more children, get the next brother
281 if (test
== null && this.parentTest
!= null) {
282 test
= this.parentTest
._nextIgnoringSkipList(null);
289 * Returns the next test in the hierarchy of tests
290 * whose 1st level is in the given range
292 * @param {?string } pattern Optional pattern to search for
293 * @param {Array<number>} range
294 * @return {tcuTestCase.DeqpTest}
296 tcuTestCase
.DeqpTest
.prototype.nextInRange = function(pattern
, range
) {
298 var test
= this._nextHonoringSkipList(pattern
);
301 var topLevelId
= tcuTestCase
.runner
.testCases
.currentTestNdx
- 1;
302 if (topLevelId
>= range
[0] && topLevelId
< range
[1])
308 * Returns the full name of the test
310 * @return {string} Full test name.
312 tcuTestCase
.DeqpTest
.prototype.fullName = function() {
313 if (this.parentTest
) {
314 var parentName
= this.parentTest
.fullName();
316 return parentName
+ '.' + this.name
;
322 * Returns the description of the test
324 * @return {string} Test description.
326 tcuTestCase
.DeqpTest
.prototype.getDescription = function() {
327 return this.description
;
331 * Find a test with a matching name. Fast-forwards to a test whose
332 * full name matches the given pattern.
334 * @param {string} pattern Regular expression to search for
335 * @return {?tcuTestCase.DeqpTest } Found test or null.
337 tcuTestCase
.DeqpTest
.prototype.find = function(pattern
) {
338 return this._findHonoringSkipList(pattern
);
342 * Find a test with a matching name. Fast-forwards to a test whose
343 * full name matches the given pattern, honoring the skip list, and
344 * reporting skipped tests.
346 * @param {string} pattern Regular expression to search for
347 * @return {?tcuTestCase.DeqpTest } Found test or null.
349 tcuTestCase
.DeqpTest
.prototype._findHonoringSkipList = function(pattern
) {
350 var tryAgain
= false;
354 test
= this._findIgnoringSkipList(pattern
);
356 // See whether the skip list vetoes the execution of
358 var fullTestName
= test
.fullName();
359 var skipDisposition
= tcuSkipList
.getSkipStatus(fullTestName
);
360 if (skipDisposition
.skip
) {
362 checkMessage(false, 'Skipping test due to tcuSkipList: ' + fullTestName
);
370 * Find a test with a matching name. Fast-forwards to a test whose
371 * full name matches the given pattern.
373 * @param {string} pattern Regular expression to search for
374 * @return {?tcuTestCase.DeqpTest } Found test or null.
376 tcuTestCase
.DeqpTest
.prototype._findIgnoringSkipList = function(pattern
) {
379 test
= test
._nextIgnoringSkipList(null);
382 if (test
.fullName().match(pattern
) || test
.executeAlways
)
389 * Reset the iterator.
391 tcuTestCase
.DeqpTest
.prototype.reset = function() {
392 this.currentTestNdx
= 0;
394 for (var i
= 0; i
< this.childrenTests
.length
; i
++)
395 this.childrenTests
[i
].reset();
401 * @param {?string} name Short test name
402 * @param {?string} description Description of the test
403 * @param {Object=} spec Test specification
405 * @return {tcuTestCase.DeqpTest} The new test
407 tcuTestCase
.newTest = function(name
, description
, spec
) {
408 var test
= new tcuTestCase
.DeqpTest(name
, description
, spec
);
414 * Defines a new executable test so it gets run even if it's not a leaf
416 * @param {string} name Short test name
417 * @param {string} description Description of the test
418 * @param {Object=} spec Test specification
420 * @return {tcuTestCase.DeqpTest} The new test
422 tcuTestCase
.newExecutableTest = function(name
, description
, spec
) {
423 var test
= tcuTestCase
.newTest(name
, description
, spec
);
424 test
.makeExecutable();
430 * Run through the test cases giving time to system operation.
432 tcuTestCase
.runTestCases = function() {
433 var state
= tcuTestCase
.runner
;
437 // If proceeding with the next test, prepare it.
438 var fullTestName
= state
.currentTest
.fullName();
440 if (tcuTestCase
.lastResult
== tcuTestCase
.IterateResult
.STOP
) {
441 // Update current test name
442 setCurrentTestName(fullTestName
);
443 bufferedLogToConsole('Init testcase: ' + fullTestName
); //Show also in console so we can see which test crashed the browser's tab
445 // Initialize particular test
446 inited
= state
.currentTest
.init();
447 inited
= inited
=== undefined ? true : inited
;
449 //If it's a leaf test, notify of it's execution.
450 if (state
.currentTest
.isLeaf() && inited
)
451 debug('<hr/><br/>Start testcase: ' + fullTestName
);
455 // Run the test, save the result.
457 const debug
= tcuTestCase
._debug
= tcuTestCase
._debug
|| (() => {
458 function LapStopwatch() {
459 this.lap = function() {
460 const now
= performance
.now();
461 const ret
= now
- this.last
;
468 stopwatch
: new LapStopwatch(),
472 const overheadDur
= debug
.stopwatch
.lap();
474 tcuTestCase
.lastResult
= state
.currentTest
.iterate();
476 const testDur
= debug
.stopwatch
.lap();
477 debug
.testDoneCount
+= 1;
479 `[test ${debug.testDoneCount}] Ran in ${testDur}ms`,
480 `(+ ${overheadDur}ms overhead)`,
483 // Skip uninitialized test.
484 tcuTestCase
.lastResult
= tcuTestCase
.IterateResult
.STOP
;
488 if (tcuTestCase
.lastResult
== tcuTestCase
.IterateResult
.STOP
)
489 state
.currentTest
.deinit();
492 // If the exception was not thrown by a test check, log it, but don't throw it again
493 if (!(err
instanceof TestFailedException
)) {
494 //Stop execution of current test.
495 tcuTestCase
.lastResult
= tcuTestCase
.IterateResult
.STOP
;
498 if (tcuTestCase
.lastResult
== tcuTestCase
.IterateResult
.STOP
)
499 state
.currentTest
.deinit();
501 bufferedLogToConsole('Error while cleaning up test: ' + cerr
);
506 testFailedOptions(msg
, false);
508 bufferedLogToConsole(err
);
511 tcuTestCase
.runner
.runCallback(tcuTestCase
.runTestCases
);
513 tcuTestCase
.runner
.terminate();