diff options
Diffstat (limited to 'src/imports/testlib/TestCase.qml')
-rw-r--r-- | src/imports/testlib/TestCase.qml | 2016 |
1 files changed, 0 insertions, 2016 deletions
diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml deleted file mode 100644 index 1c9011e01e..0000000000 --- a/src/imports/testlib/TestCase.qml +++ /dev/null @@ -1,2016 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtQuick.Window 2.0 // used for qtest_verifyItem -import QtTest 1.2 -import "testlogger.js" as TestLogger - -/*! - \qmltype TestCase - \inqmlmodule QtTest - \brief Represents a unit test case. - \since 4.8 - \ingroup qtquicktest - - \section1 Introduction to QML Test Cases - - Test cases are written as JavaScript functions within a TestCase - type: - - \code - import QtQuick 2.0 - import QtTest 1.2 - - TestCase { - name: "MathTests" - - function test_math() { - compare(2 + 2, 4, "2 + 2 = 4") - } - - function test_fail() { - compare(2 + 2, 5, "2 + 2 = 5") - } - } - \endcode - - Functions whose names start with "test_" are treated as test cases - to be executed. The \l name property is used to prefix the functions - in the output: - - \code - ********* Start testing of MathTests ********* - Config: Using QTest library 4.7.2, Qt 4.7.2 - PASS : MathTests::initTestCase() - FAIL! : MathTests::test_fail() 2 + 2 = 5 - Actual (): 4 - Expected (): 5 - Loc: [/home/.../tst_math.qml(12)] - PASS : MathTests::test_math() - PASS : MathTests::cleanupTestCase() - Totals: 3 passed, 1 failed, 0 skipped - ********* Finished testing of MathTests ********* - \endcode - - Because of the way JavaScript properties work, the order in which the - test functions are found is unpredictable. To assist with predictability, - the test framework will sort the functions on ascending order of name. - This can help when there are two tests that must be run in order. - - Multiple TestCase types can be supplied. The test program will exit - once they have all completed. If a test case doesn't need to run - (because a precondition has failed), then \l optional can be set to true. - - \section1 Data-driven Tests - - Table data can be provided to a test using a function name that ends - with "_data". Alternatively, the \c init_data() function can be used - to provide default test data for all test functions in a TestCase type: - - - \code - import QtQuick 2.0 - import QtTest 1.2 - - TestCase { - name: "DataTests" - - function init_data() { - return [ - {tag:"init_data_1", a:1, b:2, answer: 3}, - {tag:"init_data_2", a:2, b:4, answer: 6} - ]; - } - - function test_table_data() { - return [ - {tag: "2 + 2 = 4", a: 2, b: 2, answer: 4 }, - {tag: "2 + 6 = 8", a: 2, b: 6, answer: 8 }, - ] - } - - function test_table(data) { - //data comes from test_table_data - compare(data.a + data.b, data.answer) - } - - function test__default_table(data) { - //data comes from init_data - compare(data.a + data.b, data.answer) - } - } - \endcode - - The test framework will iterate over all of the rows in the table - and pass each row to the test function. As shown, the columns can be - extracted for use in the test. The \c tag column is special - it is - printed by the test framework when a row fails, to help the reader - identify which case failed amongst a set of otherwise passing tests. - - \section1 Benchmarks - - Functions whose names start with "benchmark_" will be run multiple - times with the Qt benchmark framework, with an average timing value - reported for the runs. This is equivalent to using the \c{QBENCHMARK} - macro in the C++ version of QTestLib. - - \code - TestCase { - id: top - name: "CreateBenchmark" - - function benchmark_create_component() { - var component = Qt.createComponent("item.qml") - var obj = component.createObject(top) - obj.destroy() - component.destroy() - } - } - - RESULT : CreateBenchmark::benchmark_create_component: - 0.23 msecs per iteration (total: 60, iterations: 256) - PASS : CreateBenchmark::benchmark_create_component() - \endcode - - To get the effect of the \c{QBENCHMARK_ONCE} macro, prefix the test - function name with "benchmark_once_". - - \section1 Simulating Keyboard and Mouse Events - - The keyPress(), keyRelease(), and keyClick() methods can be used - to simulate keyboard events within unit tests. The events are - delivered to the currently focused QML item. You can pass either - a Qt.Key enum value or a latin1 char (string of length one) - - \code - Rectangle { - width: 50; height: 50 - focus: true - - TestCase { - name: "KeyClick" - when: windowShown - - function test_key_click() { - keyClick(Qt.Key_Left) - keyClick("a") - ... - } - } - } - \endcode - - The mousePress(), mouseRelease(), mouseClick(), mouseDoubleClickSequence() - and mouseMove() methods can be used to simulate mouse events in a - similar fashion. - - \b{Note:} keyboard and mouse events can only be delivered once the - main window has been shown. Attempts to deliver events before then - will fail. Use the \l when and windowShown properties to track - when the main window has been shown. - - \section1 Managing Dynamically Created Test Objects - - A typical pattern with QML tests is to - \l {Dynamic QML Object Creation from JavaScript}{dynamically create} - an item and then destroy it at the end of the test function: - - \code - TestCase { - id: testCase - name: "MyTest" - when: windowShown - - function test_click() { - var item = Qt.createQmlObject("import QtQuick 2.0; Item {}", testCase); - verify(item); - - // Test item... - - item.destroy(); - } - } - \endcode - - The problem with this pattern is that any failures in the test function - will cause the call to \c item.destroy() to be skipped, leaving the item - hanging around in the scene until the test case has finished. This can - result in interference with future tests; for example, by blocking input - events or producing unrelated debug output that makes it difficult to - follow the code's execution. - - By calling \l createTemporaryQmlObject() instead, the object is guaranteed - to be destroyed at the end of the test function: - - \code - TestCase { - id: testCase - name: "MyTest" - when: windowShown - - function test_click() { - var item = createTemporaryQmlObject("import QtQuick 2.0; Item {}", testCase); - verify(item); - - // Test item... - - // Don't need to worry about destroying "item" here. - } - } - \endcode - - For objects that are created via the \l {Component::}{createObject()} function - of \l Component, the \l createTemporaryObject() function can be used. - - \sa {QtTest::SignalSpy}{SignalSpy}, {Qt Quick Test} -*/ - - -Item { - id: testCase - visible: false - TestUtil { - id:util - } - - /*! - \qmlproperty string TestCase::name - - This property defines the name of the test case for result reporting. - The default value is an empty string. - - \code - TestCase { - name: "ButtonTests" - ... - } - \endcode - */ - property string name - - /*! - \qmlproperty bool TestCase::when - - This property should be set to true when the application wants - the test cases to run. The default value is true. In the following - example, a test is run when the user presses the mouse button: - - \code - Rectangle { - id: foo - width: 640; height: 480 - color: "cyan" - - MouseArea { - id: area - anchors.fill: parent - } - - property bool bar: true - - TestCase { - name: "ItemTests" - when: area.pressed - id: test1 - - function test_bar() { - verify(bar) - } - } - } - \endcode - - The test application will exit once all \l TestCase types - have been triggered and have run. The \l optional property can - be used to exclude a \l TestCase type. - - \sa optional, completed - */ - property bool when: true - - /*! - \qmlproperty bool TestCase::completed - - This property will be set to true once the test case has completed - execution. Test cases are only executed once. The initial value - is false. - - \sa running, when - */ - property bool completed: false - - /*! - \qmlproperty bool TestCase::running - - This property will be set to true while the test case is running. - The initial value is false, and the value will become false again - once the test case completes. - - \sa completed, when - */ - property bool running: false - - /*! - \qmlproperty bool TestCase::optional - - Multiple \l TestCase types can be supplied in a test application. - The application will exit once they have all completed. If a test case - does not need to run (because a precondition has failed), then this - property can be set to true. The default value is false. - - \code - TestCase { - when: false - optional: true - function test_not_run() { - verify(false) - } - } - \endcode - - \sa when, completed - */ - property bool optional: false - - /*! - \qmlproperty bool TestCase::windowShown - - This property will be set to true after the QML viewing window has - been displayed. Normally test cases run as soon as the test application - is loaded and before a window is displayed. If the test case involves - visual types and behaviors, then it may need to be delayed until - after the window is shown. - - \code - Button { - id: button - onClicked: text = "Clicked" - TestCase { - name: "ClickTest" - when: windowShown - function test_click() { - button.clicked(); - compare(button.text, "Clicked"); - } - } - } - \endcode - */ - property bool windowShown: QTestRootObject.windowShown - - // Internal private state. Identifiers prefixed with qtest are reserved. - /*! \internal */ - property bool qtest_prevWhen: true - /*! \internal */ - property int qtest_testId: -1 - /*! \internal */ - property bool qtest_componentCompleted : false - /*! \internal */ - property var qtest_testCaseResult - /*! \internal */ - property var qtest_results: qtest_results_normal - /*! \internal */ - TestResult { id: qtest_results_normal } - /*! \internal */ - property var qtest_events: qtest_events_normal - TestEvent { id: qtest_events_normal } - /*! \internal */ - property var qtest_temporaryObjects: [] - - /*! - \qmlmethod TestCase::fail(message = "") - - Fails the current test case, with the optional \a message. - Similar to \c{QFAIL(message)} in C++. - */ - function fail(msg) { - if (msg === undefined) - msg = ""; - qtest_results.fail(msg, util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - - /*! \internal */ - function qtest_fail(msg, frame) { - if (msg === undefined) - msg = ""; - qtest_results.fail(msg, util.callerFile(frame), util.callerLine(frame)) - throw new Error("QtQuickTest::fail") - } - - /*! - \qmlmethod TestCase::verify(condition, message = "") - - Fails the current test case if \a condition is false, and - displays the optional \a message. Similar to \c{QVERIFY(condition)} - or \c{QVERIFY2(condition, message)} in C++. - */ - function verify(cond, msg) { - if (arguments.length > 2) - qtest_fail("More than two arguments given to verify(). Did you mean tryVerify() or tryCompare()?", 1) - - if (msg === undefined) - msg = ""; - if (!qtest_results.verify(cond, msg, util.callerFile(), util.callerLine())) - throw new Error("QtQuickTest::fail") - } - - /*! - \since 5.8 - \qmlmethod TestCase::tryVerify(function, timeout = 5000, message = "") - - Fails the current test case if \a function does not evaluate to - \c true before the specified \a timeout (in milliseconds) has elapsed. - The function is evaluated multiple times until the timeout is - reached. An optional \a message is displayed upon failure. - - This function is intended for testing applications where a condition - changes based on asynchronous events. Use verify() for testing - synchronous condition changes, and tryCompare() for testing - asynchronous property changes. - - For example, in the code below, it's not possible to use tryCompare(), - because the \c currentItem property might be \c null for a short period - of time: - - \code - tryCompare(listView.currentItem, "text", "Hello"); - \endcode - - Instead, we can use tryVerify() to first check that \c currentItem - isn't \c null, and then use a regular compare afterwards: - - \code - tryVerify(function(){ return listView.currentItem }) - compare(listView.currentItem.text, "Hello") - \endcode - - \sa verify(), compare(), tryCompare(), SignalSpy::wait() - */ - function tryVerify(expressionFunction, timeout, msg) { - if (!expressionFunction || !(expressionFunction instanceof Function)) { - qtest_results.fail("First argument must be a function", util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - - if (timeout && typeof(timeout) !== "number") { - qtest_results.fail("timeout argument must be a number", util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - - if (msg && typeof(msg) !== "string") { - qtest_results.fail("message argument must be a string", util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - - if (!timeout) - timeout = 5000 - - if (msg === undefined) - msg = "function returned false" - - if (!expressionFunction()) - wait(0) - - var i = 0 - while (i < timeout && !expressionFunction()) { - wait(50) - i += 50 - } - - if (!qtest_results.verify(expressionFunction(), msg, util.callerFile(), util.callerLine())) - throw new Error("QtQuickTest::fail") - } - - /*! - \since 5.13 - \qmlmethod bool TestCase::isPolishScheduled(object item) - - Returns \c true if \l {QQuickItem::}{updatePolish()} has not been called - on \a item since the last call to \l {QQuickItem::}{polish()}, - otherwise returns \c false. - - When assigning values to properties in QML, any layouting the item - must do as a result of the assignment might not take effect immediately, - but can instead be postponed until the item is polished. For these cases, - you can use this function to ensure that the item has been polished - before the execution of the test continues. For example: - - \code - verify(isPolishScheduled(item)) - verify(waitForItemPolished(item)) - \endcode - - Without the call to \c isPolishScheduled() above, the - call to \c waitForItemPolished() might see that no polish - was scheduled and therefore pass instantly, assuming that - the item had already been polished. This function - makes it obvious why an item wasn't polished and allows tests to - fail early under such circumstances. - - \sa waitForItemPolished(), QQuickItem::polish(), QQuickItem::updatePolish() - */ - function isPolishScheduled(item) { - if (!item || typeof item !== "object") { - qtest_results.fail("Argument must be a valid Item; actual type is " + typeof item, - util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - - return qtest_results.isPolishScheduled(item) - } - - /*! - \since 5.13 - \qmlmethod bool waitForItemPolished(object item, int timeout = 5000) - - Waits for \a timeout milliseconds or until - \l {QQuickItem::}{updatePolish()} has been called on \a item. - - Returns \c true if \c updatePolish() was called on \a item within - \a timeout milliseconds, otherwise returns \c false. - - \sa isPolishScheduled(), QQuickItem::polish(), QQuickItem::updatePolish() - */ - function waitForItemPolished(item, timeout) { - if (!item || typeof item !== "object") { - qtest_results.fail("First argument must be a valid Item; actual type is " + typeof item, - util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - - if (timeout !== undefined && typeof(timeout) != "number") { - qtest_results.fail("Second argument must be a number; actual type is " + typeof timeout, - util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - - if (!timeout) - timeout = 5000 - - return qtest_results.waitForItemPolished(item, timeout) - } - - /*! - \since 5.9 - \qmlmethod object TestCase::createTemporaryQmlObject(string qml, object parent, string filePath) - - This function dynamically creates a QML object from the given \a qml - string with the specified \a parent. The returned object will be - destroyed (if it was not already) after \l cleanup() has finished - executing, meaning that objects created with this function are - guaranteed to be destroyed after each test, regardless of whether or - not the tests fail. - - If there was an error while creating the object, \c null will be - returned. - - If \a filePath is specified, it will be used for error reporting for - the created object. - - This function calls - \l {QtQml::Qt::createQmlObject()}{Qt.createQmlObject()} internally. - - \sa {Managing Dynamically Created Test Objects} - */ - function createTemporaryQmlObject(qml, parent, filePath) { - if (typeof qml !== "string") { - qtest_results.fail("First argument must be a string of QML; actual type is " + typeof qml, - util.callerFile(), util.callerLine()); - throw new Error("QtQuickTest::fail"); - } - - if (!parent || typeof parent !== "object") { - qtest_results.fail("Second argument must be a valid parent object; actual type is " + typeof parent, - util.callerFile(), util.callerLine()); - throw new Error("QtQuickTest::fail"); - } - - if (filePath !== undefined && typeof filePath !== "string") { - qtest_results.fail("Third argument must be a file path string; actual type is " + typeof filePath, - util.callerFile(), util.callerLine()); - throw new Error("QtQuickTest::fail"); - } - - var object = Qt.createQmlObject(qml, parent, filePath); - qtest_temporaryObjects.push(object); - return object; - } - - /*! - \since 5.9 - \qmlmethod object TestCase::createTemporaryObject(Component component, object parent, object properties) - - This function dynamically creates a QML object from the given - \a component with the specified optional \a parent and \a properties. - The returned object will be destroyed (if it was not already) after - \l cleanup() has finished executing, meaning that objects created with - this function are guaranteed to be destroyed after each test, - regardless of whether or not the tests fail. - - If there was an error while creating the object, \c null will be - returned. - - This function calls - \l {QtQml::Component::createObject()}{component.createObject()} - internally. - - \sa {Managing Dynamically Created Test Objects} - */ - function createTemporaryObject(component, parent, properties) { - if (typeof component !== "object") { - qtest_results.fail("First argument must be a Component; actual type is " + typeof component, - util.callerFile(), util.callerLine()); - throw new Error("QtQuickTest::fail"); - } - - if (properties && typeof properties !== "object") { - qtest_results.fail("Third argument must be an object; actual type is " + typeof properties, - util.callerFile(), util.callerLine()); - throw new Error("QtQuickTest::fail"); - } - - var object = component.createObject(parent, properties ? properties : ({})); - qtest_temporaryObjects.push(object); - return object; - } - - /*! - \internal - - Destroys all temporary objects that still exist. - */ - function qtest_destroyTemporaryObjects() { - for (var i = 0; i < qtest_temporaryObjects.length; ++i) { - var temporaryObject = qtest_temporaryObjects[i]; - // ### the typeof check can be removed when QTBUG-57749 is fixed - if (temporaryObject && typeof temporaryObject.destroy === "function") - temporaryObject.destroy(); - } - qtest_temporaryObjects = []; - } - - /*! \internal */ - // Determine what is o. - // Discussions and reference: http://philrathe.com/articles/equiv - // Test suites: http://philrathe.com/tests/equiv - // Author: Philippe Rathé <prathe@gmail.com> - function qtest_typeof(o) { - if (typeof o === "undefined") { - return "undefined"; - - // consider: typeof null === object - } else if (o === null) { - return "null"; - - } else if (o.constructor === String) { - return "string"; - - } else if (o.constructor === Boolean) { - return "boolean"; - - } else if (o.constructor === Number) { - - if (isNaN(o)) { - return "nan"; - } else { - return "number"; - } - // consider: typeof [] === object - } else if (o instanceof Array) { - return "array"; - - // consider: typeof new Date() === object - } else if (o instanceof Date) { - return "date"; - - // consider: /./ instanceof Object; - // /./ instanceof RegExp; - // typeof /./ === "function"; // => false in IE and Opera, - // true in FF and Safari - } else if (o instanceof RegExp) { - return "regexp"; - - } else if (typeof o === "object") { - if ("mapFromItem" in o && "mapToItem" in o) { - return "declarativeitem"; // @todo improve detection of declarative items - } else if ("x" in o && "y" in o && "z" in o) { - return "vector3d"; // Qt 3D vector - } - return "object"; - } else if (o instanceof Function) { - return "function"; - } else { - return undefined; - } - } - - /*! \internal */ - // Test for equality - // Large parts contain sources from QUnit or http://philrathe.com - // Discussions and reference: http://philrathe.com/articles/equiv - // Test suites: http://philrathe.com/tests/equiv - // Author: Philippe Rathé <prathe@gmail.com> - function qtest_compareInternal(act, exp) { - var success = false; - if (act === exp) { - success = true; // catch the most you can - } else if (act === null || exp === null || typeof act === "undefined" || typeof exp === "undefined") { - success = false; // don't lose time with error prone cases - } else { - var typeExp = qtest_typeof(exp), typeAct = qtest_typeof(act) - if (typeExp !== typeAct) { - // allow object vs string comparison (e.g. for colors) - // else break on different types - if ((typeExp === "string" && (typeAct === "object") || typeAct == "declarativeitem") - || ((typeExp === "object" || typeExp == "declarativeitem") && typeAct === "string")) { - success = (act == exp) - } - } else if (typeExp === "string" || typeExp === "boolean" || - typeExp === "null" || typeExp === "undefined") { - if (exp instanceof act.constructor || act instanceof exp.constructor) { - // to catch short annotaion VS 'new' annotation of act declaration - // e.g. var i = 1; - // var j = new Number(1); - success = (act == exp) - } else { - success = (act === exp) - } - } else if (typeExp === "nan") { - success = isNaN(act); - } else if (typeExp === "number") { - // Use act fuzzy compare if the two values are floats - if (Math.abs(act - exp) <= 0.00001) { - success = true - } - } else if (typeExp === "array") { - success = qtest_compareInternalArrays(act, exp) - } else if (typeExp === "object") { - success = qtest_compareInternalObjects(act, exp) - } else if (typeExp === "declarativeitem") { - success = qtest_compareInternalObjects(act, exp) // @todo improve comparison of declarative items - } else if (typeExp === "vector3d") { - success = (Math.abs(act.x - exp.x) <= 0.00001 && - Math.abs(act.y - exp.y) <= 0.00001 && - Math.abs(act.z - exp.z) <= 0.00001) - } else if (typeExp === "date") { - success = (act.valueOf() === exp.valueOf()) - } else if (typeExp === "regexp") { - success = (act.source === exp.source && // the regex itself - act.global === exp.global && // and its modifers (gmi) ... - act.ignoreCase === exp.ignoreCase && - act.multiline === exp.multiline) - } - } - return success - } - - /*! \internal */ - function qtest_compareInternalObjects(act, exp) { - var i; - var eq = true; // unless we can proove it - var aProperties = [], bProperties = []; // collection of strings - - // comparing constructors is more strict than using instanceof - if (act.constructor !== exp.constructor) { - return false; - } - - for (i in act) { // be strict: don't ensures hasOwnProperty and go deep - aProperties.push(i); // collect act's properties - if (!qtest_compareInternal(act[i], exp[i])) { - eq = false; - break; - } - } - - for (i in exp) { - bProperties.push(i); // collect exp's properties - } - - if (aProperties.length == 0 && bProperties.length == 0) { // at least a special case for QUrl - return eq && (JSON.stringify(act) == JSON.stringify(exp)); - } - - // Ensures identical properties name - return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort()); - - } - - /*! \internal */ - function qtest_compareInternalArrays(actual, expected) { - if (actual.length != expected.length) { - return false - } - - for (var i = 0, len = actual.length; i < len; i++) { - if (!qtest_compareInternal(actual[i], expected[i])) { - return false - } - } - - return true - } - - /*! - \qmlmethod TestCase::compare(actual, expected, message = "") - - Fails the current test case if \a actual is not the same as - \a expected, and displays the optional \a message. Similar - to \c{QCOMPARE(actual, expected)} in C++. - - \sa tryCompare(), fuzzyCompare - */ - function compare(actual, expected, msg) { - var act = qtest_results.stringify(actual) - var exp = qtest_results.stringify(expected) - - var success = qtest_compareInternal(actual, expected) - if (msg === undefined) { - if (success) - msg = "COMPARE()" - else - msg = "Compared values are not the same" - } - if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine())) { - throw new Error("QtQuickTest::fail") - } - } - - /*! - \qmlmethod TestCase::fuzzyCompare(actual, expected, delta, message = "") - - Fails the current test case if the difference betwen \a actual and \a expected - is greater than \a delta, and displays the optional \a message. Similar - to \c{qFuzzyCompare(actual, expected)} in C++ but with a required \a delta value. - - This function can also be used for color comparisons if both the \a actual and - \a expected values can be converted into color values. If any of the differences - for RGBA channel values are greater than \a delta, the test fails. - - \sa tryCompare(), compare() - */ - function fuzzyCompare(actual, expected, delta, msg) { - if (delta === undefined) - qtest_fail("A delta value is required for fuzzyCompare", 2) - - var success = qtest_results.fuzzyCompare(actual, expected, delta) - if (msg === undefined) { - if (success) - msg = "FUZZYCOMPARE()" - else - msg = "Compared values are not the same with delta(" + delta + ")" - } - - if (!qtest_results.compare(success, msg, actual, expected, util.callerFile(), util.callerLine())) { - throw new Error("QtQuickTest::fail") - } - } - - /*! - \qmlmethod object TestCase::grabImage(item) - - Returns a snapshot image object of the given \a item. - - The returned image object has the following properties: - \list - \li width Returns the width of the underlying image (since 5.10) - \li height Returns the height of the underlying image (since 5.10) - \li size Returns the size of the underlying image (since 5.10) - \endlist - - Additionally, the returned image object has the following methods: - \list - \li \c {red(x, y)} Returns the red channel value of the pixel at \e x, \e y position - \li \c {green(x, y)} Returns the green channel value of the pixel at \e x, \e y position - \li \c {blue(x, y)} Returns the blue channel value of the pixel at \e x, \e y position - \li \c {alpha(x, y)} Returns the alpha channel value of the pixel at \e x, \e y position - \li \c {pixel(x, y)} Returns the color value of the pixel at \e x, \e y position - \li \c {equals(image)} Returns \c true if this image is identical to \e image - - see \l QImage::operator== (since 5.6) - - For example: - - \code - var image = grabImage(rect); - compare(image.red(10, 10), 255); - compare(image.pixel(20, 20), Qt.rgba(255, 0, 0, 255)); - - rect.width += 10; - var newImage = grabImage(rect); - verify(!newImage.equals(image)); - \endcode - - \li \c {save(path)} Saves the image to the given \e path. If the image cannot - be saved, an exception will be thrown. (since 5.10) - - This can be useful to perform postmortem analysis on failing tests, for - example: - - \code - var image = grabImage(rect); - try { - compare(image.width, 100); - } catch (ex) { - image.save("debug.png"); - throw ex; - } - \endcode - - \endlist - */ - function grabImage(item) { - return qtest_results.grabImage(item); - } - - /*! - \since 5.4 - \qmlmethod QtObject TestCase::findChild(parent, objectName) - - Returns the first child of \a parent with \a objectName, or \c null if - no such item exists. Both visual and non-visual children are searched - recursively, with visual children being searched first. - - \code - compare(findChild(item, "childObject"), expectedChildObject); - \endcode - */ - function findChild(parent, objectName) { - // First, search the visual item hierarchy. - var child = qtest_findVisualChild(parent, objectName); - if (child) - return child; - - // If it's not a visual child, it might be a QObject child. - return qtest_results.findChild(parent, objectName); - } - - /*! \internal */ - function qtest_findVisualChild(parent, objectName) { - if (!parent || parent.children === undefined) - return null; - - for (var i = 0; i < parent.children.length; ++i) { - // Is this direct child of ours the child we're after? - var child = parent.children[i]; - if (child.objectName === objectName) - return child; - } - - for (i = 0; i < parent.children.length; ++i) { - // Try the direct child's children. - child = qtest_findVisualChild(parent.children[i], objectName); - if (child) - return child; - } - return null; - } - - /*! - \qmlmethod TestCase::tryCompare(obj, property, expected, timeout = 5000, message = "") - - Fails the current test case if the specified \a property on \a obj - is not the same as \a expected, and displays the optional \a message. - The test will be retried multiple times until the - \a timeout (in milliseconds) is reached. - - This function is intended for testing applications where a property - changes value based on asynchronous events. Use compare() for testing - synchronous property changes. - - \code - tryCompare(img, "status", BorderImage.Ready) - compare(img.width, 120) - compare(img.height, 120) - compare(img.horizontalTileMode, BorderImage.Stretch) - compare(img.verticalTileMode, BorderImage.Stretch) - \endcode - - SignalSpy::wait() provides an alternative method to wait for a - signal to be emitted. - - \sa compare(), SignalSpy::wait() - */ - function tryCompare(obj, prop, value, timeout, msg) { - if (arguments.length == 1 || (typeof(prop) != "string" && typeof(prop) != "number")) { - qtest_results.fail("A property name as string or index is required for tryCompare", - util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - if (arguments.length == 2) { - qtest_results.fail("A value is required for tryCompare", - util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - if (timeout !== undefined && typeof(timeout) != "number") { - qtest_results.fail("timeout should be a number", - util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::fail") - } - if (!timeout) - timeout = 5000 - if (msg === undefined) - msg = "property " + prop - if (!qtest_compareInternal(obj[prop], value)) - wait(0) - var i = 0 - while (i < timeout && !qtest_compareInternal(obj[prop], value)) { - wait(50) - i += 50 - } - var actual = obj[prop] - var act = qtest_results.stringify(actual) - var exp = qtest_results.stringify(value) - var success = qtest_compareInternal(actual, value) - if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine())) - throw new Error("QtQuickTest::fail") - } - - /*! - \qmlmethod TestCase::skip(message = "") - - Skips the current test case and prints the optional \a message. - If this is a data-driven test, then only the current row is skipped. - Similar to \c{QSKIP(message)} in C++. - */ - function skip(msg) { - if (msg === undefined) - msg = "" - qtest_results.skip(msg, util.callerFile(), util.callerLine()) - throw new Error("QtQuickTest::skip") - } - - /*! - \qmlmethod TestCase::expectFail(tag, message) - - In a data-driven test, marks the row associated with \a tag as - expected to fail. When the fail occurs, display the \a message, - abort the test, and mark the test as passing. Similar to - \c{QEXPECT_FAIL(tag, message, Abort)} in C++. - - If the test is not data-driven, then \a tag must be set to - an empty string. - - \sa expectFailContinue() - */ - function expectFail(tag, msg) { - if (tag === undefined) { - warn("tag argument missing from expectFail()") - tag = "" - } - if (msg === undefined) { - warn("message argument missing from expectFail()") - msg = "" - } - if (!qtest_results.expectFail(tag, msg, util.callerFile(), util.callerLine())) - throw new Error("QtQuickTest::expectFail") - } - - /*! - \qmlmethod TestCase::expectFailContinue(tag, message) - - In a data-driven test, marks the row associated with \a tag as - expected to fail. When the fail occurs, display the \a message, - and then continue the test. Similar to - \c{QEXPECT_FAIL(tag, message, Continue)} in C++. - - If the test is not data-driven, then \a tag must be set to - an empty string. - - \sa expectFail() - */ - function expectFailContinue(tag, msg) { - if (tag === undefined) { - warn("tag argument missing from expectFailContinue()") - tag = "" - } - if (msg === undefined) { - warn("message argument missing from expectFailContinue()") - msg = "" - } - if (!qtest_results.expectFailContinue(tag, msg, util.callerFile(), util.callerLine())) - throw new Error("QtQuickTest::expectFail") - } - - /*! - \qmlmethod TestCase::warn(message) - - Prints \a message as a warning message. Similar to - \c{QWARN(message)} in C++. - - \sa ignoreWarning() - */ - function warn(msg) { - if (msg === undefined) - msg = "" - qtest_results.warn(msg, util.callerFile(), util.callerLine()); - } - - /*! - \qmlmethod TestCase::ignoreWarning(message) - - Marks \a message as an ignored warning message. When it occurs, - the warning will not be printed and the test passes. If the message - does not occur, then the test will fail. Similar to - \c{QTest::ignoreMessage(QtWarningMsg, message)} in C++. - - Since Qt 5.12, \a message can be either a string, or a regular - expression providing a pattern of messages to ignore. - - For example, the following snippet will ignore a string warning message: - \qml - ignoreWarning("Something sort of bad happened") - \endqml - - And the following snippet will ignore a regular expression matching a - number of possible warning messages: - \qml - ignoreWarning(new RegExp("[0-9]+ bad things happened")) - \endqml - - \note Despite being a JavaScript RegExp object, it will not be - interpreted as such; instead, the pattern will be passed to - \l QRegularExpression. - - \sa warn() - */ - function ignoreWarning(msg) { - if (msg === undefined) - msg = "" - qtest_results.ignoreWarning(msg) - } - - /*! - \qmlmethod TestCase::wait(ms) - - Waits for \a ms milliseconds while processing Qt events. - - \sa sleep(), waitForRendering() - */ - function wait(ms) { - qtest_results.wait(ms) - } - - /*! - \qmlmethod TestCase::waitForRendering(item, timeout = 5000) - - Waits for \a timeout milliseconds or until the \a item is rendered by the renderer. - Returns true if \c item is rendered in \a timeout milliseconds, otherwise returns false. - The default \a timeout value is 5000. - - \sa sleep(), wait() - */ - function waitForRendering(item, timeout) { - if (timeout === undefined) - timeout = 5000 - if (!qtest_verifyItem(item, "waitForRendering")) - return - return qtest_results.waitForRendering(item, timeout) - } - - /*! - \qmlmethod TestCase::sleep(ms) - - Sleeps for \a ms milliseconds without processing Qt events. - - \sa wait(), waitForRendering() - */ - function sleep(ms) { - qtest_results.sleep(ms) - } - - /*! - \qmlmethod TestCase::keyPress(key, modifiers = Qt.NoModifier, delay = -1) - - Simulates pressing a \a key with optional \a modifiers on the currently - focused item. If \a delay is larger than 0, the test will wait for - \a delay milliseconds. - - The event will be sent to the TestCase window or, in case of multiple windows, - to the current active window. See \l QGuiApplication::focusWindow() for more details. - - \b{Note:} At some point you should release the key using keyRelease(). - - \sa keyRelease(), keyClick() - */ - function keyPress(key, modifiers, delay) { - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (delay == undefined) - delay = -1 - if (typeof(key) == "string" && key.length == 1) { - if (!qtest_events.keyPressChar(key, modifiers, delay)) - qtest_fail("window not shown", 2) - } else { - if (!qtest_events.keyPress(key, modifiers, delay)) - qtest_fail("window not shown", 2) - } - } - - /*! - \qmlmethod TestCase::keyRelease(key, modifiers = Qt.NoModifier, delay = -1) - - Simulates releasing a \a key with optional \a modifiers on the currently - focused item. If \a delay is larger than 0, the test will wait for - \a delay milliseconds. - - The event will be sent to the TestCase window or, in case of multiple windows, - to the current active window. See \l QGuiApplication::focusWindow() for more details. - - \sa keyPress(), keyClick() - */ - function keyRelease(key, modifiers, delay) { - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (delay == undefined) - delay = -1 - if (typeof(key) == "string" && key.length == 1) { - if (!qtest_events.keyReleaseChar(key, modifiers, delay)) - qtest_fail("window not shown", 2) - } else { - if (!qtest_events.keyRelease(key, modifiers, delay)) - qtest_fail("window not shown", 2) - } - } - - /*! - \qmlmethod TestCase::keyClick(key, modifiers = Qt.NoModifier, delay = -1) - - Simulates clicking of \a key with optional \a modifiers on the currently - focused item. If \a delay is larger than 0, the test will wait for - \a delay milliseconds. - - The event will be sent to the TestCase window or, in case of multiple windows, - to the current active window. See \l QGuiApplication::focusWindow() for more details. - - \sa keyPress(), keyRelease() - */ - function keyClick(key, modifiers, delay) { - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (delay == undefined) - delay = -1 - if (typeof(key) == "string" && key.length == 1) { - if (!qtest_events.keyClickChar(key, modifiers, delay)) - qtest_fail("window not shown", 2) - } else { - if (!qtest_events.keyClick(key, modifiers, delay)) - qtest_fail("window not shown", 2) - } - } - - /*! - \since 5.10 - \qmlmethod TestCase::keySequence(keySequence) - - Simulates typing of \a keySequence. The key sequence can be set - to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts}, or - it can be described with a string containing a sequence of up to four key - presses. - - Each event shall be sent to the TestCase window or, in case of multiple windows, - to the current active window. See \l QGuiApplication::focusWindow() for more details. - - \sa keyPress(), keyRelease(), {GNU Emacs Style Key Sequences}, - {QtQuick::Shortcut::sequence}{Shortcut.sequence} - */ - function keySequence(keySequence) { - if (!qtest_events.keySequence(keySequence)) - qtest_fail("window not shown", 2) - } - - /*! - \qmlmethod TestCase::mousePress(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) - - Simulates pressing a mouse \a button with optional \a modifiers - on an \a item. The position is defined by \a x and \a y. - If \a x or \a y are not defined the position will be the center of \a item. - If \a delay is specified, the test will wait for the specified amount of - milliseconds before the press. - - The position given by \a x and \a y is transformed from the co-ordinate - system of \a item into window co-ordinates and then delivered. - If \a item is obscured by another item, or a child of \a item occupies - that position, then the event will be delivered to the other item instead. - - \sa mouseRelease(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel() - */ - function mousePress(item, x, y, button, modifiers, delay) { - if (!qtest_verifyItem(item, "mousePress")) - return - - if (button === undefined) - button = Qt.LeftButton - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (delay == undefined) - delay = -1 - if (x === undefined) - x = item.width / 2 - if (y === undefined) - y = item.height / 2 - if (!qtest_events.mousePress(item, x, y, button, modifiers, delay)) - qtest_fail("window not shown", 2) - } - - /*! - \qmlmethod TestCase::mouseRelease(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) - - Simulates releasing a mouse \a button with optional \a modifiers - on an \a item. The position of the release is defined by \a x and \a y. - If \a x or \a y are not defined the position will be the center of \a item. - If \a delay is specified, the test will wait for the specified amount of - milliseconds before releasing the button. - - The position given by \a x and \a y is transformed from the co-ordinate - system of \a item into window co-ordinates and then delivered. - If \a item is obscured by another item, or a child of \a item occupies - that position, then the event will be delivered to the other item instead. - - \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel() - */ - function mouseRelease(item, x, y, button, modifiers, delay) { - if (!qtest_verifyItem(item, "mouseRelease")) - return - - if (button === undefined) - button = Qt.LeftButton - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (delay == undefined) - delay = -1 - if (x === undefined) - x = item.width / 2 - if (y === undefined) - y = item.height / 2 - if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay)) - qtest_fail("window not shown", 2) - } - - /*! - \qmlmethod TestCase::mouseDrag(item, x, y, dx, dy, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) - - Simulates dragging the mouse on an \a item with \a button pressed and optional \a modifiers - The initial drag position is defined by \a x and \a y, - and drag distance is defined by \a dx and \a dy. If \a delay is specified, - the test will wait for the specified amount of milliseconds before releasing the button. - - The position given by \a x and \a y is transformed from the co-ordinate - system of \a item into window co-ordinates and then delivered. - If \a item is obscured by another item, or a child of \a item occupies - that position, then the event will be delivered to the other item instead. - - \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseRelease(), mouseWheel() - */ - function mouseDrag(item, x, y, dx, dy, button, modifiers, delay) { - if (!qtest_verifyItem(item, "mouseDrag")) - return - - if (item.x === undefined || item.y === undefined) - return - if (button === undefined) - button = Qt.LeftButton - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (delay == undefined) - delay = -1 - var moveDelay = Math.max(1, delay === -1 ? qtest_events.defaultMouseDelay : delay) - - // Divide dx and dy to have intermediate mouseMove while dragging - // Fractions of dx/dy need be superior to the dragThreshold - // to make the drag works though - var intermediateDx = Math.round(dx/3) - if (Math.abs(intermediateDx) < (util.dragThreshold + 1)) - intermediateDx = 0 - var intermediateDy = Math.round(dy/3) - if (Math.abs(intermediateDy) < (util.dragThreshold + 1)) - intermediateDy = 0 - - mousePress(item, x, y, button, modifiers, delay) - - // Trigger dragging by dragging past the drag threshold, but making sure to only drag - // along a certain axis if a distance greater than zero was given for that axis. - var dragTriggerXDistance = dx > 0 ? (util.dragThreshold + 1) : 0 - var dragTriggerYDistance = dy > 0 ? (util.dragThreshold + 1) : 0 - mouseMove(item, x + dragTriggerXDistance, y + dragTriggerYDistance, moveDelay, button) - if (intermediateDx !== 0 || intermediateDy !== 0) { - mouseMove(item, x + intermediateDx, y + intermediateDy, moveDelay, button) - mouseMove(item, x + 2*intermediateDx, y + 2*intermediateDy, moveDelay, button) - } - mouseMove(item, x + dx, y + dy, moveDelay, button) - mouseRelease(item, x + dx, y + dy, button, modifiers, delay) - } - - /*! - \qmlmethod TestCase::mouseClick(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) - - Simulates clicking a mouse \a button with optional \a modifiers - on an \a item. The position of the click is defined by \a x and \a y. - If \a x and \a y are not defined the position will be the center of \a item. - If \a delay is specified, the test will wait for the specified amount of - milliseconds before pressing and before releasing the button. - - The position given by \a x and \a y is transformed from the co-ordinate - system of \a item into window co-ordinates and then delivered. - If \a item is obscured by another item, or a child of \a item occupies - that position, then the event will be delivered to the other item instead. - - \sa mousePress(), mouseRelease(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel() - */ - function mouseClick(item, x, y, button, modifiers, delay) { - if (!qtest_verifyItem(item, "mouseClick")) - return - - if (button === undefined) - button = Qt.LeftButton - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (delay == undefined) - delay = -1 - if (x === undefined) - x = item.width / 2 - if (y === undefined) - y = item.height / 2 - if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay)) - qtest_fail("window not shown", 2) - } - - /*! - \qmlmethod TestCase::mouseDoubleClickSequence(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) - - Simulates the full sequence of events generated by double-clicking a mouse - \a button with optional \a modifiers on an \a item. - - This method reproduces the sequence of mouse events generated when a user makes - a double click: Press-Release-Press-DoubleClick-Release. - - The position of the click is defined by \a x and \a y. - If \a x and \a y are not defined the position will be the center of \a item. - If \a delay is specified, the test will wait for the specified amount of - milliseconds before pressing and before releasing the button. - - The position given by \a x and \a y is transformed from the co-ordinate - system of \a item into window co-ordinates and then delivered. - If \a item is obscured by another item, or a child of \a item occupies - that position, then the event will be delivered to the other item instead. - - This QML method was introduced in Qt 5.5. - - \sa mousePress(), mouseRelease(), mouseClick(), mouseMove(), mouseDrag(), mouseWheel() - */ - function mouseDoubleClickSequence(item, x, y, button, modifiers, delay) { - if (!qtest_verifyItem(item, "mouseDoubleClickSequence")) - return - - if (button === undefined) - button = Qt.LeftButton - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (delay == undefined) - delay = -1 - if (x === undefined) - x = item.width / 2 - if (y === undefined) - y = item.height / 2 - if (!qtest_events.mouseDoubleClickSequence(item, x, y, button, modifiers, delay)) - qtest_fail("window not shown", 2) - } - - /*! - \qmlmethod TestCase::mouseMove(item, x = item.width / 2, y = item.height / 2, delay = -1, buttons = Qt.NoButton) - - Moves the mouse pointer to the position given by \a x and \a y within - \a item, while holding \a buttons if given. Since Qt 6.0, if \a x and - \a y are not defined, the position will be the center of \a item. - - If a \a delay (in milliseconds) is given, the test will wait before - moving the mouse pointer. - - The position given by \a x and \a y is transformed from the co-ordinate - system of \a item into window co-ordinates and then delivered. - If \a item is obscured by another item, or a child of \a item occupies - that position, then the event will be delivered to the other item instead. - - \sa mousePress(), mouseRelease(), mouseClick(), mouseDoubleClickSequence(), mouseDrag(), mouseWheel() - */ - function mouseMove(item, x, y, delay, buttons) { - if (!qtest_verifyItem(item, "mouseMove")) - return - - if (delay == undefined) - delay = -1 - if (buttons == undefined) - buttons = Qt.NoButton - if (x === undefined) - x = item.width / 2 - if (y === undefined) - y = item.height / 2 - if (!qtest_events.mouseMove(item, x, y, delay, buttons)) - qtest_fail("window not shown", 2) - } - - /*! - \qmlmethod TestCase::mouseWheel(item, x, y, xDelta, yDelta, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) - - Simulates rotating the mouse wheel on an \a item with \a button pressed and optional \a modifiers. - The position of the wheel event is defined by \a x and \a y. - If \a delay is specified, the test will wait for the specified amount of milliseconds before releasing the button. - - The position given by \a x and \a y is transformed from the co-ordinate - system of \a item into window co-ordinates and then delivered. - If \a item is obscured by another item, or a child of \a item occupies - that position, then the event will be delivered to the other item instead. - - The \a xDelta and \a yDelta contain the wheel rotation distance in eighths of a degree. see \l QWheelEvent::angleDelta() for more details. - - \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseRelease(), mouseDrag(), QWheelEvent::angleDelta() - */ - function mouseWheel(item, x, y, xDelta, yDelta, buttons, modifiers, delay) { - if (!qtest_verifyItem(item, "mouseWheel")) - return - - if (delay == undefined) - delay = -1 - if (buttons == undefined) - buttons = Qt.NoButton - if (modifiers === undefined) - modifiers = Qt.NoModifier - if (xDelta == undefined) - xDelta = 0 - if (yDelta == undefined) - yDelta = 0 - if (!qtest_events.mouseWheel(item, x, y, buttons, modifiers, xDelta, yDelta, delay)) - qtest_fail("window not shown", 2) - } - - /*! - \qmlmethod TouchEventSequence TestCase::touchEvent(object item) - - \since 5.9 - - Begins a sequence of touch events through a simulated touchscreen (QPointingDevice). - Events are delivered to the window containing \a item. - - The returned object is used to enumerate events to be delivered through a single - QTouchEvent. Touches are delivered to the window containing the TestCase unless - otherwise specified. - - \code - Rectangle { - width: 640; height: 480 - - MultiPointTouchArea { - id: area - anchors.fill: parent - - property bool touched: false - - onPressed: touched = true - } - - TestCase { - name: "ItemTests" - when: windowShown - id: test1 - - function test_touch() { - var touch = touchEvent(area); - touch.press(0, area, 10, 10); - touch.commit(); - verify(area.touched); - } - } - } - \endcode - - \sa TouchEventSequence::press(), TouchEventSequence::move(), TouchEventSequence::release(), TouchEventSequence::stationary(), TouchEventSequence::commit(), QInputDevice::DeviceType - */ - - function touchEvent(item) { - if (!qtest_verifyItem(item, "touchEvent")) - return - - return { - _defaultItem: item, - _sequence: qtest_events.touchEvent(item), - - press: function (id, target, x, y) { - if (!target) - target = this._defaultItem; - if (id === undefined) - qtest_fail("No id given to TouchEventSequence::press", 1); - if (x === undefined) - x = target.width / 2; - if (y === undefined) - y = target.height / 2; - this._sequence.press(id, target, x, y); - return this; - }, - - move: function (id, target, x, y) { - if (!target) - target = this._defaultItem; - if (id === undefined) - qtest_fail("No id given to TouchEventSequence::move", 1); - if (x === undefined) - x = target.width / 2; - if (y === undefined) - y = target.height / 2; - this._sequence.move(id, target, x, y); - return this; - }, - - stationary: function (id) { - if (id === undefined) - qtest_fail("No id given to TouchEventSequence::stationary", 1); - this._sequence.stationary(id); - return this; - }, - - release: function (id, target, x, y) { - if (!target) - target = this._defaultItem; - if (id === undefined) - qtest_fail("No id given to TouchEventSequence::release", 1); - if (x === undefined) - x = target.width / 2; - if (y === undefined) - y = target.height / 2; - this._sequence.release(id, target, x, y); - return this; - }, - - commit: function () { - this._sequence.commit(); - return this; - } - }; - } - - // Functions that can be overridden in subclasses for init/cleanup duties. - /*! - \qmlmethod TestCase::initTestCase() - - This function is called before any other test functions in the - \l TestCase type. The default implementation does nothing. - The application can provide its own implementation to perform - test case initialization. - - \sa cleanupTestCase(), init() - */ - function initTestCase() {} - - /*! - \qmlmethod TestCase::cleanupTestCase() - - This function is called after all other test functions in the - \l TestCase type have completed. The default implementation - does nothing. The application can provide its own implementation - to perform test case cleanup. - - \sa initTestCase(), cleanup() - */ - function cleanupTestCase() {} - - /*! - \qmlmethod TestCase::init() - - This function is called before each test function that is - executed in the \l TestCase type. The default implementation - does nothing. The application can provide its own implementation - to perform initialization before each test function. - - \sa cleanup(), initTestCase() - */ - function init() {} - - /*! - \qmlmethod TestCase::cleanup() - - This function is called after each test function that is - executed in the \l TestCase type. The default implementation - does nothing. The application can provide its own implementation - to perform cleanup after each test function. - - \sa init(), cleanupTestCase() - */ - function cleanup() {} - - /*! \internal */ - function qtest_verifyItem(item, method) { - try { - if (!(item instanceof Item) && - !(item instanceof Window)) { - // it's a QObject, but not a type - qtest_fail("TypeError: %1 requires an Item or Window type".arg(method), 2); - return false; - } - } catch (e) { // it's not a QObject - qtest_fail("TypeError: %1 requires an Item or Window type".arg(method), 3); - return false; - } - - return true; - } - - /*! \internal */ - function qtest_runInternal(prop, arg) { - try { - qtest_testCaseResult = testCase[prop](arg) - } catch (e) { - qtest_testCaseResult = [] - if (e.message.indexOf("QtQuickTest::") != 0) { - // Test threw an unrecognized exception - fail. - qtest_results.fail("Uncaught exception: " + e.message, - e.fileName, e.lineNumber) - } - } - return !qtest_results.failed - } - - /*! \internal */ - function qtest_runFunction(prop, arg) { - qtest_runInternal("init") - if (!qtest_results.skipped) { - qtest_runInternal(prop, arg) - qtest_results.finishTestData() - qtest_runInternal("cleanup") - qtest_destroyTemporaryObjects() - qtest_results.finishTestDataCleanup() - // wait(0) will call processEvents() so objects marked for deletion - // in the test function will be deleted. - wait(0) - } - } - - /*! \internal */ - function qtest_runBenchmarkFunction(prop, arg) { - qtest_results.startMeasurement() - do { - qtest_results.beginDataRun() - do { - // Run the initialization function. - qtest_runInternal("init") - if (qtest_results.skipped) - break - - // Execute the benchmark function. - if (prop.indexOf("benchmark_once_") != 0) - qtest_results.startBenchmark(TestResult.RepeatUntilValidMeasurement, qtest_results.dataTag) - else - qtest_results.startBenchmark(TestResult.RunOnce, qtest_results.dataTag) - while (!qtest_results.isBenchmarkDone()) { - var success = qtest_runInternal(prop, arg) - qtest_results.finishTestData() - if (!success) - break - qtest_results.nextBenchmark() - } - qtest_results.stopBenchmark() - - // Run the cleanup function. - qtest_runInternal("cleanup") - qtest_results.finishTestDataCleanup() - // wait(0) will call processEvents() so objects marked for deletion - // in the test function will be deleted. - wait(0) - } while (!qtest_results.measurementAccepted()) - qtest_results.endDataRun() - } while (qtest_results.needsMoreMeasurements()) - } - - /*! \internal */ - function qtest_run() { - if (TestLogger.log_start_test()) { - qtest_results.reset() - qtest_results.testCaseName = name - qtest_results.startLogging() - } else { - qtest_results.testCaseName = name - } - running = true - - // Check the run list to see if this class is mentioned. - let checkNames = false - let testsToRun = {} // explicitly provided function names to run and their tags for data-driven tests - - if (qtest_results.functionsToRun.length > 0) { - checkNames = true - var found = false - - if (name.length > 0) { - for (var index in qtest_results.functionsToRun) { - let caseFuncName = qtest_results.functionsToRun[index] - if (caseFuncName.indexOf(name + "::") != 0) - continue - - found = true - let funcName = caseFuncName.substring(name.length + 2) - - if (!(funcName in testsToRun)) - testsToRun[funcName] = [] - - let tagName = qtest_results.tagsToRun[index] - if (tagName.length > 0) // empty tags mean run all rows - testsToRun[funcName].push(tagName) - } - } - if (!found) { - completed = true - if (!TestLogger.log_complete_test(qtest_testId)) { - qtest_results.stopLogging() - Qt.quit() - } - qtest_results.testCaseName = "" - return - } - } - - // Run the initTestCase function. - qtest_results.functionName = "initTestCase" - var runTests = true - if (!qtest_runInternal("initTestCase")) - runTests = false - qtest_results.finishTestData() - qtest_results.finishTestDataCleanup() - qtest_results.finishTestFunction() - - // Run the test methods. - var testList = [] - if (runTests) { - for (var prop in testCase) { - if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0) - continue - var tail = prop.lastIndexOf("_data"); - if (tail != -1 && tail == (prop.length - 5)) - continue - testList.push(prop) - } - testList.sort() - } - - for (var index in testList) { - var prop = testList[index] - - if (checkNames && !(prop in testsToRun)) - continue - - var datafunc = prop + "_data" - var isBenchmark = (prop.indexOf("benchmark_") == 0) - qtest_results.functionName = prop - - if (!(datafunc in testCase)) - datafunc = "init_data"; - - if (datafunc in testCase) { - if (qtest_runInternal(datafunc)) { - var table = qtest_testCaseResult - var haveData = false - - let checkTags = (checkNames && testsToRun[prop].length > 0) - - qtest_results.initTestTable() - for (var index in table) { - haveData = true - var row = table[index] - if (!row.tag) - row.tag = "row " + index // Must have something - if (checkTags) { - let tags = testsToRun[prop] - let tagIdx = tags.indexOf(row.tag) - if (tagIdx < 0) - continue - tags.splice(tagIdx, 1) - } - qtest_results.dataTag = row.tag - if (isBenchmark) - qtest_runBenchmarkFunction(prop, row) - else - qtest_runFunction(prop, row) - qtest_results.dataTag = "" - qtest_results.skipped = false - } - if (!haveData) { - if (datafunc === "init_data") - qtest_runFunction(prop, null, isBenchmark) - else - qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()" - , util.callerFile(), util.callerLine()); - } - qtest_results.clearTestTable() - } - } else if (isBenchmark) { - qtest_runBenchmarkFunction(prop, null, isBenchmark) - } else { - qtest_runFunction(prop, null, isBenchmark) - } - qtest_results.finishTestFunction() - qtest_results.skipped = false - - if (checkNames && testsToRun[prop].length <= 0) - delete testsToRun[prop] - } - - // Run the cleanupTestCase function. - qtest_results.skipped = false - qtest_results.functionName = "cleanupTestCase" - qtest_runInternal("cleanupTestCase") - - // Complain about missing functions that we were supposed to run. - if (checkNames) { - let missingTests = [] - for (var func in testsToRun) { - let caseFuncName = name + '::' + func - let tags = testsToRun[func] - if (tags.length <= 0) - missingTests.push(caseFuncName) - else - for (var i in tags) - missingTests.push(caseFuncName + ':' + tags[i]) - } - missingTests.sort() - if (missingTests.length > 0) - qtest_results.fail("Could not find test functions: " + missingTests, "", 0) - } - - // Clean up and exit. - running = false - completed = true - qtest_results.finishTestData() - qtest_results.finishTestDataCleanup() - qtest_results.finishTestFunction() - qtest_results.functionName = "" - - // Stop if there are no more tests to be run. - if (!TestLogger.log_complete_test(qtest_testId)) { - qtest_results.stopLogging() - Qt.quit() - } - qtest_results.testCaseName = "" - } - - onWhenChanged: { - if (when != qtest_prevWhen) { - qtest_prevWhen = when - if (when && !completed && !running && qtest_componentCompleted) - qtest_run() - } - } - - onOptionalChanged: { - if (!completed) { - if (optional) - TestLogger.log_optional_test(qtest_testId) - else - TestLogger.log_mandatory_test(qtest_testId) - } - } - - Component.onCompleted: { - QTestRootObject.hasTestCase = true; - qtest_componentCompleted = true; - qtest_testId = TestLogger.log_register_test(name) - if (optional) - TestLogger.log_optional_test(qtest_testId) - qtest_prevWhen = when - if (when && !completed && !running) - qtest_run() - } -} |