1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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 that is set to true when the main window is shown.
70 // We need to set the property value in an odd way to handle
71 // both qmlviewer and the QtQuickTest module test wrapper.
72 property bool windowShown: util.wrapper ? qtest.windowShown : false
74 // Internal private state. Identifiers prefixed with qtest are reserved.
75 property bool qtest_prevWhen: true
76 property int qtest_testId: -1
77 property bool qtest_componentCompleted : false
78 property variant qtest_testCaseResult
79 property variant qtest_results: qtest_results_normal
80 TestResult { id: qtest_results_normal }
81 property variant qtest_events: qtest_events_normal
82 TestEvent { id: qtest_events_normal }
85 if (msg === undefined)
87 qtest_results.fail(msg, util.callerFile(), util.callerLine())
88 throw new Error("QtQuickTest::fail")
91 function qtest_fail(msg, frame) {
92 if (msg === undefined)
94 qtest_results.fail(msg, util.callerFile(frame), util.callerLine(frame))
95 throw new Error("QtQuickTest::fail")
98 function verify(cond, msg) {
99 if (msg === undefined)
101 if (!qtest_results.verify(cond, msg, util.callerFile(), util.callerLine()))
102 throw new Error("QtQuickTest::fail")
105 // Determine what is o.
106 // Discussions and reference: http://philrathe.com/articles/equiv
107 // Test suites: http://philrathe.com/tests/equiv
108 // Author: Philippe Rathé <prathe@gmail.com>
109 function qtest_typeof(o) {
110 if (typeof o === "undefined") {
113 // consider: typeof null === object
114 } else if (o === null) {
117 } else if (o.constructor === String) {
120 } else if (o.constructor === Boolean) {
123 } else if (o.constructor === Number) {
130 // consider: typeof [] === object
131 } else if (o instanceof Array) {
134 // consider: typeof new Date() === object
135 } else if (o instanceof Date) {
138 // consider: /./ instanceof Object;
139 // /./ instanceof RegExp;
140 // typeof /./ === "function"; // => false in IE and Opera,
141 // true in FF and Safari
142 } else if (o instanceof RegExp) {
145 } else if (typeof o === "object") {
146 if ("mapFromItem" in o && "mapToItem" in o) {
147 return "declarativeitem"; // @todo improve detection of declarative items
148 } else if ("x" in o && "y" in o && "z" in o) {
149 return "vector3d"; // Qt3D vector
152 } else if (o instanceof Function) {
160 // Large parts contain sources from QUnit or http://philrathe.com
161 // Discussions and reference: http://philrathe.com/articles/equiv
162 // Test suites: http://philrathe.com/tests/equiv
163 // Author: Philippe Rathé <prathe@gmail.com>
164 function qtest_compareInternal(act, exp) {
167 success = true; // catch the most you can
168 } else if (act === null || exp === null || typeof act === "undefined" || typeof exp === "undefined") {
169 success = false; // don't lose time with error prone cases
171 var typeExp = qtest_typeof(exp), typeAct = qtest_typeof(act)
172 if (typeExp !== typeAct) {
173 // allow object vs string comparison (e.g. for colors)
174 // else break on different types
175 if ((typeExp === "string" && (typeAct === "object") || typeAct == "declarativeitem")
176 || ((typeExp === "object" || typeExp == "declarativeitem") && typeAct === "string")) {
177 success = (act == exp)
179 } else if (typeExp === "string" || typeExp === "boolean" ||
180 typeExp === "null" || typeExp === "undefined") {
181 if (exp instanceof act.constructor || act instanceof exp.constructor) {
182 // to catch short annotaion VS 'new' annotation of act declaration
184 // var j = new Number(1);
185 success = (act == exp)
187 success = (act === exp)
189 } else if (typeExp === "nan") {
190 success = isNaN(act);
191 } else if (typeExp === "number") {
192 // Use act fuzzy compare if the two values are floats
193 if (Math.abs(act - exp) <= 0.00001) {
196 } else if (typeExp === "array") {
197 success = qtest_compareInternalArrays(act, exp)
198 } else if (typeExp === "object") {
199 success = qtest_compareInternalObjects(act, exp)
200 } else if (typeExp === "declarativeitem") {
201 success = qtest_compareInternalObjects(act, exp) // @todo improve comparison of declarative items
202 } else if (typeExp === "vector3d") {
203 success = (Math.abs(act.x - exp.x) <= 0.00001 &&
204 Math.abs(act.y - exp.y) <= 0.00001 &&
205 Math.abs(act.z - exp.z) <= 0.00001)
206 } else if (typeExp === "date") {
207 success = (act.valueOf() === exp.valueOf())
208 } else if (typeExp === "regexp") {
209 success = (act.source === exp.source && // the regex itself
210 act.global === exp.global && // and its modifers (gmi) ...
211 act.ignoreCase === exp.ignoreCase &&
212 act.multiline === exp.multiline)
218 function qtest_compareInternalObjects(act, exp) {
220 var eq = true; // unless we can proove it
221 var aProperties = [], bProperties = []; // collection of strings
223 // comparing constructors is more strict than using instanceof
224 if (act.constructor !== exp.constructor) {
228 for (i in act) { // be strict: don't ensures hasOwnProperty and go deep
229 aProperties.push(i); // collect act's properties
230 if (!qtest_compareInternal(act[i], exp[i])) {
237 bProperties.push(i); // collect exp's properties
240 // Ensures identical properties name
241 return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort());
245 function qtest_compareInternalArrays(actual, expected) {
246 if (actual.length != expected.length) {
250 for (var i = 0, len = actual.length; i < len; i++) {
251 if (!qtest_compareInternal(actual[i], expected[i])) {
259 function qtest_formatValue(value) {
260 if (qtest_typeof(value) == "object") {
261 if ("x" in value && "y" in value && "z" in value) {
262 return "Qt.vector3d(" + value.x + ", " +
263 value.y + ", " + value.z + ")"
266 return JSON.stringify(value)
268 // stringify might fail (e.g. due to circular references)
274 function compare(actual, expected, msg) {
275 var act = testCase.qtest_formatValue(actual)
276 var exp = testCase.qtest_formatValue(expected)
278 var success = qtest_compareInternal(actual, expected)
279 if (msg === undefined) {
283 msg = "Compared values are not the same"
285 if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine())) {
286 throw new Error("QtQuickTest::fail")
290 function tryCompare(obj, prop, value, timeout) {
293 if (!qtest_compareInternal(obj[prop], value))
296 while (i < timeout && !qtest_compareInternal(obj[prop], value)) {
300 var actual = obj[prop]
301 var act = testCase.qtest_formatValue(actual)
302 var exp = testCase.qtest_formatValue(value)
303 var success = qtest_compareInternal(actual, value)
304 if (!qtest_results.compare(success, "property " + prop, act, exp, util.callerFile(), util.callerLine()))
305 throw new Error("QtQuickTest::fail")
309 if (msg === undefined)
311 qtest_results.skip(msg, util.callerFile(), util.callerLine())
312 throw new Error("QtQuickTest::skip")
315 function skipAll(msg) {
316 msg = "The skipAll function is no longer available. Please update this test by changing skipAll to skip."
317 qtest_results.fail(msg, util.callerFile(), util.callerLine())
318 throw new Error("QtQuickTest::skip")
321 function expectFail(tag, msg) {
322 if (tag === undefined) {
323 warn("tag argument missing from expectFail()")
326 if (msg === undefined) {
327 warn("message argument missing from expectFail()")
330 if (!qtest_results.expectFail(tag, msg, util.callerFile(), util.callerLine()))
331 throw new Error("QtQuickTest::expectFail")
334 function expectFailContinue(tag, msg) {
335 if (tag === undefined) {
336 warn("tag argument missing from expectFailContinue()")
339 if (msg === undefined) {
340 warn("message argument missing from expectFailContinue()")
343 if (!qtest_results.expectFailContinue(tag, msg, util.callerFile(), util.callerLine()))
344 throw new Error("QtQuickTest::expectFail")
348 if (msg === undefined)
350 qtest_results.warn(msg, util.callerFile(), util.callerLine());
353 function ignoreWarning(msg) {
354 if (msg === undefined)
356 qtest_results.ignoreWarning(msg)
360 qtest_results.wait(ms)
364 qtest_results.sleep(ms)
367 function keyPress(key, modifiers, delay) {
368 if (modifiers === undefined)
369 modifiers = Qt.NoModifier
370 if (delay == undefined)
372 if (!qtest_events.keyPress(key, modifiers, delay))
373 qtest_fail("window not shown", 2)
376 function keyRelease(key, modifiers, delay) {
377 if (modifiers === undefined)
378 modifiers = Qt.NoModifier
379 if (delay == undefined)
381 if (!qtest_events.keyRelease(key, modifiers, delay))
382 qtest_fail("window not shown", 2)
385 function keyClick(key, modifiers, delay) {
386 if (modifiers === undefined)
387 modifiers = Qt.NoModifier
388 if (delay == undefined)
390 if (!qtest_events.keyClick(key, modifiers, delay))
391 qtest_fail("window not shown", 2)
394 function mousePress(item, x, y, button, modifiers, delay) {
395 if (button === undefined)
396 button = Qt.LeftButton
397 if (modifiers === undefined)
398 modifiers = Qt.NoModifier
399 if (delay == undefined)
401 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
402 qtest_fail("window not shown", 2)
405 function mouseRelease(item, x, y, button, modifiers, delay) {
406 if (button === undefined)
407 button = Qt.LeftButton
408 if (modifiers === undefined)
409 modifiers = Qt.NoModifier
410 if (delay == undefined)
412 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
413 qtest_fail("window not shown", 2)
416 function mouseClick(item, x, y, button, modifiers, delay) {
417 if (button === undefined)
418 button = Qt.LeftButton
419 if (modifiers === undefined)
420 modifiers = Qt.NoModifier
421 if (delay == undefined)
423 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
424 qtest_fail("window not shown", 2)
427 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
428 if (button === undefined)
429 button = Qt.LeftButton
430 if (modifiers === undefined)
431 modifiers = Qt.NoModifier
432 if (delay == undefined)
434 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
435 qtest_fail("window not shown", 2)
438 function mouseMove(item, x, y, delay, buttons) {
439 if (delay == undefined)
441 if (buttons == undefined)
442 buttons = Qt.NoButton
443 if (!qtest_events.mouseMove(item, x, y, delay, buttons))
444 qtest_fail("window not shown", 2)
447 function mouseWheel(item, x, y, delta, buttons, modifiers, delay, orientation) {
448 if (delay == undefined)
450 if (buttons == undefined)
451 buttons = Qt.NoButton
452 if (modifiers === undefined)
453 modifiers = Qt.NoModifier
454 if (delta == undefined)
456 if (orientation == undefined)
457 orientation = Qt.Vertical
458 if (!qtest_events.mouseWheel(item, x, y, buttons, modifiers, delta, delay, orientation))
459 qtest_fail("window not shown", 2)
463 // Functions that can be overridden in subclasses for init/cleanup duties.
464 function initTestCase() {}
465 function cleanupTestCase() {}
467 function cleanup() {}
469 function qtest_runInternal(prop, arg) {
471 qtest_testCaseResult = testCase[prop](arg)
473 qtest_testCaseResult = []
474 if (e.message.indexOf("QtQuickTest::") != 0) {
475 // Test threw an unrecognized exception - fail.
476 qtest_results.fail("Uncaught exception: " + e.message,
477 e.fileName, e.lineNumber)
480 return !qtest_results.dataFailed
483 function qtest_runFunction(prop, arg) {
484 qtest_results.functionType = TestResult.InitFunc
485 qtest_runInternal("init")
486 if (!qtest_results.skipped) {
487 qtest_results.functionType = TestResult.Func
488 qtest_runInternal(prop, arg)
489 qtest_results.functionType = TestResult.CleanupFunc
490 qtest_runInternal("cleanup")
492 qtest_results.functionType = TestResult.NoWhere
495 function qtest_runBenchmarkFunction(prop, arg) {
496 qtest_results.startMeasurement()
498 qtest_results.beginDataRun()
500 // Run the initialization function.
501 qtest_results.functionType = TestResult.InitFunc
502 qtest_runInternal("init")
503 if (qtest_results.skipped)
506 // Execute the benchmark function.
507 qtest_results.functionType = TestResult.Func
508 if (prop.indexOf("benchmark_once_") != 0)
509 qtest_results.startBenchmark(TestResult.RepeatUntilValidMeasurement, qtest_results.dataTag)
511 qtest_results.startBenchmark(TestResult.RunOnce, qtest_results.dataTag)
512 while (!qtest_results.isBenchmarkDone()) {
513 if (!qtest_runInternal(prop, arg))
515 qtest_results.nextBenchmark()
517 qtest_results.stopBenchmark()
519 // Run the cleanup function.
520 qtest_results.functionType = TestResult.CleanupFunc
521 qtest_runInternal("cleanup")
522 qtest_results.functionType = TestResult.NoWhere
523 } while (!qtest_results.measurementAccepted())
524 qtest_results.endDataRun()
525 } while (qtest_results.needsMoreMeasurements())
528 function qtest_run() {
529 if (util.printAvailableFunctions) {
534 if (TestLogger.log_start_test()) {
535 qtest_results.reset()
536 qtest_results.testCaseName = name
537 qtest_results.startLogging()
539 qtest_results.testCaseName = name
543 // Check the run list to see if this class is mentioned.
544 var functionsToRun = qtest_results.functionsToRun
545 if (functionsToRun.length > 0) {
548 if (name.length > 0) {
549 var prefix = name + "::"
550 for (var index in functionsToRun) {
551 if (functionsToRun[index].indexOf(prefix) == 0) {
552 list.push(functionsToRun[index])
559 if (!TestLogger.log_complete_test(qtest_testId)) {
560 qtest_results.stopLogging()
563 qtest_results.testCaseName = ""
566 functionsToRun = list
569 // Run the initTestCase function.
570 qtest_results.functionName = "initTestCase"
571 qtest_results.functionType = TestResult.InitFunc
573 if (!qtest_runInternal("initTestCase"))
575 qtest_results.finishTestFunction()
577 // Run the test methods.
580 for (var prop in testCase) {
581 if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
583 var tail = prop.lastIndexOf("_data");
584 if (tail != -1 && tail == (prop.length - 5))
590 var checkNames = (functionsToRun.length > 0)
591 for (var index in testList) {
592 var prop = testList[index]
593 var datafunc = prop + "_data"
594 var isBenchmark = (prop.indexOf("benchmark_") == 0)
596 var index = functionsToRun.indexOf(name + "::" + prop)
599 functionsToRun.splice(index, 1)
601 qtest_results.functionName = prop
602 if (datafunc in testCase) {
603 qtest_results.functionType = TestResult.DataFunc
604 if (qtest_runInternal(datafunc)) {
605 var table = qtest_testCaseResult
607 qtest_results.initTestTable()
608 for (var index in table) {
610 var row = table[index]
612 row.tag = "row " + index // Must have something
613 qtest_results.dataTag = row.tag
615 qtest_runBenchmarkFunction(prop, row)
617 qtest_runFunction(prop, row)
618 qtest_results.dataTag = ""
621 qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()"
622 , util.callerFile(), util.callerLine());
623 qtest_results.clearTestTable()
625 } else if (isBenchmark) {
626 qtest_runBenchmarkFunction(prop, null, isBenchmark)
628 qtest_runFunction(prop, null, isBenchmark)
630 qtest_results.finishTestFunction()
631 qtest_results.skipped = false
634 // Run the cleanupTestCase function.
635 qtest_results.skipped = false
636 qtest_results.functionName = "cleanupTestCase"
637 qtest_results.functionType = TestResult.CleanupFunc
638 qtest_runInternal("cleanupTestCase")
640 // Complain about missing functions that we were supposed to run.
641 if (functionsToRun.length > 0)
642 qtest_results.fail("Could not find functions: " + functionsToRun, "", 0)
644 // Clean up and exit.
647 qtest_results.finishTestFunction()
648 qtest_results.functionName = ""
650 // Stop if there are no more tests to be run.
651 if (!TestLogger.log_complete_test(qtest_testId)) {
652 qtest_results.stopLogging()
655 qtest_results.testCaseName = ""
659 if (when != qtest_prevWhen) {
660 qtest_prevWhen = when
661 if (when && !completed && !running && qtest_componentCompleted)
669 TestLogger.log_optional_test(qtest_testId)
671 TestLogger.log_mandatory_test(qtest_testId)
675 // The test framework will set util.windowShown when the
676 // window is actually shown. If we are running with qmlviewer,
677 // then this won't happen. So we use a timer instead.
679 id: qtest_windowShowTimer
682 onTriggered: { windowShown = true }
685 Component.onCompleted: {
686 qtest.hasTestCase = true;
687 qtest_componentCompleted = true;
689 if (util.printAvailableFunctions) {
691 for (var prop in testCase) {
692 if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
694 var tail = prop.lastIndexOf("_data");
695 if (tail != -1 && tail == (prop.length - 5))
697 // Note: cannot run functions in TestCase elements
700 testList.push(name + "::" + prop + "()")
703 for (var index in testList)
704 console.log(testList[index])
707 qtest_testId = TestLogger.log_register_test(name)
709 TestLogger.log_optional_test(qtest_testId)
710 qtest_prevWhen = when
711 var isQmlViewer = util.wrapper ? false : true
713 qtest_windowShowTimer.running = true
714 if (when && !completed && !running)