c44aaca45c6ddf1060dd36845abfdcf16b5eb8be
[profile/ivi/qtdeclarative.git] / src / imports / testlib / TestCase.qml
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 import QtQuick 2.0
43 import QtTest 1.0
44 import "testlogger.js" as TestLogger
45
46 Item {
47     id: testCase
48     visible: false
49     TestUtil {
50         id:util
51     }
52
53     // Name of the test case to prefix the function name in messages.
54     property string name
55
56     // Set to true to start the test running.
57     property bool when: true
58
59     // Set to true once the test has completed.
60     property bool completed: false
61
62     // Set to true when the test is running but not yet complete.
63     property bool running: false
64
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
68
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
73
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 }
83
84     function fail(msg) {
85         if (msg === undefined)
86             msg = "";
87         qtest_results.fail(msg, util.callerFile(), util.callerLine())
88         throw new Error("QtQuickTest::fail")
89     }
90
91     function qtest_fail(msg, frame) {
92         if (msg === undefined)
93             msg = "";
94         qtest_results.fail(msg, util.callerFile(frame), util.callerLine(frame))
95         throw new Error("QtQuickTest::fail")
96     }
97
98     function verify(cond, msg) {
99         if (msg === undefined)
100             msg = "";
101         if (!qtest_results.verify(cond, msg, util.callerFile(), util.callerLine()))
102             throw new Error("QtQuickTest::fail")
103     }
104
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") {
111             return "undefined";
112
113         // consider: typeof null === object
114         } else if (o === null) {
115             return "null";
116
117         } else if (o.constructor === String) {
118             return "string";
119
120         } else if (o.constructor === Boolean) {
121             return "boolean";
122
123         } else if (o.constructor === Number) {
124
125             if (isNaN(o)) {
126                 return "nan";
127             } else {
128                 return "number";
129             }
130         // consider: typeof [] === object
131         } else if (o instanceof Array) {
132             return "array";
133
134         // consider: typeof new Date() === object
135         } else if (o instanceof Date) {
136             return "date";
137
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) {
143             return "regexp";
144
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
150             }
151             return "object";
152         } else if (o instanceof Function) {
153             return "function";
154         } else {
155             return undefined;
156         }
157     }
158
159     // Test for equality
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) {
165         var success = false;
166         if (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
170         } else {
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)
178                 }
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
183                     // e.g. var i = 1;
184                     //      var j = new Number(1);
185                     success = (act == exp)
186                 } else {
187                     success = (act === exp)
188                 }
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) {
194                     success = true
195                 }
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)
213             }
214         }
215         return success
216     }
217
218     function qtest_compareInternalObjects(act, exp) {
219         var i;
220         var eq = true; // unless we can proove it
221         var aProperties = [], bProperties = []; // collection of strings
222
223         // comparing constructors is more strict than using instanceof
224         if (act.constructor !== exp.constructor) {
225             return false;
226         }
227
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])) {
231                 eq = false;
232                 break;
233             }
234         }
235
236         for (i in exp) {
237             bProperties.push(i); // collect exp's properties
238         }
239
240         // Ensures identical properties name
241         return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort());
242
243     }
244
245     function qtest_compareInternalArrays(actual, expected) {
246         if (actual.length != expected.length) {
247             return false
248         }
249
250         for (var i = 0, len = actual.length; i < len; i++) {
251             if (!qtest_compareInternal(actual[i], expected[i])) {
252                 return false
253             }
254         }
255
256         return true
257     }
258
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 + ")"
264             }
265             try {
266                 return JSON.stringify(value)
267             } catch (ex) {
268                 // stringify might fail (e.g. due to circular references)
269             }
270         }
271         return value
272     }
273
274     function compare(actual, expected, msg) {
275         var act = testCase.qtest_formatValue(actual)
276         var exp = testCase.qtest_formatValue(expected)
277
278         var success = qtest_compareInternal(actual, expected)
279         if (msg === undefined) {
280             if (success)
281                 msg = "COMPARE()"
282             else
283                 msg = "Compared values are not the same"
284         }
285         if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine())) {
286             throw new Error("QtQuickTest::fail")
287         }
288     }
289
290     function tryCompare(obj, prop, value, timeout) {
291         if (!timeout)
292             timeout = 5000
293         if (!qtest_compareInternal(obj[prop], value))
294             wait(0)
295         var i = 0
296         while (i < timeout && !qtest_compareInternal(obj[prop], value)) {
297             wait(50)
298             i += 50
299         }
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")
306     }
307
308     function skip(msg) {
309         if (msg === undefined)
310             msg = ""
311         qtest_results.skip(msg, util.callerFile(), util.callerLine())
312         throw new Error("QtQuickTest::skip")
313     }
314
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")
319     }
320
321     function expectFail(tag, msg) {
322         if (tag === undefined) {
323             warn("tag argument missing from expectFail()")
324             tag = ""
325         }
326         if (msg === undefined) {
327             warn("message argument missing from expectFail()")
328             msg = ""
329         }
330         if (!qtest_results.expectFail(tag, msg, util.callerFile(), util.callerLine()))
331             throw new Error("QtQuickTest::expectFail")
332     }
333
334     function expectFailContinue(tag, msg) {
335         if (tag === undefined) {
336             warn("tag argument missing from expectFailContinue()")
337             tag = ""
338         }
339         if (msg === undefined) {
340             warn("message argument missing from expectFailContinue()")
341             msg = ""
342         }
343         if (!qtest_results.expectFailContinue(tag, msg, util.callerFile(), util.callerLine()))
344             throw new Error("QtQuickTest::expectFail")
345     }
346
347     function warn(msg) {
348         if (msg === undefined)
349             msg = ""
350         qtest_results.warn(msg, util.callerFile(), util.callerLine());
351     }
352
353     function ignoreWarning(msg) {
354         if (msg === undefined)
355             msg = ""
356         qtest_results.ignoreWarning(msg)
357     }
358
359     function wait(ms) {
360         qtest_results.wait(ms)
361     }
362
363     function sleep(ms) {
364         qtest_results.sleep(ms)
365     }
366
367     function keyPress(key, modifiers, delay) {
368         if (modifiers === undefined)
369             modifiers = Qt.NoModifier
370         if (delay == undefined)
371             delay = -1
372         if (!qtest_events.keyPress(key, modifiers, delay))
373             qtest_fail("window not shown", 2)
374     }
375
376     function keyRelease(key, modifiers, delay) {
377         if (modifiers === undefined)
378             modifiers = Qt.NoModifier
379         if (delay == undefined)
380             delay = -1
381         if (!qtest_events.keyRelease(key, modifiers, delay))
382             qtest_fail("window not shown", 2)
383     }
384
385     function keyClick(key, modifiers, delay) {
386         if (modifiers === undefined)
387             modifiers = Qt.NoModifier
388         if (delay == undefined)
389             delay = -1
390         if (!qtest_events.keyClick(key, modifiers, delay))
391             qtest_fail("window not shown", 2)
392     }
393
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)
400             delay = -1
401         if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
402             qtest_fail("window not shown", 2)
403     }
404
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)
411             delay = -1
412         if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
413             qtest_fail("window not shown", 2)
414     }
415
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)
422             delay = -1
423         if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
424             qtest_fail("window not shown", 2)
425     }
426
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)
433             delay = -1
434         if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
435             qtest_fail("window not shown", 2)
436     }
437
438     function mouseMove(item, x, y, delay, buttons) {
439         if (delay == undefined)
440             delay = -1
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)
445     }
446
447     function mouseWheel(item, x, y, delta, buttons, modifiers, delay, orientation) {
448         if (delay == undefined)
449             delay = -1
450         if (buttons == undefined)
451             buttons = Qt.NoButton
452         if (modifiers === undefined)
453             modifiers = Qt.NoModifier
454         if (delta == undefined)
455             delta = 0
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)
460    }
461
462
463     // Functions that can be overridden in subclasses for init/cleanup duties.
464     function initTestCase() {}
465     function cleanupTestCase() {}
466     function init() {}
467     function cleanup() {}
468
469     function qtest_runInternal(prop, arg) {
470         try {
471             qtest_testCaseResult = testCase[prop](arg)
472         } catch (e) {
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)
478             }
479         }
480         return !qtest_results.dataFailed
481     }
482
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")
491         }
492         qtest_results.functionType = TestResult.NoWhere
493     }
494
495     function qtest_runBenchmarkFunction(prop, arg) {
496         qtest_results.startMeasurement()
497         do {
498             qtest_results.beginDataRun()
499             do {
500                 // Run the initialization function.
501                 qtest_results.functionType = TestResult.InitFunc
502                 qtest_runInternal("init")
503                 if (qtest_results.skipped)
504                     break
505
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)
510                 else
511                     qtest_results.startBenchmark(TestResult.RunOnce, qtest_results.dataTag)
512                 while (!qtest_results.isBenchmarkDone()) {
513                     if (!qtest_runInternal(prop, arg))
514                         break
515                     qtest_results.nextBenchmark()
516                 }
517                 qtest_results.stopBenchmark()
518
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())
526     }
527
528     function qtest_run() {
529         if (util.printAvailableFunctions) {
530             completed = true
531             return
532         }
533
534         if (TestLogger.log_start_test()) {
535             qtest_results.reset()
536             qtest_results.testCaseName = name
537             qtest_results.startLogging()
538         } else {
539             qtest_results.testCaseName = name
540         }
541         running = true
542
543         // Check the run list to see if this class is mentioned.
544         var functionsToRun = qtest_results.functionsToRun
545         if (functionsToRun.length > 0) {
546             var found = false
547             var list = []
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])
553                         found = true
554                     }
555                 }
556             }
557             if (!found) {
558                 completed = true
559                 if (!TestLogger.log_complete_test(qtest_testId)) {
560                     qtest_results.stopLogging()
561                     Qt.quit()
562                 }
563                 qtest_results.testCaseName = ""
564                 return
565             }
566             functionsToRun = list
567         }
568
569         // Run the initTestCase function.
570         qtest_results.functionName = "initTestCase"
571         qtest_results.functionType = TestResult.InitFunc
572         var runTests = true
573         if (!qtest_runInternal("initTestCase"))
574             runTests = false
575         qtest_results.finishTestFunction()
576
577         // Run the test methods.
578         var testList = []
579         if (runTests) {
580             for (var prop in testCase) {
581                 if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
582                     continue
583                 var tail = prop.lastIndexOf("_data");
584                 if (tail != -1 && tail == (prop.length - 5))
585                     continue
586                 testList.push(prop)
587             }
588             testList.sort()
589         }
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)
595             if (checkNames) {
596                 var index = functionsToRun.indexOf(name + "::" + prop)
597                 if (index < 0)
598                     continue
599                 functionsToRun.splice(index, 1)
600             }
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
606                     var haveData = false
607                     qtest_results.initTestTable()
608                     for (var index in table) {
609                         haveData = true
610                         var row = table[index]
611                         if (!row.tag)
612                             row.tag = "row " + index    // Must have something
613                         qtest_results.dataTag = row.tag
614                         if (isBenchmark)
615                             qtest_runBenchmarkFunction(prop, row)
616                         else
617                             qtest_runFunction(prop, row)
618                         qtest_results.dataTag = ""
619                     }
620                     if (!haveData)
621                         qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()"
622                                            , util.callerFile(), util.callerLine());
623                     qtest_results.clearTestTable()
624                 }
625             } else if (isBenchmark) {
626                 qtest_runBenchmarkFunction(prop, null, isBenchmark)
627             } else {
628                 qtest_runFunction(prop, null, isBenchmark)
629             }
630             qtest_results.finishTestFunction()
631             qtest_results.skipped = false
632         }
633
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")
639
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)
643
644         // Clean up and exit.
645         running = false
646         completed = true
647         qtest_results.finishTestFunction()
648         qtest_results.functionName = ""
649
650         // Stop if there are no more tests to be run.
651         if (!TestLogger.log_complete_test(qtest_testId)) {
652             qtest_results.stopLogging()
653             Qt.quit()
654         }
655         qtest_results.testCaseName = ""
656     }
657
658     onWhenChanged: {
659         if (when != qtest_prevWhen) {
660             qtest_prevWhen = when
661             if (when && !completed && !running && qtest_componentCompleted)
662                 qtest_run()
663         }
664     }
665
666     onOptionalChanged: {
667         if (!completed) {
668             if (optional)
669                 TestLogger.log_optional_test(qtest_testId)
670             else
671                 TestLogger.log_mandatory_test(qtest_testId)
672         }
673     }
674
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.
678     Timer {
679         id: qtest_windowShowTimer
680         interval: 100
681         repeat: false
682         onTriggered: { windowShown = true }
683     }
684
685     Component.onCompleted: {
686         qtest.hasTestCase = true;
687         qtest_componentCompleted = true;
688
689         if (util.printAvailableFunctions) {
690             var testList = []
691             for (var prop in testCase) {
692                 if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
693                     continue
694                 var tail = prop.lastIndexOf("_data");
695                 if (tail != -1 && tail == (prop.length - 5))
696                     continue
697                 // Note: cannot run functions in TestCase elements
698                 // that lack a name.
699                 if (name.length > 0)
700                     testList.push(name + "::" + prop + "()")
701             }
702             testList.sort()
703             for (var index in testList)
704                 console.log(testList[index])
705             return
706         }
707         qtest_testId = TestLogger.log_register_test(name)
708         if (optional)
709             TestLogger.log_optional_test(qtest_testId)
710         qtest_prevWhen = when
711         var isQmlViewer = util.wrapper ? false : true
712         if (isQmlViewer)
713             qtest_windowShowTimer.running = true
714         if (when && !completed && !running)
715             qtest_run()
716     }
717 }