1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 import "testlogger.js" as TestLogger
53 // Name of the test case to prefix the function name in messages.
56 // Set to true to start the test running.
57 property bool when: true
59 // Set to true once the test has completed.
60 property bool completed: false
62 // Set to true when the test is running but not yet complete.
63 property bool running: false
65 // Set to true if the test doesn't have to run (because some
66 // other test failed which this one depends on).
67 property bool optional: false
69 property bool windowShown: qtest.windowShown
71 // Internal private state. Identifiers prefixed with qtest are reserved.
72 property bool qtest_prevWhen: true
73 property int qtest_testId: -1
74 property bool qtest_componentCompleted : false
75 property var qtest_testCaseResult
76 property var qtest_results: qtest_results_normal
77 TestResult { id: qtest_results_normal }
78 property var qtest_events: qtest_events_normal
79 TestEvent { id: qtest_events_normal }
82 if (msg === undefined)
84 qtest_results.fail(msg, util.callerFile(), util.callerLine())
85 throw new Error("QtQuickTest::fail")
88 function qtest_fail(msg, frame) {
89 if (msg === undefined)
91 qtest_results.fail(msg, util.callerFile(frame), util.callerLine(frame))
92 throw new Error("QtQuickTest::fail")
95 function verify(cond, msg) {
96 if (msg === undefined)
98 if (!qtest_results.verify(cond, msg, util.callerFile(), util.callerLine()))
99 throw new Error("QtQuickTest::fail")
102 // Determine what is o.
103 // Discussions and reference: http://philrathe.com/articles/equiv
104 // Test suites: http://philrathe.com/tests/equiv
105 // Author: Philippe Rathé <prathe@gmail.com>
106 function qtest_typeof(o) {
107 if (typeof o === "undefined") {
110 // consider: typeof null === object
111 } else if (o === null) {
114 } else if (o.constructor === String) {
117 } else if (o.constructor === Boolean) {
120 } else if (o.constructor === Number) {
127 // consider: typeof [] === object
128 } else if (o instanceof Array) {
131 // consider: typeof new Date() === object
132 } else if (o instanceof Date) {
135 // consider: /./ instanceof Object;
136 // /./ instanceof RegExp;
137 // typeof /./ === "function"; // => false in IE and Opera,
138 // true in FF and Safari
139 } else if (o instanceof RegExp) {
142 } else if (typeof o === "object") {
143 if ("mapFromItem" in o && "mapToItem" in o) {
144 return "declarativeitem"; // @todo improve detection of declarative items
145 } else if ("x" in o && "y" in o && "z" in o) {
146 return "vector3d"; // Qt3D vector
149 } else if (o instanceof Function) {
157 // Large parts contain sources from QUnit or http://philrathe.com
158 // Discussions and reference: http://philrathe.com/articles/equiv
159 // Test suites: http://philrathe.com/tests/equiv
160 // Author: Philippe Rathé <prathe@gmail.com>
161 function qtest_compareInternal(act, exp) {
164 success = true; // catch the most you can
165 } else if (act === null || exp === null || typeof act === "undefined" || typeof exp === "undefined") {
166 success = false; // don't lose time with error prone cases
168 var typeExp = qtest_typeof(exp), typeAct = qtest_typeof(act)
169 if (typeExp !== typeAct) {
170 // allow object vs string comparison (e.g. for colors)
171 // else break on different types
172 if ((typeExp === "string" && (typeAct === "object") || typeAct == "declarativeitem")
173 || ((typeExp === "object" || typeExp == "declarativeitem") && typeAct === "string")) {
174 success = (act == exp)
176 } else if (typeExp === "string" || typeExp === "boolean" ||
177 typeExp === "null" || typeExp === "undefined") {
178 if (exp instanceof act.constructor || act instanceof exp.constructor) {
179 // to catch short annotaion VS 'new' annotation of act declaration
181 // var j = new Number(1);
182 success = (act == exp)
184 success = (act === exp)
186 } else if (typeExp === "nan") {
187 success = isNaN(act);
188 } else if (typeExp === "number") {
189 // Use act fuzzy compare if the two values are floats
190 if (Math.abs(act - exp) <= 0.00001) {
193 } else if (typeExp === "array") {
194 success = qtest_compareInternalArrays(act, exp)
195 } else if (typeExp === "object") {
196 success = qtest_compareInternalObjects(act, exp)
197 } else if (typeExp === "declarativeitem") {
198 success = qtest_compareInternalObjects(act, exp) // @todo improve comparison of declarative items
199 } else if (typeExp === "vector3d") {
200 success = (Math.abs(act.x - exp.x) <= 0.00001 &&
201 Math.abs(act.y - exp.y) <= 0.00001 &&
202 Math.abs(act.z - exp.z) <= 0.00001)
203 } else if (typeExp === "date") {
204 success = (act.valueOf() === exp.valueOf())
205 } else if (typeExp === "regexp") {
206 success = (act.source === exp.source && // the regex itself
207 act.global === exp.global && // and its modifers (gmi) ...
208 act.ignoreCase === exp.ignoreCase &&
209 act.multiline === exp.multiline)
215 function qtest_compareInternalObjects(act, exp) {
217 var eq = true; // unless we can proove it
218 var aProperties = [], bProperties = []; // collection of strings
220 // comparing constructors is more strict than using instanceof
221 if (act.constructor !== exp.constructor) {
225 for (i in act) { // be strict: don't ensures hasOwnProperty and go deep
226 aProperties.push(i); // collect act's properties
227 if (!qtest_compareInternal(act[i], exp[i])) {
234 bProperties.push(i); // collect exp's properties
237 // Ensures identical properties name
238 return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort());
242 function qtest_compareInternalArrays(actual, expected) {
243 if (actual.length != expected.length) {
247 for (var i = 0, len = actual.length; i < len; i++) {
248 if (!qtest_compareInternal(actual[i], expected[i])) {
256 function compare(actual, expected, msg) {
257 var act = qtest_results.stringify(actual)
258 var exp = qtest_results.stringify(expected)
260 var success = qtest_compareInternal(actual, expected)
261 if (msg === undefined) {
265 msg = "Compared values are not the same"
267 if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine())) {
268 throw new Error("QtQuickTest::fail")
272 function fuzzyCompare(actual, expected, delta, msg) {
273 if (delta === undefined)
274 qtest_fail("A delta value is required for fuzzyCompare", 2)
276 var success = qtest_results.fuzzyCompare(actual, expected, delta)
277 if (msg === undefined) {
279 msg = "FUZZYCOMPARE()"
281 msg = "Compared values are not the same with delta(" + delta + ")"
284 if (!qtest_results.compare(success, msg, actual, expected, util.callerFile(), util.callerLine())) {
285 throw new Error("QtQuickTest::fail")
289 function grabImage(item) {
290 return qtest_results.grabImage(item);
293 function tryCompare(obj, prop, value, timeout) {
296 if (!qtest_compareInternal(obj[prop], value))
299 while (i < timeout && !qtest_compareInternal(obj[prop], value)) {
303 var actual = obj[prop]
304 var act = qtest_results.stringify(actual)
305 var exp = qtest_results.stringify(value)
306 var success = qtest_compareInternal(actual, value)
307 if (!qtest_results.compare(success, "property " + prop, act, exp, util.callerFile(), util.callerLine()))
308 throw new Error("QtQuickTest::fail")
312 if (msg === undefined)
314 qtest_results.skip(msg, util.callerFile(), util.callerLine())
315 throw new Error("QtQuickTest::skip")
318 function expectFail(tag, msg) {
319 if (tag === undefined) {
320 warn("tag argument missing from expectFail()")
323 if (msg === undefined) {
324 warn("message argument missing from expectFail()")
327 if (!qtest_results.expectFail(tag, msg, util.callerFile(), util.callerLine()))
328 throw new Error("QtQuickTest::expectFail")
331 function expectFailContinue(tag, msg) {
332 if (tag === undefined) {
333 warn("tag argument missing from expectFailContinue()")
336 if (msg === undefined) {
337 warn("message argument missing from expectFailContinue()")
340 if (!qtest_results.expectFailContinue(tag, msg, util.callerFile(), util.callerLine()))
341 throw new Error("QtQuickTest::expectFail")
345 if (msg === undefined)
347 qtest_results.warn(msg, util.callerFile(), util.callerLine());
350 function ignoreWarning(msg) {
351 if (msg === undefined)
353 qtest_results.ignoreWarning(msg)
357 qtest_results.wait(ms)
361 qtest_results.sleep(ms)
364 function keyPress(key, modifiers, delay) {
365 if (modifiers === undefined)
366 modifiers = Qt.NoModifier
367 if (delay == undefined)
369 if (!qtest_events.keyPress(key, modifiers, delay))
370 qtest_fail("window not shown", 2)
373 function keyRelease(key, modifiers, delay) {
374 if (modifiers === undefined)
375 modifiers = Qt.NoModifier
376 if (delay == undefined)
378 if (!qtest_events.keyRelease(key, modifiers, delay))
379 qtest_fail("window not shown", 2)
382 function keyClick(key, modifiers, delay) {
383 if (modifiers === undefined)
384 modifiers = Qt.NoModifier
385 if (delay == undefined)
387 if (!qtest_events.keyClick(key, modifiers, delay))
388 qtest_fail("window not shown", 2)
391 function mousePress(item, x, y, button, modifiers, delay) {
392 if (button === undefined)
393 button = Qt.LeftButton
394 if (modifiers === undefined)
395 modifiers = Qt.NoModifier
396 if (delay == undefined)
398 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
399 qtest_fail("window not shown", 2)
402 function mouseRelease(item, x, y, button, modifiers, delay) {
403 if (button === undefined)
404 button = Qt.LeftButton
405 if (modifiers === undefined)
406 modifiers = Qt.NoModifier
407 if (delay == undefined)
409 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
410 qtest_fail("window not shown", 2)
413 function mouseDrag(item, x, y, dx, dy, button, modifiers, delay) {
414 if (item.x === undefined || item.y === undefined)
416 if (button === undefined)
417 button = Qt.LeftButton
418 if (modifiers === undefined)
419 modifiers = Qt.NoModifier
420 if (delay == undefined)
423 mousePress(item, x, y, button, modifiers, delay)
425 mouseMove(item, x + util.dragThreshold + 1, y + util.dragThreshold + 1, delay, button)
426 mouseMove(item, x + dx, y + dy, delay, button)
429 function mouseClick(item, x, y, button, modifiers, delay) {
430 if (button === undefined)
431 button = Qt.LeftButton
432 if (modifiers === undefined)
433 modifiers = Qt.NoModifier
434 if (delay == undefined)
436 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
437 qtest_fail("window not shown", 2)
440 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
441 if (button === undefined)
442 button = Qt.LeftButton
443 if (modifiers === undefined)
444 modifiers = Qt.NoModifier
445 if (delay == undefined)
447 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
448 qtest_fail("window not shown", 2)
451 function mouseMove(item, x, y, delay, buttons) {
452 if (delay == undefined)
454 if (buttons == undefined)
455 buttons = Qt.NoButton
456 if (!qtest_events.mouseMove(item, x, y, delay, buttons))
457 qtest_fail("window not shown", 2)
460 function mouseWheel(item, x, y, xDelta, yDelta, buttons, modifiers, delay) {
461 if (delay == undefined)
463 if (buttons == undefined)
464 buttons = Qt.NoButton
465 if (modifiers === undefined)
466 modifiers = Qt.NoModifier
467 if (xDelta == undefined)
469 if (yDelta == undefined)
471 if (!qtest_events.mouseWheel(item, x, y, buttons, modifiers, xDelta, yDelta, delay))
472 qtest_fail("window not shown", 2)
476 // Functions that can be overridden in subclasses for init/cleanup duties.
477 function initTestCase() {}
478 function cleanupTestCase() {}
480 function cleanup() {}
482 function qtest_runInternal(prop, arg) {
484 qtest_testCaseResult = testCase[prop](arg)
486 qtest_testCaseResult = []
487 if (e.message.indexOf("QtQuickTest::") != 0) {
488 // Test threw an unrecognized exception - fail.
489 qtest_results.fail("Uncaught exception: " + e.message,
490 e.fileName, e.lineNumber)
493 return !qtest_results.failed
496 function qtest_runFunction(prop, arg) {
497 qtest_runInternal("init")
498 if (!qtest_results.skipped) {
499 qtest_runInternal(prop, arg)
500 qtest_results.finishTestData()
501 qtest_runInternal("cleanup")
502 qtest_results.finishTestDataCleanup()
506 function qtest_runBenchmarkFunction(prop, arg) {
507 qtest_results.startMeasurement()
509 qtest_results.beginDataRun()
511 // Run the initialization function.
512 qtest_runInternal("init")
513 if (qtest_results.skipped)
516 // Execute the benchmark function.
517 if (prop.indexOf("benchmark_once_") != 0)
518 qtest_results.startBenchmark(TestResult.RepeatUntilValidMeasurement, qtest_results.dataTag)
520 qtest_results.startBenchmark(TestResult.RunOnce, qtest_results.dataTag)
521 while (!qtest_results.isBenchmarkDone()) {
522 var success = qtest_runInternal(prop, arg)
523 qtest_results.finishTestData()
526 qtest_results.nextBenchmark()
528 qtest_results.stopBenchmark()
530 // Run the cleanup function.
531 qtest_runInternal("cleanup")
532 qtest_results.finishTestDataCleanup()
533 } while (!qtest_results.measurementAccepted())
534 qtest_results.endDataRun()
535 } while (qtest_results.needsMoreMeasurements())
538 function qtest_run() {
539 if (util.printAvailableFunctions) {
544 if (TestLogger.log_start_test()) {
545 qtest_results.reset()
546 qtest_results.testCaseName = name
547 qtest_results.startLogging()
549 qtest_results.testCaseName = name
553 // Check the run list to see if this class is mentioned.
554 var functionsToRun = qtest_results.functionsToRun
555 if (functionsToRun.length > 0) {
558 if (name.length > 0) {
559 var prefix = name + "::"
560 for (var index in functionsToRun) {
561 if (functionsToRun[index].indexOf(prefix) == 0) {
562 list.push(functionsToRun[index])
569 if (!TestLogger.log_complete_test(qtest_testId)) {
570 qtest_results.stopLogging()
573 qtest_results.testCaseName = ""
576 functionsToRun = list
579 // Run the initTestCase function.
580 qtest_results.functionName = "initTestCase"
582 if (!qtest_runInternal("initTestCase"))
584 qtest_results.finishTestData()
585 qtest_results.finishTestDataCleanup()
586 qtest_results.finishTestFunction()
588 // Run the test methods.
591 for (var prop in testCase) {
592 if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
594 var tail = prop.lastIndexOf("_data");
595 if (tail != -1 && tail == (prop.length - 5))
601 var checkNames = (functionsToRun.length > 0)
602 for (var index in testList) {
603 var prop = testList[index]
604 var datafunc = prop + "_data"
605 var isBenchmark = (prop.indexOf("benchmark_") == 0)
607 var index = functionsToRun.indexOf(name + "::" + prop)
610 functionsToRun.splice(index, 1)
612 qtest_results.functionName = prop
614 if (!(datafunc in testCase))
615 datafunc = "init_data";
617 if (datafunc in testCase) {
618 if (qtest_runInternal(datafunc)) {
619 var table = qtest_testCaseResult
621 qtest_results.initTestTable()
622 for (var index in table) {
624 var row = table[index]
626 row.tag = "row " + index // Must have something
627 qtest_results.dataTag = row.tag
629 qtest_runBenchmarkFunction(prop, row)
631 qtest_runFunction(prop, row)
632 qtest_results.dataTag = ""
635 if (datafunc === "init_data")
636 qtest_runFunction(prop, null, isBenchmark)
638 qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()"
639 , util.callerFile(), util.callerLine());
641 qtest_results.clearTestTable()
643 } else if (isBenchmark) {
644 qtest_runBenchmarkFunction(prop, null, isBenchmark)
646 qtest_runFunction(prop, null, isBenchmark)
648 qtest_results.finishTestFunction()
649 qtest_results.skipped = false
652 // Run the cleanupTestCase function.
653 qtest_results.skipped = false
654 qtest_results.functionName = "cleanupTestCase"
655 qtest_runInternal("cleanupTestCase")
657 // Complain about missing functions that we were supposed to run.
658 if (functionsToRun.length > 0)
659 qtest_results.fail("Could not find functions: " + functionsToRun, "", 0)
661 // Clean up and exit.
664 qtest_results.finishTestData()
665 qtest_results.finishTestDataCleanup()
666 qtest_results.finishTestFunction()
667 qtest_results.functionName = ""
669 // Stop if there are no more tests to be run.
670 if (!TestLogger.log_complete_test(qtest_testId)) {
671 qtest_results.stopLogging()
674 qtest_results.testCaseName = ""
678 if (when != qtest_prevWhen) {
679 qtest_prevWhen = when
680 if (when && !completed && !running && qtest_componentCompleted)
688 TestLogger.log_optional_test(qtest_testId)
690 TestLogger.log_mandatory_test(qtest_testId)
695 Component.onCompleted: {
696 qtest.hasTestCase = true;
697 qtest_componentCompleted = true;
699 if (util.printAvailableFunctions) {
701 for (var prop in testCase) {
702 if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
704 var tail = prop.lastIndexOf("_data");
705 if (tail != -1 && tail == (prop.length - 5))
707 // Note: cannot run functions in TestCase elements
710 testList.push(name + "::" + prop + "()")
713 for (var index in testList)
714 console.log(testList[index])
717 qtest_testId = TestLogger.log_register_test(name)
719 TestLogger.log_optional_test(qtest_testId)
720 qtest_prevWhen = when
721 if (when && !completed && !running)