1 /****************************************************************************
3 ** Copyright (C) 2010 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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
44 import "testlogger.js" as TestLogger
50 // Name of the test case to prefix the function name in messages.
53 // Set to true to start the test running.
54 property bool when: true
56 // Set to true once the test has completed.
57 property bool completed: false
59 // Set to true when the test is running but not yet complete.
60 property bool running: false
62 // Set to true if the test doesn't have to run (because some
63 // other test failed which this one depends on).
64 property bool optional: false
66 // Property that is set to true when the main window is shown.
67 // We need to set the property value in an odd way to handle
68 // both qmlviewer and the QtQuickTest module test wrapper.
69 property bool windowShown: Qt.qtest_wrapper ? qtest.windowShown : false
71 // Internal private state. Identifiers prefixed with qtest are reserved.
72 property bool qtest_prevWhen: true
73 property int qtest_testId: -1
74 property variant qtest_testCaseResult
75 property variant qtest_results: qtest_results_normal
76 TestResult { id: qtest_results_normal }
77 property variant qtest_events: qtest_events_normal
78 TestEvent { id: qtest_events_normal }
81 if (msg === undefined)
83 qtest_results.fail(msg, Qt.qtest_caller_file(), Qt.qtest_caller_line())
84 throw new Error("QtQuickTest::fail")
87 function qtest_fail(msg, frame) {
88 if (msg === undefined)
90 qtest_results.fail(msg, Qt.qtest_caller_file(frame), Qt.qtest_caller_line(frame))
91 throw new Error("QtQuickTest::fail")
94 function verify(cond, msg) {
95 if (msg === undefined)
97 if (!qtest_results.verify(cond, msg, Qt.qtest_caller_file(), Qt.qtest_caller_line()))
98 throw new Error("QtQuickTest::fail")
101 // Determine what is o.
102 // Discussions and reference: http://philrathe.com/articles/equiv
103 // Test suites: http://philrathe.com/tests/equiv
104 // Author: Philippe Rathé <prathe@gmail.com>
105 function qtest_typeof(o) {
106 if (typeof o === "undefined") {
109 // consider: typeof null === object
110 } else if (o === null) {
113 } else if (o.constructor === String) {
116 } else if (o.constructor === Boolean) {
119 } else if (o.constructor === Number) {
126 // consider: typeof [] === object
127 } else if (o instanceof Array) {
130 // consider: typeof new Date() === object
131 } else if (o instanceof Date) {
134 // consider: /./ instanceof Object;
135 // /./ instanceof RegExp;
136 // typeof /./ === "function"; // => false in IE and Opera,
137 // true in FF and Safari
138 } else if (o instanceof RegExp) {
141 } else if (typeof o === "object") {
142 if ("mapFromItem" in o && "mapToItem" in o) {
143 return "declarativeitem"; // @todo improve detection of declarative items
144 } else if ("x" in o && "y" in o && "z" in o) {
145 return "vector3d"; // Qt3D vector
148 } else if (o instanceof Function) {
156 // Large parts contain sources from QUnit or http://philrathe.com
157 // Discussions and reference: http://philrathe.com/articles/equiv
158 // Test suites: http://philrathe.com/tests/equiv
159 // Author: Philippe Rathé <prathe@gmail.com>
160 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)
170 if (typeExp !== typeAct) {
171 // allow object vs string comparison (e.g. for colors)
172 // else break on different types
173 if ((typeExp === "string" && typeAct === "object") || (typeExp === "object" && typeAct === "string")) {
174 success = (act == exp)
176 } else if (typeExp === "string" || typeExp === "boolean" || typeExp === "number" ||
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
228 if (!qtest_compareInternal(act[i], exp[i])) {
235 bProperties.push(i); // collect exp's properties
238 // Ensures identical properties name
239 return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort());
243 function qtest_compareInternalArrays(actual, expected) {
244 if (actual.length != expected.length) {
248 for (var i = 0, len = actual.length; i < len; i++) {
249 if (!qtest_compareInternal(actual[i], expected[i])) {
257 function qtest_formatValue(value) {
258 if (typeof value == "object") {
259 if ("x" in value && "y" in value && "z" in value) {
260 return "Qt.vector3d(" + value.x + ", " +
261 value.y + ", " + value.z + ")"
264 return JSON.stringify(value)
266 // stringify might fail (e.g. due to circular references)
272 function compare(actual, expected, msg) {
273 var act = qtest_formatValue(actual)
274 var exp = qtest_formatValue(expected)
275 var success = qtest_compareInternal(actual, expected)
276 if (msg === undefined) {
280 msg = "Compared values are not the same"
282 if (!qtest_results.compare(success, msg, act, exp, Qt.qtest_caller_file(), Qt.qtest_caller_line()))
283 throw new Error("QtQuickTest::fail")
286 function tryCompare(obj, prop, value, timeout) {
289 if (!qtest_compareInternal(obj[prop], value))
292 while (i < timeout && !qtest_compareInternal(obj[prop], value)) {
296 var actual = obj[prop]
297 var act = qtest_formatValue(actual)
298 var exp = qtest_formatValue(value)
299 var success = qtest_compareInternal(actual, value)
300 if (!qtest_results.compare(success, "property " + prop, act, exp, Qt.qtest_caller_file(), Qt.qtest_caller_line()))
301 throw new Error("QtQuickTest::fail")
305 if (msg === undefined)
307 qtest_results.skipSingle(msg, Qt.qtest_caller_file(), Qt.qtest_caller_line())
308 throw new Error("QtQuickTest::skip")
311 function skipAll(msg) {
312 if (msg === undefined)
314 qtest_results.skipAll(msg, Qt.qtest_caller_file(), Qt.qtest_caller_line())
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, Qt.qtest_caller_file(), Qt.qtest_caller_line()))
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, Qt.qtest_caller_file(), Qt.qtest_caller_line()))
341 throw new Error("QtQuickTest::expectFail")
345 if (msg === undefined)
347 qtest_results.warn(msg);
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 mouseClick(item, x, y, button, modifiers, delay) {
414 if (button === undefined)
415 button = Qt.LeftButton
416 if (modifiers === undefined)
417 modifiers = Qt.NoModifier
418 if (delay == undefined)
420 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
421 qtest_fail("window not shown", 2)
424 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
425 if (button === undefined)
426 button = Qt.LeftButton
427 if (modifiers === undefined)
428 modifiers = Qt.NoModifier
429 if (delay == undefined)
431 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
432 qtest_fail("window not shown", 2)
435 function mouseMove(item, x, y, delay) {
436 if (delay == undefined)
438 if (!qtest_events.mouseMove(item, x, y, delay))
439 qtest_fail("window not shown", 2)
442 // Functions that can be overridden in subclasses for init/cleanup duties.
443 function initTestCase() {}
444 function cleanupTestCase() {}
446 function cleanup() {}
448 function qtest_runInternal(prop, arg) {
450 qtest_testCaseResult = testCase[prop](arg)
452 qtest_testCaseResult = []
453 if (e.message.indexOf("QtQuickTest::") != 0) {
454 // Test threw an unrecognized exception - fail.
455 qtest_results.fail("Uncaught exception: " + e.message,
456 e.fileName, e.lineNumber)
459 return !qtest_results.dataFailed
462 function qtest_runFunction(prop, arg) {
463 qtest_results.functionType = TestResult.InitFunc
464 qtest_runInternal("init")
465 if (!qtest_results.skipped) {
466 qtest_results.functionType = TestResult.Func
467 qtest_runInternal(prop, arg)
468 qtest_results.functionType = TestResult.CleanupFunc
469 qtest_runInternal("cleanup")
471 qtest_results.functionType = TestResult.NoWhere
474 function qtest_runBenchmarkFunction(prop, arg) {
475 qtest_results.startMeasurement()
477 qtest_results.beginDataRun()
479 // Run the initialization function.
480 qtest_results.functionType = TestResult.InitFunc
481 qtest_runInternal("init")
482 if (qtest_results.skipped)
485 // Execute the benchmark function.
486 qtest_results.functionType = TestResult.Func
487 if (prop.indexOf("benchmark_once_") != 0)
488 qtest_results.startBenchmark(TestResult.RepeatUntilValidMeasurement, qtest_results.dataTag)
490 qtest_results.startBenchmark(TestResult.RunOnce, qtest_results.dataTag)
491 while (!qtest_results.isBenchmarkDone()) {
492 if (!qtest_runInternal(prop, arg))
494 qtest_results.nextBenchmark()
496 qtest_results.stopBenchmark()
498 // Run the cleanup function.
499 qtest_results.functionType = TestResult.CleanupFunc
500 qtest_runInternal("cleanup")
501 qtest_results.functionType = TestResult.NoWhere
502 } while (!qtest_results.measurementAccepted())
503 qtest_results.endDataRun()
504 } while (qtest_results.needsMoreMeasurements())
507 function qtest_run() {
508 if (Qt.qtest_printAvailableFunctions) {
513 if (TestLogger.log_start_test()) {
514 qtest_results.reset()
515 qtest_results.testCaseName = name
516 qtest_results.startLogging()
518 qtest_results.testCaseName = name
522 // Check the run list to see if this class is mentioned.
523 var functionsToRun = qtest_results.functionsToRun
524 if (functionsToRun.length > 0) {
527 if (name.length > 0) {
528 var prefix = name + "::"
529 for (var index in functionsToRun) {
530 if (functionsToRun[index].indexOf(prefix) == 0) {
531 list.push(functionsToRun[index])
538 if (!TestLogger.log_complete_test(qtest_testId)) {
539 qtest_results.stopLogging()
542 qtest_results.testCaseName = ""
545 functionsToRun = list
548 // Run the initTestCase function.
549 qtest_results.functionName = "initTestCase"
550 qtest_results.functionType = TestResult.InitFunc
552 if (!qtest_runInternal("initTestCase"))
554 qtest_results.finishTestFunction()
556 // Run the test methods.
559 for (var prop in testCase) {
560 if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
562 var tail = prop.lastIndexOf("_data");
563 if (tail != -1 && tail == (prop.length - 5))
569 var checkNames = (functionsToRun.length > 0)
570 for (var index in testList) {
571 var prop = testList[index]
572 var datafunc = prop + "_data"
573 var isBenchmark = (prop.indexOf("benchmark_") == 0)
575 var index = functionsToRun.indexOf(name + "::" + prop)
578 functionsToRun.splice(index, 1)
580 qtest_results.functionName = prop
581 if (datafunc in testCase) {
582 qtest_results.functionType = TestResult.DataFunc
583 if (qtest_runInternal(datafunc)) {
584 var table = qtest_testCaseResult
586 qtest_results.initTestTable()
587 for (var index in table) {
589 var row = table[index]
591 row.tag = "row " + index // Must have something
592 qtest_results.dataTag = row.tag
594 qtest_runBenchmarkFunction(prop, row)
596 qtest_runFunction(prop, row)
597 qtest_results.dataTag = ""
600 qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()")
601 qtest_results.clearTestTable()
603 } else if (isBenchmark) {
604 qtest_runBenchmarkFunction(prop, null, isBenchmark)
606 qtest_runFunction(prop, null, isBenchmark)
608 qtest_results.finishTestFunction()
609 qtest_results.skipped = false
612 // Run the cleanupTestCase function.
613 qtest_results.skipped = false
614 qtest_results.functionName = "cleanupTestCase"
615 qtest_results.functionType = TestResult.CleanupFunc
616 qtest_runInternal("cleanupTestCase")
618 // Complain about missing functions that we were supposed to run.
619 if (functionsToRun.length > 0)
620 qtest_results.fail("Could not find functions: " + functionsToRun, "", 0)
622 // Clean up and exit.
625 qtest_results.finishTestFunction()
626 qtest_results.functionName = ""
628 // Stop if there are no more tests to be run.
629 if (!TestLogger.log_complete_test(qtest_testId)) {
630 qtest_results.stopLogging()
633 qtest_results.testCaseName = ""
637 if (when != qtest_prevWhen) {
638 qtest_prevWhen = when
639 if (when && !completed && !running)
647 TestLogger.log_optional_test(qtest_testId)
649 TestLogger.log_mandatory_test(qtest_testId)
653 // The test framework will set qtest.windowShown when the
654 // window is actually shown. If we are running with qmlviewer,
655 // then this won't happen. So we use a timer instead.
657 id: qtest_windowShowTimer
660 onTriggered: { windowShown = true }
663 Component.onCompleted: {
664 if (Qt.qtest_printAvailableFunctions) {
666 for (var prop in testCase) {
667 if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
669 var tail = prop.lastIndexOf("_data");
670 if (tail != -1 && tail == (prop.length - 5))
672 // Note: cannot run functions in TestCase elements
675 testList.push(name + "::" + prop + "()")
678 for (var index in testList)
679 console.log(testList[index])
682 qtest_testId = TestLogger.log_register_test(name)
684 TestLogger.log_optional_test(qtest_testId)
685 qtest_prevWhen = when
686 var isQmlViewer = Qt.qtest_wrapper ? false : true
688 qtest_windowShowTimer.running = true
689 if (when && !completed && !running)