From daf671b42241533a2db1e598487256d616edf290 Mon Sep 17 00:00:00 2001 From: Charles Yin Date: Fri, 20 May 2011 11:57:29 +1000 Subject: [PATCH] Integrate QtQuickTest into Qt Change-Id: I558821c0dec9166ea1d0d2e1e2f889553c436316 Task-number:QTBUG-16082 --- doc/src/declarative/qmltest.qdoc | 123 ++ examples/examples.pro | 1 + examples/qmltest/qmltest.pro | 4 + examples/qmltest/tst_basic.qml | 76 + examples/qmltest/tst_item.qml | 58 + examples/qmltest/tst_qmltest.cpp | 43 + modules/qt_qmltest.pri | 17 + src/imports/imports.pro | 1 + src/imports/testlib/SignalSpy.qml | 109 ++ src/imports/testlib/TestCase.qml | 692 ++++++++ src/imports/testlib/main.cpp | 133 ++ src/imports/testlib/qmldir | 3 + src/imports/testlib/signalspy.h | 83 + src/imports/testlib/signalspy.qdoc | 146 ++ src/imports/testlib/testcase.h | 94 ++ src/imports/testlib/testcase.qdoc | 597 +++++++ src/imports/testlib/testlib.pro | 29 + src/imports/testlib/testlogger.js | 98 ++ src/qmltest/features/qmltestcase.prf | 25 + src/qmltest/qmltest.pro | 48 + src/qmltest/qtestoptions_p.h | 67 + src/qmltest/quicktest.cpp | 315 ++++ src/qmltest/quicktest.h | 97 ++ src/qmltest/quicktestevent.cpp | 258 +++ src/qmltest/quicktestevent_p.h | 78 + src/qmltest/quicktestglobal.h | 73 + src/qmltest/quicktestresult.cpp | 630 ++++++++ src/qmltest/quicktestresult_p.h | 179 +++ src/src.pro | 4 +- sync.profile | 7 + tests/auto/auto.pro | 2 + tests/auto/qmltest/buttonclick/Button.qml | 74 + .../qmltest/buttonclick/tst_buttonclick.qml | 66 + tests/auto/qmltest/createbenchmark/item.qml | 75 + .../createbenchmark/tst_createbenchmark.qml | 55 + tests/auto/qmltest/events/tst_events.qml | 86 + .../qdecarativebinding/tst_binding.qml | 75 + .../qdecarativebinding/tst_binding2.qml | 70 + .../qdecarativeborderimage/InvalidSciFile.qml | 48 + .../qdecarativeborderimage/colors-round.sci | 7 + .../qmltest/qdecarativeborderimage/colors.png | Bin 0 -> 1655 bytes .../qdecarativeborderimage/invalid.sci | 7 + .../tst_borderimage.qml | 247 +++ tests/auto/qmltest/qmltest.pro | 11 + tests/auto/qmltest/selftests/tst_compare.qml | 1391 +++++++++++++++++ .../selftests/tst_compare_quickobjects.qml | 81 + .../auto/qmltest/selftests/tst_selftests.qml | 307 ++++ tests/auto/qmltest/tst_qmltest.cpp | 43 + tools/qmltestrunner/main.cpp | 75 + tools/qmltestrunner/qmltestrunner.pro | 11 + tools/tools.pro | 2 + 51 files changed, 6820 insertions(+), 1 deletion(-) create mode 100644 doc/src/declarative/qmltest.qdoc create mode 100644 examples/qmltest/qmltest.pro create mode 100644 examples/qmltest/tst_basic.qml create mode 100644 examples/qmltest/tst_item.qml create mode 100644 examples/qmltest/tst_qmltest.cpp create mode 100644 modules/qt_qmltest.pri create mode 100644 src/imports/testlib/SignalSpy.qml create mode 100644 src/imports/testlib/TestCase.qml create mode 100644 src/imports/testlib/main.cpp create mode 100644 src/imports/testlib/qmldir create mode 100644 src/imports/testlib/signalspy.h create mode 100644 src/imports/testlib/signalspy.qdoc create mode 100644 src/imports/testlib/testcase.h create mode 100644 src/imports/testlib/testcase.qdoc create mode 100644 src/imports/testlib/testlib.pro create mode 100644 src/imports/testlib/testlogger.js create mode 100644 src/qmltest/features/qmltestcase.prf create mode 100644 src/qmltest/qmltest.pro create mode 100644 src/qmltest/qtestoptions_p.h create mode 100644 src/qmltest/quicktest.cpp create mode 100644 src/qmltest/quicktest.h create mode 100644 src/qmltest/quicktestevent.cpp create mode 100644 src/qmltest/quicktestevent_p.h create mode 100644 src/qmltest/quicktestglobal.h create mode 100644 src/qmltest/quicktestresult.cpp create mode 100644 src/qmltest/quicktestresult_p.h create mode 100644 tests/auto/qmltest/buttonclick/Button.qml create mode 100644 tests/auto/qmltest/buttonclick/tst_buttonclick.qml create mode 100644 tests/auto/qmltest/createbenchmark/item.qml create mode 100644 tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml create mode 100644 tests/auto/qmltest/events/tst_events.qml create mode 100644 tests/auto/qmltest/qdecarativebinding/tst_binding.qml create mode 100644 tests/auto/qmltest/qdecarativebinding/tst_binding2.qml create mode 100644 tests/auto/qmltest/qdecarativeborderimage/InvalidSciFile.qml create mode 100644 tests/auto/qmltest/qdecarativeborderimage/colors-round.sci create mode 100644 tests/auto/qmltest/qdecarativeborderimage/colors.png create mode 100644 tests/auto/qmltest/qdecarativeborderimage/invalid.sci create mode 100644 tests/auto/qmltest/qdecarativeborderimage/tst_borderimage.qml create mode 100644 tests/auto/qmltest/qmltest.pro create mode 100644 tests/auto/qmltest/selftests/tst_compare.qml create mode 100644 tests/auto/qmltest/selftests/tst_compare_quickobjects.qml create mode 100644 tests/auto/qmltest/selftests/tst_selftests.qml create mode 100644 tests/auto/qmltest/tst_qmltest.cpp create mode 100644 tools/qmltestrunner/main.cpp create mode 100644 tools/qmltestrunner/qmltestrunner.pro diff --git a/doc/src/declarative/qmltest.qdoc b/doc/src/declarative/qmltest.qdoc new file mode 100644 index 0000000000..fdecf21183 --- /dev/null +++ b/doc/src/declarative/qmltest.qdoc @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qmltest.html + \title QtQuickTest Reference Documentation + \keyword QtQuickTest Reference Documentation + + \section1 Introduction + + QtQuickTest is a unit test framework for Qt Quick (QML) applications. + Test cases are written as JavaScript functions within a TestCase + element: + + \code + import QtQuick 2.0 + import QtTest 1.0 + + 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 \c{test_} are treated as test cases + to be executed. See the documentation for the \l TestCase and + \l SignalSpy elements for more information on writing test cases. + + \section1 Running tests + + Test cases are launched by a C++ harness that consists of + the following code: + + \code + #include + QUICK_TEST_MAIN(example) + \endcode + + Where "example" is an identifier to use to uniquely identify + this set of tests. You should add \c{CONFIG += qmltestcase}. + for example: + + \code + TEMPLATE = app + TARGET = tst_example + CONFIG += warn_on qmltestcase + SOURCES += tst_example.cpp + \endcode + + The test harness scans the specified source directory recursively + for "tst_*.qml" files. If \c{QUICK_TEST_SOURCE_DIR} is not defined, + then the current directory will be scanned when the harness is run. + Other *.qml files may appear for auxillary QML components that are + used by the test. + + The \c{-input} command-line option can be set at runtime to run + test cases from a different directory. This may be needed to run + tests on a target device where the compiled-in directory name refers + to a host. For example: + + \code + tst_example -input /mnt/SDCard/qmltests + \endcode + + See \c{tests/qmlauto} in the source tree for an example of creating a + test harness that uses the \c{QUICK_TEST_SOURCE_DIR} macro. + + If your test case needs QML imports, then you can add them as + \c{-import} options to the the test program command-line by adding + the following line to your .pro file: + + \code + IMPORTPATH += $$PWD/../imports/my_module1 $$PWD/../imports/my_module2 + \endcode + + \section1 Running tests with QtQuick 1 + + The \c{-qtquick1} option can be passed to a test binary to run + the tests using QDeclarativeView (QtQuick 1) rather than QSGView (QtQuick 2): + + \code + tst_example -qtquick1 + \endcode + + To run tests with either QtQuick 1 or QtQuick 2, use + "import QtQuick 1.0" in your unit tests and then specify + compatibility mode to the QtQuick2 engine: + + \code + QMLSCENE_IMPORT_NAME=quick1 tst_example + \endcode +*/ diff --git a/examples/examples.pro b/examples/examples.pro index 09881f1a9c..2ee93a92db 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -1,2 +1,3 @@ TEMPLATE = subdirs SUBDIRS += declarative +contains(QT_CONFIG, qmltest): SUBDIRS += qmltest diff --git a/examples/qmltest/qmltest.pro b/examples/qmltest/qmltest.pro new file mode 100644 index 0000000000..40d964c20a --- /dev/null +++ b/examples/qmltest/qmltest.pro @@ -0,0 +1,4 @@ +TEMPLATE=app +TARGET=tst_qmltestexample +CONFIG += qmltestcase +SOURCES += tst_qmltest.cpp diff --git a/examples/qmltest/tst_basic.qml b/examples/qmltest/tst_basic.qml new file mode 100644 index 0000000000..48aac141a2 --- /dev/null +++ b/examples/qmltest/tst_basic.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +TestCase { + name: "BasicTests" + + function test_pass() { + compare(2 + 2, 4, "2 + 2") + } + + function test_fail() { + compare(2 + 2, 5, "2 + 2") + } + + function test_skip() { + skip("skipping") + } + + function test_expecting() { + expectFail("", "this is the fail we wanted") + verify(false) + } + + 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 }, + {tag: "2 + 2 = 5", a: 2, b: 2, answer: 5 }, // fail + ] + } + + function test_table(data) { + compare(data.a + data.b, data.answer) + } +} diff --git a/examples/qmltest/tst_item.qml b/examples/qmltest/tst_item.qml new file mode 100644 index 0000000000..4247834565 --- /dev/null +++ b/examples/qmltest/tst_item.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +Rectangle { + id: foo + width: 640; height: 480 + color: "cyan" + + TestCase { + name: "ItemTests" + id: test1 + + function test_color() { + compare(foo.color, "#00ffff") + } + } +} diff --git a/examples/qmltest/tst_qmltest.cpp b/examples/qmltest/tst_qmltest.cpp new file mode 100644 index 0000000000..4f6eb4d1b9 --- /dev/null +++ b/examples/qmltest/tst_qmltest.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +QUICK_TEST_MAIN(qmltest) diff --git a/modules/qt_qmltest.pri b/modules/qt_qmltest.pri new file mode 100644 index 0000000000..9cc2977837 --- /dev/null +++ b/modules/qt_qmltest.pri @@ -0,0 +1,17 @@ +QT.qmltest.VERSION = 5.0.0 +QT.qmltest.MAJOR_VERSION = 5 +QT.qmltest.MINOR_VERSION = 0 +QT.qmltest.PATCH_VERSION = 0 + +QT.qmltest.name = QtQuickTest +QT.qmltest.bins = $$QT_MODULE_BIN_BASE +QT.qmltest.includes = $$QT_MODULE_INCLUDE_BASE $$QT_MODULE_INCLUDE_BASE/QtQuickTest +QT.qmltest.private_includes = $$QT_MODULE_INCLUDE_BASE/QtQuickTest/$$QT.qmltest.VERSION +QT.qmltest.sources = $$QT_MODULE_BASE/src/qmltest +QT.qmltest.libs = $$QT_MODULE_LIB_BASE +QT.qmltest.plugins = $$QT_MODULE_PLUGIN_BASE +QT.qmltest.imports = $$QT_MODULE_IMPORT_BASE +QT.qmltest.depends = declarative testlib +QT.qmltest.DEFINES = QT_DECLARATIVE_LIB + +QT_CONFIG += qmltest diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 6704b3ae3c..e81c4bf5cf 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs SUBDIRS += folderlistmodel particles gestures inputcontext etcprovider +contains(QT_CONFIG, qmltest): SUBDIRS += testlib diff --git a/src/imports/testlib/SignalSpy.qml b/src/imports/testlib/SignalSpy.qml new file mode 100644 index 0000000000..676412998b --- /dev/null +++ b/src/imports/testlib/SignalSpy.qml @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: spy + visible: false + + // Public API. + + property variant target: null + property string signalName: "" + property int count: 0 + + function clear() { + count = 0 + qtest_expectedCount = 0 + } + + function wait(timeout) { + if (timeout === undefined) + timeout = 5000 + var expected = ++qtest_expectedCount + var i = 0 + while (i < timeout && count < expected) { + qtest_results.wait(50) + i += 50 + } + var success = (count >= expected) + if (!qtest_results.verify(success, "wait for signal " + signalName, Qt.qtest_caller_file(), Qt.qtest_caller_line())) + throw new Error("QtQuickTest::fail") + } + + // Internal implementation detail follows. + + TestResult { id: qtest_results } + + onTargetChanged: { + qtest_update() + } + onSignalNameChanged: { + qtest_update() + } + + property variant qtest_prevTarget: null + property string qtest_prevSignalName: "" + property int qtest_expectedCount: 0 + + function qtest_update() { + if (qtest_prevTarget != null) { + qtest_prevTarget[qtest_prevSignalName].disconnect(spy, "qtest_activated") + qtest_prevTarget = null + qtest_prevSignalName = "" + } + if (target != null && signalName != "") { + var func = target[signalName] + if (func === undefined) { + console.log("Signal '" + signalName + "' not found") + } else { + qtest_prevTarget = target + qtest_prevSignalName = signalName + func.connect(spy.qtest_activated) + } + } + } + + function qtest_activated() { + ++count + } +} diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml new file mode 100644 index 0000000000..3659c6029d --- /dev/null +++ b/src/imports/testlib/TestCase.qml @@ -0,0 +1,692 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import "testlogger.js" as TestLogger + +Item { + id: testCase + visible: false + + // Name of the test case to prefix the function name in messages. + property string name + + // Set to true to start the test running. + property bool when: true + + // Set to true once the test has completed. + property bool completed: false + + // Set to true when the test is running but not yet complete. + property bool running: false + + // Set to true if the test doesn't have to run (because some + // other test failed which this one depends on). + property bool optional: false + + // Property that is set to true when the main window is shown. + // We need to set the property value in an odd way to handle + // both qmlviewer and the QtQuickTest module test wrapper. + property bool windowShown: Qt.qtest_wrapper ? qtest.windowShown : false + + // Internal private state. Identifiers prefixed with qtest are reserved. + property bool qtest_prevWhen: true + property int qtest_testId: -1 + property variant qtest_testCaseResult + property variant qtest_results: qtest_results_normal + TestResult { id: qtest_results_normal } + property variant qtest_events: qtest_events_normal + TestEvent { id: qtest_events_normal } + + function fail(msg) { + if (msg === undefined) + msg = ""; + qtest_results.fail(msg, Qt.qtest_caller_file(), Qt.qtest_caller_line()) + throw new Error("QtQuickTest::fail") + } + + function qtest_fail(msg, frame) { + if (msg === undefined) + msg = ""; + qtest_results.fail(msg, Qt.qtest_caller_file(frame), Qt.qtest_caller_line(frame)) + throw new Error("QtQuickTest::fail") + } + + function verify(cond, msg) { + if (msg === undefined) + msg = ""; + if (!qtest_results.verify(cond, msg, Qt.qtest_caller_file(), Qt.qtest_caller_line())) + throw new Error("QtQuickTest::fail") + } + + // Determine what is o. + // Discussions and reference: http://philrathe.com/articles/equiv + // Test suites: http://philrathe.com/tests/equiv + // Author: Philippe Rathé + 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"; // Qt3D vector + } + return "object"; + } else if (o instanceof Function) { + return "function"; + } else { + return undefined; + } + } + + // 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é + 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") || (typeExp === "object" && typeAct === "string")) { + success = (act == exp) + } + } else if (typeExp === "string" || typeExp === "boolean" || typeExp === "number" || + 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 + } + + 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 + } + + // Ensures identical properties name + return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort()); + + } + + 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 + } + + function qtest_formatValue(value) { + if (typeof value == "object") { + if ("x" in value && "y" in value && "z" in value) { + return "Qt.vector3d(" + value.x + ", " + + value.y + ", " + value.z + ")" + } + try { + return JSON.stringify(value) + } catch (ex) { + // stringify might fail (e.g. due to circular references) + } + } + return value + } + + function compare(actual, expected, msg) { + var act = qtest_formatValue(actual) + var exp = qtest_formatValue(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, Qt.qtest_caller_file(), Qt.qtest_caller_line())) + throw new Error("QtQuickTest::fail") + } + + function tryCompare(obj, prop, value, timeout) { + if (!timeout) + timeout = 5000 + 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_formatValue(actual) + var exp = qtest_formatValue(value) + var success = qtest_compareInternal(actual, value) + if (!qtest_results.compare(success, "property " + prop, act, exp, Qt.qtest_caller_file(), Qt.qtest_caller_line())) + throw new Error("QtQuickTest::fail") + } + + function skip(msg) { + if (msg === undefined) + msg = "" + qtest_results.skipSingle(msg, Qt.qtest_caller_file(), Qt.qtest_caller_line()) + throw new Error("QtQuickTest::skip") + } + + function skipAll(msg) { + if (msg === undefined) + msg = "" + qtest_results.skipAll(msg, Qt.qtest_caller_file(), Qt.qtest_caller_line()) + throw new Error("QtQuickTest::skip") + } + + 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, Qt.qtest_caller_file(), Qt.qtest_caller_line())) + throw new Error("QtQuickTest::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, Qt.qtest_caller_file(), Qt.qtest_caller_line())) + throw new Error("QtQuickTest::expectFail") + } + + function warn(msg) { + if (msg === undefined) + msg = "" + qtest_results.warn(msg); + } + + function ignoreWarning(msg) { + if (msg === undefined) + msg = "" + qtest_results.ignoreWarning(msg) + } + + function wait(ms) { + qtest_results.wait(ms) + } + + function sleep(ms) { + qtest_results.sleep(ms) + } + + function keyPress(key, modifiers, delay) { + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.keyPress(key, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function keyRelease(key, modifiers, delay) { + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.keyRelease(key, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function keyClick(key, modifiers, delay) { + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.keyClick(key, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mousePress(item, x, y, button, modifiers, delay) { + if (button === undefined) + button = Qt.LeftButton + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.mousePress(item, x, y, button, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mouseRelease(item, x, y, button, modifiers, delay) { + if (button === undefined) + button = Qt.LeftButton + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mouseClick(item, x, y, button, modifiers, delay) { + if (button === undefined) + button = Qt.LeftButton + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mouseDoubleClick(item, x, y, button, modifiers, delay) { + if (button === undefined) + button = Qt.LeftButton + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mouseMove(item, x, y, delay) { + if (delay == undefined) + delay = -1 + if (!qtest_events.mouseMove(item, x, y, delay)) + qtest_fail("window not shown", 2) + } + + // Functions that can be overridden in subclasses for init/cleanup duties. + function initTestCase() {} + function cleanupTestCase() {} + function init() {} + function cleanup() {} + + 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.dataFailed + } + + function qtest_runFunction(prop, arg) { + qtest_results.functionType = TestResult.InitFunc + qtest_runInternal("init") + if (!qtest_results.skipped) { + qtest_results.functionType = TestResult.Func + qtest_runInternal(prop, arg) + qtest_results.functionType = TestResult.CleanupFunc + qtest_runInternal("cleanup") + } + qtest_results.functionType = TestResult.NoWhere + } + + function qtest_runBenchmarkFunction(prop, arg) { + qtest_results.startMeasurement() + do { + qtest_results.beginDataRun() + do { + // Run the initialization function. + qtest_results.functionType = TestResult.InitFunc + qtest_runInternal("init") + if (qtest_results.skipped) + break + + // Execute the benchmark function. + qtest_results.functionType = TestResult.Func + 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()) { + if (!qtest_runInternal(prop, arg)) + break + qtest_results.nextBenchmark() + } + qtest_results.stopBenchmark() + + // Run the cleanup function. + qtest_results.functionType = TestResult.CleanupFunc + qtest_runInternal("cleanup") + qtest_results.functionType = TestResult.NoWhere + } while (!qtest_results.measurementAccepted()) + qtest_results.endDataRun() + } while (qtest_results.needsMoreMeasurements()) + } + + function qtest_run() { + if (Qt.qtest_printAvailableFunctions) { + completed = true + return + } + + 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. + var functionsToRun = qtest_results.functionsToRun + if (functionsToRun.length > 0) { + var found = false + var list = [] + if (name.length > 0) { + var prefix = name + "::" + for (var index in functionsToRun) { + if (functionsToRun[index].indexOf(prefix) == 0) { + list.push(functionsToRun[index]) + found = true + } + } + } + if (!found) { + completed = true + if (!TestLogger.log_complete_test(qtest_testId)) { + qtest_results.stopLogging() + Qt.quit() + } + qtest_results.testCaseName = "" + return + } + functionsToRun = list + } + + // Run the initTestCase function. + qtest_results.functionName = "initTestCase" + qtest_results.functionType = TestResult.InitFunc + var runTests = true + if (!qtest_runInternal("initTestCase")) + runTests = false + 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() + } + var checkNames = (functionsToRun.length > 0) + for (var index in testList) { + var prop = testList[index] + var datafunc = prop + "_data" + var isBenchmark = (prop.indexOf("benchmark_") == 0) + if (checkNames) { + var index = functionsToRun.indexOf(name + "::" + prop) + if (index < 0) + continue + functionsToRun.splice(index, 1) + } + qtest_results.functionName = prop + if (datafunc in testCase) { + qtest_results.functionType = TestResult.DataFunc + if (qtest_runInternal(datafunc)) { + var table = qtest_testCaseResult + var haveData = false + qtest_results.initTestTable() + for (var index in table) { + haveData = true + var row = table[index] + if (!row.tag) + row.tag = "row " + index // Must have something + qtest_results.dataTag = row.tag + if (isBenchmark) + qtest_runBenchmarkFunction(prop, row) + else + qtest_runFunction(prop, row) + qtest_results.dataTag = "" + } + if (!haveData) + qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()") + qtest_results.clearTestTable() + } + } else if (isBenchmark) { + qtest_runBenchmarkFunction(prop, null, isBenchmark) + } else { + qtest_runFunction(prop, null, isBenchmark) + } + qtest_results.finishTestFunction() + qtest_results.skipped = false + } + + // Run the cleanupTestCase function. + qtest_results.skipped = false + qtest_results.functionName = "cleanupTestCase" + qtest_results.functionType = TestResult.CleanupFunc + qtest_runInternal("cleanupTestCase") + + // Complain about missing functions that we were supposed to run. + if (functionsToRun.length > 0) + qtest_results.fail("Could not find functions: " + functionsToRun, "", 0) + + // Clean up and exit. + running = false + completed = true + 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_run() + } + } + + onOptionalChanged: { + if (!completed) { + if (optional) + TestLogger.log_optional_test(qtest_testId) + else + TestLogger.log_mandatory_test(qtest_testId) + } + } + + // The test framework will set qtest.windowShown when the + // window is actually shown. If we are running with qmlviewer, + // then this won't happen. So we use a timer instead. + Timer { + id: qtest_windowShowTimer + interval: 100 + repeat: false + onTriggered: { windowShown = true } + } + + Component.onCompleted: { + if (Qt.qtest_printAvailableFunctions) { + var testList = [] + 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 + // Note: cannot run functions in TestCase elements + // that lack a name. + if (name.length > 0) + testList.push(name + "::" + prop + "()") + } + testList.sort() + for (var index in testList) + console.log(testList[index]) + return + } + qtest_testId = TestLogger.log_register_test(name) + if (optional) + TestLogger.log_optional_test(qtest_testId) + qtest_prevWhen = when + var isQmlViewer = Qt.qtest_wrapper ? false : true + if (isQmlViewer) + qtest_windowShowTimer.running = true + if (when && !completed && !running) + qtest_run() + } +} diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp new file mode 100644 index 0000000000..5ef07b9d55 --- /dev/null +++ b/src/imports/testlib/main.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "QtQuickTest/private/quicktestresult_p.h" +#include "QtQuickTest/private/quicktestevent_p.h" +QT_BEGIN_NAMESPACE + +QML_DECLARE_TYPE(QuickTestResult) +QML_DECLARE_TYPE(QuickTestEvent) + +// Copied from qdeclarativedebughelper_p.h in Qt, to avoid a dependency +// on a private header from Qt. +class Q_DECLARATIVE_EXPORT QDeclarativeDebugHelper +{ +public: + static QScriptEngine *getScriptEngine(QDeclarativeEngine *engine); + static void setAnimationSlowDownFactor(qreal factor); + static void enableDebugging(); +}; + +static QScriptContext *qtest_find_frame(QScriptContext *ctx) +{ + qint32 frame = 1; + if (ctx->argumentCount() > 0) + frame = ctx->argument(0).toInt32(); + ++frame; // Exclude the native function; start at its caller. + while (ctx) { + if (frame-- <= 0) + break; + ctx = ctx->parentContext(); + } + return ctx; +} + +static QScriptValue qtest_caller_file + (QScriptContext *ctx, QScriptEngine *engine) +{ + ctx = qtest_find_frame(ctx); + if (ctx) { + QScriptContextInfo info(ctx); + return engine->newVariant(info.fileName()); + } + return engine->newVariant(QLatin1String("")); +} + +static QScriptValue qtest_caller_line + (QScriptContext *ctx, QScriptEngine *engine) +{ + ctx = qtest_find_frame(ctx); + if (ctx) { + QScriptContextInfo info(ctx); + return engine->newVariant(info.lineNumber()); + } + return engine->newVariant(qint32(0)); +} + +class QTestQmlModule : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtTest")); + qmlRegisterType(uri,1,0,"TestResult"); + qmlRegisterType(uri,1,0,"TestEvent"); + } + void initializeEngine(QDeclarativeEngine *engine, const char *) + { + // Install some helper functions in the global "Qt" object + // for walking the stack and finding a caller's location. + // Normally we would use an exception's backtrace, but JSC + // only provides the top-most frame in the backtrace. + QScriptEngine *eng = QDeclarativeDebugHelper::getScriptEngine(engine); + QScriptValue qtObject + = eng->globalObject().property(QLatin1String("Qt")); + qtObject.setProperty + (QLatin1String("qtest_caller_file"), + eng->newFunction(qtest_caller_file)); + qtObject.setProperty + (QLatin1String("qtest_caller_line"), + eng->newFunction(qtest_caller_line)); + } +}; + +QT_END_NAMESPACE + +#include "main.moc" + +Q_EXPORT_PLUGIN2(qmltestplugin, QT_PREPEND_NAMESPACE(QTestQmlModule)); diff --git a/src/imports/testlib/qmldir b/src/imports/testlib/qmldir new file mode 100644 index 0000000000..9e872f9683 --- /dev/null +++ b/src/imports/testlib/qmldir @@ -0,0 +1,3 @@ +plugin qmltestplugin +TestCase 1.0 TestCase.qml +SignalSpy 1.0 SignalSpy.qml diff --git a/src/imports/testlib/signalspy.h b/src/imports/testlib/signalspy.h new file mode 100644 index 0000000000..4a97959339 --- /dev/null +++ b/src/imports/testlib/signalspy.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNALSPY_H +#define SIGNALSPY_H + +// This is a dummy header for defining the interface of "SignalSpy.qml" to qdoc. + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class SignalSpy : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString signalName READ signalName WRITE signalName NOTIFY signalNameChanged) + Q_PROPERTY(int count READ count countChanged) +public: + SignalSpy(QDeclarativeItem *parent) : QDeclarativeItem(parent) {} + ~SignalSpy() + + QObject *target() const; + void setTarget(QObject *target); + + QString signalName() const; + void setSignalName(const QString &signalName); + + int count() const; + +Q_SIGNALS: + void targetChanged(); + void signalNameChanged(); + void countChanged(); +}; + +QML_DECLARE_TYPE(SignalSpy) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/imports/testlib/signalspy.qdoc b/src/imports/testlib/signalspy.qdoc new file mode 100644 index 0000000000..3ed39c4ad2 --- /dev/null +++ b/src/imports/testlib/signalspy.qdoc @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmlclass SignalSpy SignalSpy + \brief The SignalSpy item enables introspection of signal emission. + \since 4.8 + \ingroup qtest::qml + + In the following example, a SignalSpy is installed to watch the + "clicked" signal on a user-defined Button element. When the signal + is emitted, the \l count property on the spy will be increased. + + \code + Button { + id: button + SignalSpy { + id: spy + target: button + signalName: "clicked" + } + TestCase { + name: "ButtonClick" + function test_click() { + compare(spy.count, 0) + button.clicked(); + compare(spy.count, 1) + } + } + } + \endcode + + The above style of test is suitable for signals that are emitted + synchronously. For asynchronous signals, the wait() method can be + used to block the test until the signal occurs (or a timeout expires). + + \sa TestCase +*/ + +/*! + \qmlproperty object SignalSpy::target + + This property defines the target object that will be used to + listen for emissions of the \l signalName signal. + + \sa signalName, count +*/ + +/*! + \qmlproperty string SignalSpy::signalName + + This property defines the name of the signal on \l target to + listen for. + + \sa target, count +*/ + +/*! + \qmlproperty int SignalSpy::count + + This property defines the number of times that \l signalName has + been emitted from \l target since the last call to clear(). + + \sa target, signalName, clear() +*/ + +/*! + \qmlmethod SignalSpy::clear() + + Clears \l count to 0. + + \sa count, wait() +*/ + +/*! + \qmlmethod SignalSpy::wait(timeout = 5000) + + Waits for the signal \l signalName on \l target to be emitted, + for up to \a timeout milliseconds. The test case will fail if + the signal is not emitted. + + \code + SignalSpy { + id: spy + target: button + signalName: "clicked" + } + + function test_async_click() { + ... + // do something that will cause clicked() to be emitted + ... + spy.wait() + compare(spy.count, 1) + } + \endcode + + There are two possible scenarios: the signal has already been + emitted when wait() is called, or the signal has not yet been + emitted. The wait() function handles the first scenario by immediately + returning if the signal has already occurred. + + The clear() method can be used to discard information about signals + that have already occurred to synchronize wait() with future signal + emissions. + + \sa clear(), TestCase::tryCompare() +*/ diff --git a/src/imports/testlib/testcase.h b/src/imports/testlib/testcase.h new file mode 100644 index 0000000000..f7a1992290 --- /dev/null +++ b/src/imports/testlib/testcase.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTCASE_H +#define TESTCASE_H + +// This is a dummy header for defining the interface of "TestCase.qml" to qdoc. + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class TestCase : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(bool when READ when WRITE setWhen NOTIFY whenChanged) + Q_PROPERTY(bool optional READ optional WRITE setOptional NOTIFY optionalChanged) + Q_PROPERTY(bool completed READ completed NOTIFY completedChanged) + Q_PROPERTY(bool running READ running NOTIFY runningChanged) + Q_PROPERTY(bool windowShown READ windowShown NOTIFY windowShownChanged) +public: + TestCase(QDeclarativeItem *parent) : QDeclarativeItem(parent) {} + ~TestCase() + + QString name() const; + void setName(const QString &name); + + bool when() const; + void setWhen(bool when); + + bool optional() const; + void setOptional(bool optional); + + bool completed() const; + bool running() const; + bool windowShown() const; + +Q_SIGNALS: + void nameChanged(); + void whenChanged(); + void optionalChanged(); + void completedChanged(); + void runningChanged(); + void windowShownChanged(); +}; + +QML_DECLARE_TYPE(TestCase) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/imports/testlib/testcase.qdoc b/src/imports/testlib/testcase.qdoc new file mode 100644 index 0000000000..bb7044f907 --- /dev/null +++ b/src/imports/testlib/testcase.qdoc @@ -0,0 +1,597 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmlclass TestCase TestCase + \brief The TestCase item represents a unit test case. + \since 4.8 + \ingroup qtest::qml + + \section1 Introduction to QML test cases + + Test cases are written as JavaScript functions within a TestCase + element: + + \code + import QtQuick 1.0 + import QtQuickTest 1.0 + + 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 elements 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": + + \code + import QtQuick 1.0 + import QtQuickTest 1.0 + + TestCase { + name: "DataTests" + + 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) { + 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. + + \code + Rectangle { + width: 50; height: 50 + focus: true + + TestCase { + name: "KeyClick" + when: windowShown + + function test_key_click() { + keyClick(Qt.Key_Left) + ... + } + } + } + \endcode + + The mousePress(), mouseRelease(), mouseClick(), mouseDoubleClick(), + and mouseMove() methods can be used to simulate mouse events in a + similar fashion. + + \bold{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. + + \sa SignalSpy +*/ + +/*! + \qmlproperty string TestCase::name + + This property defines the name of the test case for result reporting. + The default is the empty string. + + \code + TestCase { + name: "ButtonTests" + ... + } + \endcode +*/ + +/*! + \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 elements + have been triggered and have run. The \l optional property can + be used to exclude a \l TestCase element. + + \sa optional, completed +*/ + +/*! + \qmlproperty bool TestCase::optional + + Multiple \l TestCase elements 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 +*/ + +/*! + \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 +*/ + +/*! + \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 +*/ + +/*! + \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 elements 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 +*/ + +/*! + \qmlmethod TestCase::fail(message = "") + + Fails the current test case, with the optional \a message. + Similar to \c{QFAIL(message)} in C++. +*/ + +/*! + \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++. +*/ + +/*! + \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() +*/ + +/*! + \qmlmethod TestCase::tryCompare(obj, property, expected, timeout = 5000) + + Fails the current test case if the specified \a property on \a obj + is not the same as \a expected. 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() +*/ + +/*! + \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, SkipSingle)} in C++. + + \sa skipAll() +*/ + +/*! + \qmlmethod TestCase::skipAll(message = "") + + Skips the current test case and prints the optional \a message. + If this is a data-driven test, then all remaining rows are skipped. + Similar to \c{QSKIP(message, SkipAll)} in C++. + + \sa 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 + the empty string. + + \sa expectFailContinue() +*/ + +/*! + \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 + the empty string. + + \sa expectFail() +*/ + +/*! + \qmlmethod TestCase::warn(message) + + Prints \a message as a warning message. Similar to + \c{QWARN(message)} in C++. + + \sa ignoreWarning() +*/ + +/*! + \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++. + + \sa warn() +*/ + +/*! + \qmlmethod TestCase::wait(ms) + + Waits for \a ms milliseconds while processing Qt events. + + \sa sleep() +*/ + +/*! + \qmlmethod TestCase::sleep(ms) + + Sleeps for \a ms milliseconds without processing Qt events. + + \sa wait() +*/ + +/*! + \qmlmethod TestCase::keyClick(key, modifiers = Qt.NoModifier, delay = -1) + + Simulates clicking of \a key with an optional \a modifier on the currently + focused item. If \a delay is larger than 0, the test will wait for + \a delay milliseconds. + + \sa keyPress(), keyRelease() +*/ + +/*! + \qmlmethod TestCase::keyPress(key, modifiers = Qt.NoModifier, delay = -1) + + Simulates pressing a \a key with an optional \a modifier on the currently + focused item. If \a delay is larger than 0, the test will wait for + \a delay milliseconds. + + \bold{Note:} At some point you should release the key using keyRelease(). + + \sa keyRelease(), keyClick() +*/ + +/*! + \qmlmethod TestCase::keyRelease(key, modifiers = Qt.NoModifier, delay = -1) + + Simulates releasing a \a key with an optional \a modifier on the currently + focused item. If \a delay is larger than 0, the test will wait for + \a delay milliseconds. + + \sa keyPress(), keyClick() +*/ + +/*! + \qmlmethod TestCase::mousePress(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) + + Simulates pressing a mouse \a button with an optional \a modifier + on an \a item. The position is defined by \a x and \a y. 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(), mouseDoubleClick(), mouseMove() +*/ + +/*! + \qmlmethod TestCase::mouseRelease(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) + + Simulates releasing a mouse \a button with an optional \a modifier + on an \a item. The position of the release 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. + + \sa mousePress(), mouseClick(), mouseDoubleClick(), mouseMove() +*/ + +/*! + \qmlmethod TestCase::mouseClick(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) + + Simulates clicking a mouse \a button with an optional \a modifier + on an \a item. The position of the click is defined by \a x and \a y. + 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(), mouseDoubleClick(), mouseMove() +*/ + +/*! + \qmlmethod TestCase::mouseDoubleClick(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) + + Simulates double-clicking a mouse \a button with an optional \a modifier + on an \a item. The position of the click is defined by \a x and \a y. + 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(), mouseClick(), mouseMove() +*/ + +/*! + \qmlmethod TestCase::mouseMove(item, x, y, delay = -1) + + Moves the mouse pointer to the position given by \a x and \a y within + \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(), mouseDoubleClick() +*/ + +/*! + \qmlmethod TestCase::initTestCase() + + This function is called before any other test functions in the + \l TestCase element. The default implementation does nothing. + The application can provide its own implementation to perform + test case initialization. + + \sa cleanupTestCase(), init() +*/ + +/*! + \qmlmethod TestCase::cleanupTestCase() + + This function is called after all other test functions in the + \l TestCase element have completed. The default implementation + does nothing. The application can provide its own implementation + to perform test case cleanup. + + \sa initTestCase(), cleanup() +*/ + +/*! + \qmlmethod TestCase::init() + + This function is called before each test function that is + executed in the \l TestCase element. The default implementation + does nothing. The application can provide its own implementation + to perform initialization before each test function. + + \sa cleanup(), initTestCase() +*/ + +/*! + \qmlmethod TestCase::cleanup() + + This function is called after each test function that is + executed in the \l TestCase element. The default implementation + does nothing. The application can provide its own implementation + to perform cleanup after each test function. + + \sa init(), cleanupTestCase() +*/ diff --git a/src/imports/testlib/testlib.pro b/src/imports/testlib/testlib.pro new file mode 100644 index 0000000000..cc29d53f51 --- /dev/null +++ b/src/imports/testlib/testlib.pro @@ -0,0 +1,29 @@ +TARGET = qmltestplugin +TARGETPATH = QtTest +include(../qimportbase.pri) + +CONFIG += qt plugin + +symbian { + CONFIG += epocallowdlldata + contains(QT_EDITION, OpenSource) { + TARGET.CAPABILITY = LocalServices NetworkServices ReadUserData UserEnvironment WriteUserData + } else { + TARGET.CAPABILITY = All -Tcb + } +} + +QT += declarative script qmltest qmltest-private + +SOURCES += main.cpp +HEADERS += + +qdeclarativesources.files += \ + qmldir \ + TestCase.qml \ + SignalSpy.qml \ + testlogger.js + +qdeclarativesources.path += $$[QT_INSTALL_IMPORTS]/QtTest +target.path += $$[QT_INSTALL_IMPORTS]/QtTest +INSTALLS += qdeclarativesources target diff --git a/src/imports/testlib/testlogger.js b/src/imports/testlib/testlogger.js new file mode 100644 index 0000000000..7fd33ca6f4 --- /dev/null +++ b/src/imports/testlib/testlogger.js @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +// We need a global place to store the results that can be +// shared between multiple TestCase instances. Because QML +// creates a separate scope for every inclusion of this file, +// we hijack the global "Qt" object to store our data. +function log_init_results() +{ + if (!Qt.testResults) { + Qt.testResults = { + reportedStart: false, + nextId: 0, + testCases: [] + } + } +} + +function log_register_test(name) +{ + log_init_results() + var testId = Qt.testResults.nextId++ + Qt.testResults.testCases.push(testId) + return testId +} + +function log_optional_test(testId) +{ + log_init_results() + var index = Qt.testResults.testCases.indexOf(testId) + if (index >= 0) + Qt.testResults.testCases.splice(index, 1) +} + +function log_mandatory_test(testId) +{ + log_init_results() + var index = Qt.testResults.testCases.indexOf(testId) + if (index == -1) + Qt.testResults.testCases.push(testId) +} + +function log_start_test() +{ + log_init_results() + if (Qt.testResults.reportedStart) + return false + Qt.testResults.reportedStart = true + return true +} + +function log_complete_test(testId) +{ + var index = Qt.testResults.testCases.indexOf(testId) + if (index >= 0) + Qt.testResults.testCases.splice(index, 1) + return Qt.testResults.testCases.length > 0 +} diff --git a/src/qmltest/features/qmltestcase.prf b/src/qmltest/features/qmltestcase.prf new file mode 100644 index 0000000000..a3d66e659e --- /dev/null +++ b/src/qmltest/features/qmltestcase.prf @@ -0,0 +1,25 @@ +CONFIG += testcase + +!symbian { + INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtQuickTest +} else { + load(data_caging_paths) + + INCLUDEPATH+=$$MW_LAYER_PUBLIC_EXPORT_PATH(QtQuickTest) +} + +QT += declarative + +win32:CONFIG(debug, debug|release) { + LIBS += -lQtQuickTest$${QT_LIBINFIX}d +} else { + LIBS += -lQtQuickTest$${QT_LIBINFIX} +} + +# If the .pro file specified an IMPORTPATH, then add that to +# the command-line when the test is run. +!isEmpty(IMPORTPATH) { + load(testcase) + for(import, IMPORTPATH): check.commands += -import \"$$import\" +} +DEFINES += QUICK_TEST_SOURCE_DIR=\"\\\"$$OUT_PWD\\\"\" diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro new file mode 100644 index 0000000000..de0dbe2ef8 --- /dev/null +++ b/src/qmltest/qmltest.pro @@ -0,0 +1,48 @@ +load(qt_module) + +TARGET = QtQuickTest +QPRO_PWD = $$PWD + +CONFIG += module +CONFIG += dll warn_on +MODULE_PRI += ../../modules/qt_qmltest.pri + +QT = testlib-private declarative script testlib +DEFINES += QT_BUILD_DECLARATIVE_LIB QT_NO_URL_CAST_FROM_STRING + + +include($$QT_SOURCE_TREE/src/qbase.pri) + +# Install qmltestcase.prf into the Qt mkspecs so that "CONFIG += qmltestcase" +# can be used in customer applications to build against QtQuickTest. +feature.path = $$[QT_INSTALL_DATA]/mkspecs/features +feature.files = $$PWD/features/qmltestcase.prf +INSTALLS += feature + +symbian { + DEFINES += QT_MAKEDLL + CONFIG += epocallowdlldata + contains(QT_EDITION, OpenSource) { + TARGET.CAPABILITY = LocalServices NetworkServices ReadUserData UserEnvironment WriteUserData + } else { + TARGET.CAPABILITY = All -Tcb + } +} + +INCLUDEPATH += $$PWD/QtQuickTest +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/quicktest.cpp \ + $$PWD/quicktestevent.cpp \ + $$PWD/quicktestresult.cpp +HEADERS += \ + $$PWD/quicktestglobal.h \ + $$PWD/quicktest.h \ + $$PWD/quicktestevent_p.h \ + $$PWD/quicktestresult_p.h \ + $$PWD/qtestoptions_p.h + + +DEFINES += QT_BUILD_QUICK_TEST_LIB + diff --git a/src/qmltest/qtestoptions_p.h b/src/qmltest/qtestoptions_p.h new file mode 100644 index 0000000000..167f906645 --- /dev/null +++ b/src/qmltest/qtestoptions_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTOPTIONS_P_H +#define QTESTOPTIONS_P_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest +{ + extern Q_TESTLIB_EXPORT bool printAvailableFunctions; + extern Q_TESTLIB_EXPORT QStringList testFunctions; + extern Q_TESTLIB_EXPORT QStringList testTags; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp new file mode 100644 index 0000000000..19e15f2e5f --- /dev/null +++ b/src/qmltest/quicktest.cpp @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "quicktest.h" +#include "quicktestresult_p.h" +#include +#include "qtestoptions_p.h" +#include +#include +#include +#include +#include +#if defined(QML_VERSION) && QML_VERSION >= 0x020000 +#include +#define QUICK_TEST_SCENEGRAPH 1 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Copied from qdeclarativedebughelper_p.h in Qt, to avoid a dependency +// on a private header from Qt. +class Q_DECLARATIVE_EXPORT QDeclarativeDebugHelper +{ +public: + static QScriptEngine *getScriptEngine(QDeclarativeEngine *engine); + static void setAnimationSlowDownFactor(qreal factor); + static void enableDebugging(); +}; + +class QTestRootObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool windowShown READ windowShown NOTIFY windowShownChanged) +public: + QTestRootObject(QObject *parent = 0) + : QObject(parent), hasQuit(false), m_windowShown(false) {} + + bool hasQuit; + + bool windowShown() const { return m_windowShown; } + void setWindowShown(bool value) { m_windowShown = value; emit windowShownChanged(); } + +Q_SIGNALS: + void windowShownChanged(); + +private Q_SLOTS: + void quit() { hasQuit = true; } + +private: + bool m_windowShown; +}; + +static inline QString stripQuotes(const QString &s) +{ + if (s.length() >= 2 && s.startsWith(QLatin1Char('"')) && s.endsWith(QLatin1Char('"'))) + return s.mid(1, s.length() - 2); + else + return s; +} + +int quick_test_main(int argc, char **argv, const char *name, quick_test_viewport_create createViewport, const char *sourceDir) +{ + QApplication app(argc, argv); + + // Look for QML-specific command-line options. + // -import dir Specify an import directory. + // -input dir Specify the input directory for test cases. + // -qtquick1 Run with QtQuick 1 rather than QtQuick 2. + QStringList imports; + QString testPath; + bool qtQuick2 = true; + int outargc = 1; + int index = 1; + while (index < argc) { + if (strcmp(argv[index], "-import") == 0 && (index + 1) < argc) { + imports += stripQuotes(QString::fromLocal8Bit(argv[index + 1])); + index += 2; + } else if (strcmp(argv[index], "-input") == 0 && (index + 1) < argc) { + testPath = stripQuotes(QString::fromLocal8Bit(argv[index + 1])); + index += 2; + } else if (strcmp(argv[index], "-opengl") == 0) { + ++index; + } else if (strcmp(argv[index], "-qtquick1") == 0) { + qtQuick2 = false; + ++index; + } else if (outargc != index) { + argv[outargc++] = argv[index++]; + } else { + ++outargc; + ++index; + } + } + argv[outargc] = 0; + argc = outargc; + + // Determine where to look for the test data. + if (testPath.isEmpty() && sourceDir) + testPath = QString::fromLocal8Bit(sourceDir); + if (testPath.isEmpty()) + testPath = QLatin1String("."); + + // Scan the test data directory recursively, looking for "tst_*.qml" files. + QStringList filters; + filters += QLatin1String("tst_*.qml"); + QStringList files; + QDirIterator iter(testPath, filters, QDir::Files, + QDirIterator::Subdirectories | + QDirIterator::FollowSymlinks); + while (iter.hasNext()) + files += iter.next(); + files.sort(); + + // Bail out if we didn't find any test cases. + if (files.isEmpty()) { + qWarning() << argv[0] << ": could not find any test cases under" + << testPath; + return 1; + } + + // Parse the command-line arguments. + QuickTestResult::parseArgs(argc, argv); + QuickTestResult::setProgramName(name); + + // Scan through all of the "tst_*.qml" files and run each of them + // in turn with a QDeclarativeView. +#ifdef QUICK_TEST_SCENEGRAPH + if (qtQuick2) { + foreach (QString file, files) { + QFileInfo fi(file); + if (!fi.exists()) + continue; + QSGView view; + QTestRootObject rootobj; + QEventLoop eventLoop; + QObject::connect(view.engine(), SIGNAL(quit()), + &rootobj, SLOT(quit())); + QObject::connect(view.engine(), SIGNAL(quit()), + &eventLoop, SLOT(quit())); + view.rootContext()->setContextProperty + (QLatin1String("qtest"), &rootobj); + QScriptEngine *engine; + engine = QDeclarativeDebugHelper::getScriptEngine(view.engine()); + QScriptValue qtObject + = engine->globalObject().property(QLatin1String("Qt")); + qtObject.setProperty + (QLatin1String("qtest_wrapper"), QScriptValue(true)); + qtObject.setProperty + (QLatin1String("qtest_printAvailableFunctions"), + QScriptValue(QTest::printAvailableFunctions)); + foreach (QString path, imports) + view.engine()->addImportPath(path); + QString path = fi.absoluteFilePath(); + if (path.startsWith(QLatin1String(":/"))) + view.setSource(QUrl(QLatin1String("qrc:") + path.mid(2))); + else + view.setSource(QUrl::fromLocalFile(path)); + if (QTest::printAvailableFunctions) + continue; + if (view.status() == QSGView::Error) { + // Error compiling the test - flag failure in the log and continue. + QList errors = view.errors(); + QuickTestResult results; + results.setTestCaseName(fi.baseName()); + results.startLogging(); + results.setFunctionName(QLatin1String("compile")); + results.setFunctionType(QuickTestResult::Func); + results.fail(errors.at(0).description(), + errors.at(0).url().toString(), + errors.at(0).line()); + results.finishTestFunction(); + results.setFunctionName(QString()); + results.setFunctionType(QuickTestResult::NoWhere); + results.stopLogging(); + continue; + } + if (!rootobj.hasQuit) { + // If the test already quit, then it was performed + // synchronously during setSource(). Otherwise it is + // an asynchronous test and we need to show the window + // and wait for the quit indication. + view.show(); + QTest::qWaitForWindowShown(&view); + rootobj.setWindowShown(true); + if (!rootobj.hasQuit) + eventLoop.exec(); + } + } + } else +#endif + { + foreach (QString file, files) { + QFileInfo fi(file); + if (!fi.exists()) + continue; + QDeclarativeView view; + QTestRootObject rootobj; + QEventLoop eventLoop; + QObject::connect(view.engine(), SIGNAL(quit()), + &rootobj, SLOT(quit())); + QObject::connect(view.engine(), SIGNAL(quit()), + &eventLoop, SLOT(quit())); + if (createViewport) + view.setViewport((*createViewport)()); + view.rootContext()->setContextProperty + (QLatin1String("qtest"), &rootobj); + QScriptEngine *engine; + engine = QDeclarativeDebugHelper::getScriptEngine(view.engine()); + QScriptValue qtObject + = engine->globalObject().property(QLatin1String("Qt")); + qtObject.setProperty + (QLatin1String("qtest_wrapper"), QScriptValue(true)); + qtObject.setProperty + (QLatin1String("qtest_printAvailableFunctions"), + QScriptValue(QTest::printAvailableFunctions)); + foreach (QString path, imports) + view.engine()->addImportPath(path); + QString path = fi.absoluteFilePath(); + if (path.startsWith(QLatin1String(":/"))) + view.setSource(QUrl(QLatin1String("qrc:") + path.mid(2))); + else + view.setSource(QUrl::fromLocalFile(path)); + if (QTest::printAvailableFunctions) + continue; + if (view.status() == QDeclarativeView::Error) { + // Error compiling the test - flag failure in the log and continue. + QList errors = view.errors(); + QuickTestResult results; + results.setTestCaseName(fi.baseName()); + results.startLogging(); + results.setFunctionName(QLatin1String("compile")); + results.setFunctionType(QuickTestResult::Func); + results.fail(errors.at(0).description(), + errors.at(0).url().toString(), + errors.at(0).line()); + results.finishTestFunction(); + results.setFunctionName(QString()); + results.setFunctionType(QuickTestResult::NoWhere); + results.stopLogging(); + continue; + } + if (!rootobj.hasQuit) { + // If the test already quit, then it was performed + // synchronously during setSource(). Otherwise it is + // an asynchronous test and we need to show the window + // and wait for the quit indication. + view.show(); + QTest::qWaitForWindowShown(&view); + rootobj.setWindowShown(true); + if (!rootobj.hasQuit) + eventLoop.exec(); + } + } + } + + // Flush the current logging stream. + QuickTestResult::setProgramName(0); + + // Return the number of failures as the exit code. + return QuickTestResult::exitCode(); +} + +QT_END_NAMESPACE + +#include "quicktest.moc" diff --git a/src/qmltest/quicktest.h b/src/qmltest/quicktest.h new file mode 100644 index 0000000000..60c25ec3fb --- /dev/null +++ b/src/qmltest/quicktest.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUICKTEST_H +#define QUICKTEST_H + +#include +#include +#ifdef QT_OPENGL_LIB +#include +#endif + +QT_BEGIN_NAMESPACE + +typedef QWidget *(*quick_test_viewport_create)(); + +Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, quick_test_viewport_create createViewport, const char *sourceDir); + +#ifdef QUICK_TEST_SOURCE_DIR + +#define QUICK_TEST_MAIN(name) \ + int main(int argc, char **argv) \ + { \ + return quick_test_main(argc, argv, #name, 0, QUICK_TEST_SOURCE_DIR); \ + } + +#define QUICK_TEST_OPENGL_MAIN(name) \ + static QWidget *name##_create_viewport() \ + { \ + return new QGLWidget(); \ + } \ + int main(int argc, char **argv) \ + { \ + return quick_test_main(argc, argv, #name, name##_create_viewport, QUICK_TEST_SOURCE_DIR); \ + } + +#else + +#define QUICK_TEST_MAIN(name) \ + int main(int argc, char **argv) \ + { \ + return quick_test_main(argc, argv, #name, 0, 0); \ + } + +#define QUICK_TEST_OPENGL_MAIN(name) \ + static QWidget *name##_create_viewport() \ + { \ + return new QGLWidget(); \ + } \ + int main(int argc, char **argv) \ + { \ + return quick_test_main(argc, argv, #name, name##_create_viewport, 0); \ + } + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp new file mode 100644 index 0000000000..894e29efc3 --- /dev/null +++ b/src/qmltest/quicktestevent.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "quicktestevent_p.h" +#include +#include +#include +#include +#if defined(QML_VERSION) && QML_VERSION >= 0x020000 +#include +#include +#define QUICK_TEST_SCENEGRAPH 1 +#endif +#include + +QT_BEGIN_NAMESPACE + +QuickTestEvent::QuickTestEvent(QObject *parent) + : QObject(parent) +{ +} + +QuickTestEvent::~QuickTestEvent() +{ +} + +bool QuickTestEvent::keyPress(int key, int modifiers, int delay) +{ + QWidget *widget = eventWidget(); + if (!widget) + return false; + QTest::keyPress(widget, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); + return true; +} + +bool QuickTestEvent::keyRelease(int key, int modifiers, int delay) +{ + QWidget *widget = eventWidget(); + if (!widget) + return false; + QTest::keyRelease(widget, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); + return true; +} + +bool QuickTestEvent::keyClick(int key, int modifiers, int delay) +{ + QWidget *widget = eventWidget(); + if (!widget) + return false; + QTest::keyClick(widget, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); + return true; +} + +namespace QTest { + extern int Q_TESTLIB_EXPORT defaultMouseDelay(); +}; + +namespace QtQuickTest +{ + enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove }; + + static void mouseEvent(MouseAction action, QWidget *widget, + QObject *item, Qt::MouseButton button, + Qt::KeyboardModifiers stateKey, QPointF _pos, int delay=-1) + { + QTEST_ASSERT(widget); + QTEST_ASSERT(item); + + if (delay == -1 || delay < QTest::defaultMouseDelay()) + delay = QTest::defaultMouseDelay(); + if (delay > 0) + QTest::qWait(delay); + + if (action == MouseClick) { + mouseEvent(MousePress, widget, item, button, stateKey, _pos); + mouseEvent(MouseRelease, widget, item, button, stateKey, _pos); + return; + } + + QPoint pos; + QDeclarativeView *view = qobject_cast(widget); + QWidget *eventWidget = widget; +#ifdef QUICK_TEST_SCENEGRAPH + QSGItem *sgitem = qobject_cast(item); + if (sgitem) { + pos = sgitem->mapToScene(_pos).toPoint(); + } else +#endif + { + QDeclarativeItem *ditem = qobject_cast(item); + if (!ditem) { + qWarning("Mouse event target is not an Item"); + return; + } + pos = view->mapFromScene(ditem->mapToScene(_pos)); + eventWidget = view->viewport(); + } + + QTEST_ASSERT(button == Qt::NoButton || button & Qt::MouseButtonMask); + QTEST_ASSERT(stateKey == 0 || stateKey & Qt::KeyboardModifierMask); + + stateKey &= static_cast(Qt::KeyboardModifierMask); + + QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, button, stateKey); + switch (action) + { + case MousePress: + me = QMouseEvent(QEvent::MouseButtonPress, pos, widget->mapToGlobal(pos), button, button, stateKey); + break; + case MouseRelease: + me = QMouseEvent(QEvent::MouseButtonRelease, pos, widget->mapToGlobal(pos), button, 0, stateKey); + break; + case MouseDoubleClick: + me = QMouseEvent(QEvent::MouseButtonDblClick, pos, widget->mapToGlobal(pos), button, button, stateKey); + break; + case MouseMove: + QCursor::setPos(widget->mapToGlobal(pos)); + qApp->processEvents(); + return; + default: + QTEST_ASSERT(false); + } + QSpontaneKeyEvent::setSpontaneous(&me); + if (!qApp->notify(eventWidget, &me)) { + static const char *mouseActionNames[] = + { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; + QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving widget"); + QTest::qWarn(warning.arg(QString::fromLatin1(mouseActionNames[static_cast(action)])).toAscii().data()); + } + } +}; + +bool QuickTestEvent::mousePress + (QObject *item, qreal x, qreal y, int button, + int modifiers, int delay) +{ + QWidget *view = eventWidget(); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MousePress, view, item, + Qt::MouseButton(button), + Qt::KeyboardModifiers(modifiers), + QPointF(x, y), delay); + return true; +} + +bool QuickTestEvent::mouseRelease + (QObject *item, qreal x, qreal y, int button, + int modifiers, int delay) +{ + QWidget *view = eventWidget(); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MouseRelease, view, item, + Qt::MouseButton(button), + Qt::KeyboardModifiers(modifiers), + QPointF(x, y), delay); + return true; +} + +bool QuickTestEvent::mouseClick + (QObject *item, qreal x, qreal y, int button, + int modifiers, int delay) +{ + QWidget *view = eventWidget(); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MouseClick, view, item, + Qt::MouseButton(button), + Qt::KeyboardModifiers(modifiers), + QPointF(x, y), delay); + return true; +} + +bool QuickTestEvent::mouseDoubleClick + (QObject *item, qreal x, qreal y, int button, + int modifiers, int delay) +{ + QWidget *view = eventWidget(); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MouseDoubleClick, view, item, + Qt::MouseButton(button), + Qt::KeyboardModifiers(modifiers), + QPointF(x, y), delay); + return true; +} + +bool QuickTestEvent::mouseMove + (QObject *item, qreal x, qreal y, int delay) +{ + QWidget *view = eventWidget(); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MouseMove, view, item, + Qt::NoButton, Qt::NoModifier, + QPointF(x, y), delay); + return true; +} + +QWidget *QuickTestEvent::eventWidget() +{ +#ifdef QUICK_TEST_SCENEGRAPH + QSGItem *sgitem = qobject_cast(parent()); + if (sgitem) + return sgitem->canvas(); +#endif + QDeclarativeItem *item = qobject_cast(parent()); + if (!item) + return 0; + QGraphicsScene *s = item->scene(); + if (!s) + return 0; + QList views = s->views(); + if (views.isEmpty()) + return 0; + return views.at(0); +} + +QT_END_NAMESPACE diff --git a/src/qmltest/quicktestevent_p.h b/src/qmltest/quicktestevent_p.h new file mode 100644 index 0000000000..1f5926f051 --- /dev/null +++ b/src/qmltest/quicktestevent_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUICKTESTEVENT_P_H +#define QUICKTESTEVENT_P_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_TEST_EXPORT QuickTestEvent : public QObject +{ + Q_OBJECT +public: + QuickTestEvent(QObject *parent = 0); + ~QuickTestEvent(); + +public Q_SLOTS: + bool keyPress(int key, int modifiers, int delay); + bool keyRelease(int key, int modifiers, int delay); + bool keyClick(int key, int modifiers, int delay); + + bool mousePress(QObject *item, qreal x, qreal y, int button, + int modifiers, int delay); + bool mouseRelease(QObject *item, qreal x, qreal y, int button, + int modifiers, int delay); + bool mouseClick(QObject *item, qreal x, qreal y, int button, + int modifiers, int delay); + bool mouseDoubleClick(QObject *item, qreal x, qreal y, int button, + int modifiers, int delay); + bool mouseMove(QObject *item, qreal x, qreal y, int delay); + +private: + QWidget *eventWidget(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qmltest/quicktestglobal.h b/src/qmltest/quicktestglobal.h new file mode 100644 index 0000000000..0c71b5ce8e --- /dev/null +++ b/src/qmltest/quicktestglobal.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTQUICKGLOBAL_H +#define TESTQUICKGLOBAL_H + +#include + +QT_LICENSED_MODULE(QtQuickTest) +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) +# if defined(QT_NODLL) +# undef QT_MAKEDLL +# undef QT_DLL +# elif defined(QT_MAKEDLL) /* create a Qt DLL library */ +# if defined(QT_DLL) +# undef QT_DLL +# endif +# if defined(QT_BUILD_QUICK_TEST_LIB) +# define Q_QUICK_TEST_EXPORT Q_DECL_EXPORT +# else +# define Q_QUICK_TEST_EXPORT Q_DECL_IMPORT +# endif +# elif defined(QT_DLL) /* use a Qt DLL library */ +# define Q_QUICK_TEST_EXPORT Q_DECL_IMPORT +# endif +#endif +#if !defined(Q_QUICK_TEST_EXPORT) +# if defined(QT_SHARED) +# define Q_QUICK_TEST_EXPORT Q_DECL_EXPORT +# else +# define Q_QUICK_TEST_EXPORT +# endif +#endif + +#endif diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp new file mode 100644 index 0000000000..59fea965e8 --- /dev/null +++ b/src/qmltest/quicktestresult.cpp @@ -0,0 +1,630 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "quicktestresult_p.h" +#include +#include +#include +#include +#include +#include "qtestoptions_p.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *globalProgramName = 0; +static bool loggingStarted = false; +static QBenchmarkGlobalData globalBenchmarkData; + +class QuickTestResultPrivate +{ +public: + QuickTestResultPrivate() + : table(0) + , benchmarkIter(0) + , benchmarkData(0) + , iterCount(0) + { + } + ~QuickTestResultPrivate() + { + delete table; + delete benchmarkIter; + delete benchmarkData; + } + + QByteArray intern(const QString &str); + void updateTestObjectName(); + + QString testCaseName; + QString functionName; + QSet internedStrings; + QTestTable *table; + QTest::QBenchmarkIterationController *benchmarkIter; + QBenchmarkTestMethodData *benchmarkData; + int iterCount; + QList results; +}; + +QByteArray QuickTestResultPrivate::intern(const QString &str) +{ + QByteArray bstr = str.toUtf8(); + return *(internedStrings.insert(bstr)); +} + +void QuickTestResultPrivate::updateTestObjectName() +{ + // In plain logging mode we use the TestCase name as the + // class name so that multiple TestCase elements will report + // results with "testCase::function". In XML logging mode, + // we use the program name as the class name and report test + // functions as "testCase__function". + if (QTestLog::logMode() == QTestLog::Plain) { + if (testCaseName.isEmpty()) { + QTestResult::setCurrentTestObject(globalProgramName); + } else if (QTestLog::logMode() == QTestLog::Plain) { + QTestResult::setCurrentTestObject + (intern(testCaseName).constData()); + } + } else { + QTestResult::setCurrentTestObject(globalProgramName); + } +} + +QuickTestResult::QuickTestResult(QObject *parent) + : QObject(parent), d_ptr(new QuickTestResultPrivate) +{ + if (!QBenchmarkGlobalData::current) + QBenchmarkGlobalData::current = &globalBenchmarkData; +} + +QuickTestResult::~QuickTestResult() +{ +} + +/*! + \qmlproperty string TestResult::testCaseName + + This property defines the name of current TestCase element + that is running test cases. + + \sa functionName +*/ +QString QuickTestResult::testCaseName() const +{ + Q_D(const QuickTestResult); + return d->testCaseName; +} + +void QuickTestResult::setTestCaseName(const QString &name) +{ + Q_D(QuickTestResult); + d->testCaseName = name; + d->updateTestObjectName(); + emit testCaseNameChanged(); +} + +/*! + \qmlproperty string TestResult::functionName + + This property defines the name of current test function + within a TestCase element that is running. If this string is + empty, then no function is currently running. + + \sa testCaseName +*/ +QString QuickTestResult::functionName() const +{ + Q_D(const QuickTestResult); + return d->functionName; +} + +void QuickTestResult::setFunctionName(const QString &name) +{ + Q_D(QuickTestResult); + if (!name.isEmpty()) { + // In plain logging mode, we use the function name directly. + // In XML logging mode, we use "testCase__functionName" as the + // program name is acting as the class name. + if (QTestLog::logMode() == QTestLog::Plain || + d->testCaseName.isEmpty()) { + QTestResult::setCurrentTestFunction + (d->intern(name).constData()); + } else { + QString fullName = d->testCaseName + QLatin1String("__") + name; + QTestResult::setCurrentTestFunction + (d->intern(fullName).constData()); + } + } else { + QTestResult::setCurrentTestFunction(0); + } + d->functionName = name; + emit functionNameChanged(); +} + +QuickTestResult::FunctionType QuickTestResult::functionType() const +{ + return FunctionType(QTestResult::currentTestLocation()); +} + +void QuickTestResult::setFunctionType(FunctionType type) +{ + QTestResult::setCurrentTestLocation(QTestResult::TestLocation(type)); + emit functionTypeChanged(); +} + +/*! + \qmlproperty string TestResult::dataTag + + This property defines the tag for the current row in a + data-driven test, or an empty string if not a data-driven test. +*/ +QString QuickTestResult::dataTag() const +{ + const char *tag = QTestResult::currentDataTag(); + if (tag) + return QString::fromUtf8(tag); + else + return QString(); +} + +void QuickTestResult::setDataTag(const QString &tag) +{ + if (!tag.isEmpty()) { + QTestData *data = &(QTest::newRow(tag.toUtf8().constData())); + QTestResult::setCurrentTestData(data); + emit dataTagChanged(); + } else { + QTestResult::setCurrentTestData(0); + } +} + +/*! + \qmlproperty bool TestResult::failed + + This property returns true if the current test function has + failed; false otherwise. The fail state is reset when + functionName is changed or finishTestFunction() is called. + + \sa skipped, dataFailed +*/ +bool QuickTestResult::isFailed() const +{ + return QTestResult::testFailed(); +} + +/*! + \qmlproperty bool TestResult::dataFailed + + This property returns true if the current data function has + failed; false otherwise. The fail state is reset when + functionName is changed or finishTestFunction() is called. + + \sa failed +*/ +bool QuickTestResult::isDataFailed() const +{ + return QTestResult::currentTestFailed(); +} + +/*! + \qmlproperty bool TestResult::skipped + + This property returns true if the current test function was + marked as skipped; false otherwise. + + \sa failed +*/ +bool QuickTestResult::isSkipped() const +{ + return QTestResult::skipCurrentTest(); +} + +void QuickTestResult::setSkipped(bool skip) +{ + QTestResult::setSkipCurrentTest(skip); + emit skippedChanged(); +} + +/*! + \qmlproperty int TestResult::passCount + + This property returns the number of tests that have passed. + + \sa failCount, skipCount +*/ +int QuickTestResult::passCount() const +{ + return QTestResult::passCount(); +} + +/*! + \qmlproperty int TestResult::failCount + + This property returns the number of tests that have failed. + + \sa passCount, skipCount +*/ +int QuickTestResult::failCount() const +{ + return QTestResult::failCount(); +} + +/*! + \qmlproperty int TestResult::skipCount + + This property returns the number of tests that have been skipped. + + \sa passCount, failCount +*/ +int QuickTestResult::skipCount() const +{ + return QTestResult::skipCount(); +} + +/*! + \qmlproperty list TestResult::functionsToRun + + This property returns the list of function names to be run. +*/ +QStringList QuickTestResult::functionsToRun() const +{ + return QTest::testFunctions; +} + +/*! + \qmlmethod TestResult::reset() + + Resets all pass/fail/skip counters and prepare for testing. +*/ +void QuickTestResult::reset() +{ + if (!globalProgramName) // Only if run via qmlviewer. + QTestResult::reset(); +} + +/*! + \qmlmethod TestResult::startLogging() + + Starts logging to the test output stream and writes the + test header. + + \sa stopLogging() +*/ +void QuickTestResult::startLogging() +{ + // The program name is used for logging headers and footers if it + // is set. Otherwise the test case name is used. + Q_D(QuickTestResult); + if (loggingStarted) + return; + const char *saved = QTestResult::currentTestObjectName(); + if (globalProgramName) { + QTestResult::setCurrentTestObject(globalProgramName); + } else { + QTestResult::setCurrentTestObject + (d->intern(d->testCaseName).constData()); + } + QTestLog::startLogging(); + QTestResult::setCurrentTestObject(saved); + loggingStarted = true; +} + +/*! + \qmlmethod TestResult::stopLogging() + + Writes the test footer to the test output stream and then stops logging. + + \sa startLogging() +*/ +void QuickTestResult::stopLogging() +{ + Q_D(QuickTestResult); + if (globalProgramName) + return; // Logging will be stopped by setProgramName(0). + const char *saved = QTestResult::currentTestObjectName(); + QTestResult::setCurrentTestObject(d->intern(d->testCaseName).constData()); + QTestLog::stopLogging(); + QTestResult::setCurrentTestObject(saved); +} + +void QuickTestResult::initTestTable() +{ + Q_D(QuickTestResult); + delete d->table; + d->table = new QTestTable; +} + +void QuickTestResult::clearTestTable() +{ + Q_D(QuickTestResult); + delete d->table; + d->table = 0; +} + +void QuickTestResult::finishTestFunction() +{ + QTestResult::finishedCurrentTestFunction(); +} + +static QString qtest_fixFile(const QString &file) +{ + if (file.startsWith(QLatin1String("file://"))) + return file.mid(7); + else + return file; +} + +void QuickTestResult::fail + (const QString &message, const QString &file, int line) +{ + QTestResult::addFailure(message.toLatin1().constData(), + qtest_fixFile(file).toLatin1().constData(), line); +} + +bool QuickTestResult::verify + (bool success, const QString &message, const QString &file, int line) +{ + if (!success && message.isEmpty()) { + return QTestResult::verify + (success, "verify()", "", + qtest_fixFile(file).toLatin1().constData(), line); + } else { + return QTestResult::verify + (success, message.toLatin1().constData(), "", + qtest_fixFile(file).toLatin1().constData(), line); + } +} + +bool QuickTestResult::compare + (bool success, const QString &message, + const QString &val1, const QString &val2, + const QString &file, int line) +{ + if (success) { + return QTestResult::compare + (success, message.toLocal8Bit().constData(), + qtest_fixFile(file).toLatin1().constData(), line); + } else { + return QTestResult::compare + (success, message.toLocal8Bit().constData(), + QTest::toString(val1.toLatin1().constData()), + QTest::toString(val2.toLatin1().constData()), + "", "", + qtest_fixFile(file).toLatin1().constData(), line); + } +} + +void QuickTestResult::skipSingle + (const QString &message, const QString &file, int line) +{ + QTestResult::addSkip(message.toLatin1().constData(), QTest::SkipSingle, + qtest_fixFile(file).toLatin1().constData(), line); +} + +void QuickTestResult::skipAll + (const QString &message, const QString &file, int line) +{ + QTestResult::addSkip(message.toLatin1().constData(), QTest::SkipAll, + qtest_fixFile(file).toLatin1().constData(), line); + QTestResult::setSkipCurrentTest(true); +} + +bool QuickTestResult::expectFail + (const QString &tag, const QString &comment, const QString &file, int line) +{ + return QTestResult::expectFail + (tag.toLatin1().constData(), + QTest::toString(comment.toLatin1().constData()), + QTest::Abort, qtest_fixFile(file).toLatin1().constData(), line); +} + +bool QuickTestResult::expectFailContinue + (const QString &tag, const QString &comment, const QString &file, int line) +{ + return QTestResult::expectFail + (tag.toLatin1().constData(), + QTest::toString(comment.toLatin1().constData()), + QTest::Continue, qtest_fixFile(file).toLatin1().constData(), line); +} + +void QuickTestResult::warn(const QString &message) +{ + QTestLog::warn(message.toLatin1().constData()); +} + +void QuickTestResult::ignoreWarning(const QString &message) +{ + QTestResult::ignoreMessage(QtWarningMsg, message.toLatin1().constData()); +} + +void QuickTestResult::wait(int ms) +{ + QTest::qWait(ms); +} + +void QuickTestResult::sleep(int ms) +{ + QTest::qSleep(ms); +} + +void QuickTestResult::startMeasurement() +{ + Q_D(QuickTestResult); + delete d->benchmarkData; + d->benchmarkData = new QBenchmarkTestMethodData(); + QBenchmarkTestMethodData::current = d->benchmarkData; + d->iterCount = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0; + d->results.clear(); +} + +void QuickTestResult::beginDataRun() +{ + QBenchmarkTestMethodData::current->beginDataRun(); +} + +void QuickTestResult::endDataRun() +{ + Q_D(QuickTestResult); + QBenchmarkTestMethodData::current->endDataRun(); + if (d->iterCount > -1) // iteration -1 is the warmup iteration. + d->results.append(QBenchmarkTestMethodData::current->result); + + if (QBenchmarkGlobalData::current->verboseOutput) { + if (d->iterCount == -1) { + qDebug() << "warmup stage result :" << QBenchmarkTestMethodData::current->result.value; + } else { + qDebug() << "accumulation stage result:" << QBenchmarkTestMethodData::current->result.value; + } + } +} + +bool QuickTestResult::measurementAccepted() +{ + return QBenchmarkTestMethodData::current->resultsAccepted(); +} + +static QBenchmarkResult qMedian(const QList &container) +{ + const int count = container.count(); + if (count == 0) + return QBenchmarkResult(); + + if (count == 1) + return container.at(0); + + QList containerCopy = container; + qSort(containerCopy); + + const int middle = count / 2; + + // ### handle even-sized containers here by doing an aritmetic mean of the two middle items. + return containerCopy.at(middle); +} + +bool QuickTestResult::needsMoreMeasurements() +{ + Q_D(QuickTestResult); + ++(d->iterCount); + if (d->iterCount < QBenchmarkGlobalData::current->adjustMedianIterationCount()) + return true; + if (QBenchmarkTestMethodData::current->resultsAccepted()) + QTestLog::addBenchmarkResult(qMedian(d->results)); + return false; +} + +void QuickTestResult::startBenchmark(RunMode runMode, const QString &tag) +{ + QBenchmarkTestMethodData::current->result = QBenchmarkResult(); + QBenchmarkTestMethodData::current->resultAccepted = false; + QBenchmarkGlobalData::current->context.tag = tag; + QBenchmarkGlobalData::current->context.slotName = functionName(); + + Q_D(QuickTestResult); + delete d->benchmarkIter; + d->benchmarkIter = new QTest::QBenchmarkIterationController + (QTest::QBenchmarkIterationController::RunMode(runMode)); +} + +bool QuickTestResult::isBenchmarkDone() const +{ + Q_D(const QuickTestResult); + if (d->benchmarkIter) + return d->benchmarkIter->isDone(); + else + return true; +} + +void QuickTestResult::nextBenchmark() +{ + Q_D(QuickTestResult); + if (d->benchmarkIter) + d->benchmarkIter->next(); +} + +void QuickTestResult::stopBenchmark() +{ + Q_D(QuickTestResult); + delete d->benchmarkIter; + d->benchmarkIter = 0; +} + +namespace QTest { + void qtest_qParseArgs(int argc, char *argv[], bool qml); +}; + +void QuickTestResult::parseArgs(int argc, char *argv[]) +{ + if (!QBenchmarkGlobalData::current) + QBenchmarkGlobalData::current = &globalBenchmarkData; + QTest::qtest_qParseArgs(argc, argv, true); +} + +void QuickTestResult::setProgramName(const char *name) +{ + if (name) { + QTestResult::reset(); + } else if (!name && loggingStarted) { + QTestResult::setCurrentTestObject(globalProgramName); + QTestLog::stopLogging(); + QTestResult::setCurrentTestObject(0); + } + globalProgramName = name; +} + +int QuickTestResult::exitCode() +{ +#if defined(QTEST_NOEXITCODE) + return 0; +#else + // make sure our exit code is never going above 127 + // since that could wrap and indicate 0 test fails + return qMin(QTestResult::failCount(), 127); +#endif +} + +QT_END_NAMESPACE diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h new file mode 100644 index 0000000000..d9ae694f10 --- /dev/null +++ b/src/qmltest/quicktestresult_p.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUICKTESTRESULT_P_H +#define QUICKTESTRESULT_P_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QuickTestResultPrivate; + +class Q_QUICK_TEST_EXPORT QuickTestResult : public QObject +{ + Q_OBJECT + Q_ENUMS(FunctionType RunMode) + Q_PROPERTY(QString testCaseName READ testCaseName WRITE setTestCaseName NOTIFY testCaseNameChanged) + Q_PROPERTY(QString functionName READ functionName WRITE setFunctionName NOTIFY functionNameChanged) + Q_PROPERTY(FunctionType functionType READ functionType WRITE setFunctionType NOTIFY functionTypeChanged) + Q_PROPERTY(QString dataTag READ dataTag WRITE setDataTag NOTIFY dataTagChanged) + Q_PROPERTY(bool failed READ isFailed) + Q_PROPERTY(bool dataFailed READ isDataFailed) + Q_PROPERTY(bool skipped READ isSkipped WRITE setSkipped NOTIFY skippedChanged) + Q_PROPERTY(int passCount READ passCount) + Q_PROPERTY(int failCount READ failCount) + Q_PROPERTY(int skipCount READ skipCount) + Q_PROPERTY(QStringList functionsToRun READ functionsToRun) +public: + QuickTestResult(QObject *parent = 0); + ~QuickTestResult(); + + // Values must match QTestResult::TestLocation. + enum FunctionType + { + NoWhere = 0, + DataFunc = 1, + InitFunc = 2, + Func = 3, + CleanupFunc = 4 + }; + + // Values must match QBenchmarkIterationController::RunMode. + enum RunMode + { + RepeatUntilValidMeasurement, + RunOnce + }; + + QString testCaseName() const; + void setTestCaseName(const QString &name); + + QString functionName() const; + void setFunctionName(const QString &name); + + FunctionType functionType() const; + void setFunctionType(FunctionType type); + + QString dataTag() const; + void setDataTag(const QString &tag); + + bool isFailed() const; + bool isDataFailed() const; + + bool isSkipped() const; + void setSkipped(bool skip); + + int passCount() const; + int failCount() const; + int skipCount() const; + + QStringList functionsToRun() const; + +public Q_SLOTS: + void reset(); + + void startLogging(); + void stopLogging(); + + void initTestTable(); + void clearTestTable(); + + void finishTestFunction(); + + void fail(const QString &message, const QString &file, int line); + bool verify(bool success, const QString &message, + const QString &file, int line); + bool compare(bool success, const QString &message, + const QString &val1, const QString &val2, + const QString &file, int line); + void skipSingle(const QString &message, const QString &file, int line); + void skipAll(const QString &message, const QString &file, int line); + bool expectFail(const QString &tag, const QString &comment, + const QString &file, int line); + bool expectFailContinue(const QString &tag, const QString &comment, + const QString &file, int line); + void warn(const QString &message); + + void ignoreWarning(const QString &message); + + void wait(int ms); + void sleep(int ms); + + void startMeasurement(); + void beginDataRun(); + void endDataRun(); + bool measurementAccepted(); + bool needsMoreMeasurements(); + + void startBenchmark(RunMode runMode, const QString &tag); + bool isBenchmarkDone() const; + void nextBenchmark(); + void stopBenchmark(); + +public: + // Helper functions for the C++ main() shell. + static void parseArgs(int argc, char *argv[]); + static void setProgramName(const char *name); + static int exitCode(); + +Q_SIGNALS: + void programNameChanged(); + void testCaseNameChanged(); + void functionNameChanged(); + void functionTypeChanged(); + void dataTagChanged(); + void skippedChanged(); + +private: + QScopedPointer d_ptr; + + Q_DECLARE_PRIVATE(QuickTestResult) + Q_DISABLE_COPY(QuickTestResult) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/src.pro b/src/src.pro index 27cc875ff2..e8f77e6bad 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,3 +1,5 @@ TEMPLATE = subdirs CONFIG += ordered -SUBDIRS += declarative plugins imports +SUBDIRS += declarative plugins imports +contains(QT_CONFIG, qmltest): SUBDIRS += qmltest + diff --git a/sync.profile b/sync.profile index 42ea13d9c3..5ec72304d0 100644 --- a/sync.profile +++ b/sync.profile @@ -1,5 +1,6 @@ %modules = ( # path to module name map "QtDeclarative" => "$basedir/src/declarative", + "QtQuickTest" => "$basedir/src/qmltest", ); %moduleheaders = ( # restrict the module headers to those found in relative path ); @@ -9,9 +10,11 @@ "gui" => "#include \n", "script" => "#include \n", "network" => "#include \n", + "testlib" => "#include \n", ); %modulepris = ( "QtDeclarative" => "$basedir/modules/qt_declarative.pri", + "QtQuickTest" => "$basedir/modules/qt_qmltest.pri", ); # Modules and programs, and their dependencies. # Each of the module version specifiers can take one of the following values: @@ -29,4 +32,8 @@ "QtSql" => "0c637cb07ba3c9b353e7e483a209537485cc4e2a", "QtCore" => "0c637cb07ba3c9b353e7e483a209537485cc4e2a", }, + "QtQuickTest" => { + "QtTest" => "0c637cb07ba3c9b353e7e483a209537485cc4e2a", + "QtDeclarative" => "THIS_REPOSITORY", + }, ); diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 1edeaaa956..2543261eeb 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -2,4 +2,6 @@ TEMPLATE=subdirs SUBDIRS=\ declarative \ +contains(QT_CONFIG, qmltest): SUBDIRS += qmltest + !cross_compile: SUBDIRS += host.pro diff --git a/tests/auto/qmltest/buttonclick/Button.qml b/tests/auto/qmltest/buttonclick/Button.qml new file mode 100644 index 0000000000..6b195ff7c4 --- /dev/null +++ b/tests/auto/qmltest/buttonclick/Button.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + id: container + + property string text: "Button" + + signal clicked + + width: buttonLabel.width + 20; height: buttonLabel.height + 5 + border { width: 1; color: "black" } + smooth: true + radius: 8 + + // color the button with a gradient + gradient: Gradient { + GradientStop { position: 0.0; color: "blue" } + GradientStop { position: 1.0; color: "lightblue" } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: container.clicked(); + } + + Text { + id: buttonLabel + anchors.centerIn: container + color: "white" + text: container.text + } +} diff --git a/tests/auto/qmltest/buttonclick/tst_buttonclick.qml b/tests/auto/qmltest/buttonclick/tst_buttonclick.qml new file mode 100644 index 0000000000..a875daa7d7 --- /dev/null +++ b/tests/auto/qmltest/buttonclick/tst_buttonclick.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +Button { + id: button + onClicked: text = "Clicked" + + SignalSpy { + id: spy + target: button + signalName: "clicked" + } + + TestCase { + name: "ButtonClick" + when: windowShown + + function test_click() { + compare(spy.count, 0) + button.clicked(); + compare(button.text, "Clicked"); + compare(spy.count, 1) + } + } +} diff --git a/tests/auto/qmltest/createbenchmark/item.qml b/tests/auto/qmltest/createbenchmark/item.qml new file mode 100644 index 0000000000..e3a3491f41 --- /dev/null +++ b/tests/auto/qmltest/createbenchmark/item.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} + Item {} +} diff --git a/tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml b/tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml new file mode 100644 index 0000000000..b484020d3c --- /dev/null +++ b/tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +TestCase { + id: top + name: "CreateBenchmark" + + function benchmark_create_component() { + var component = Qt.createComponent("item.qml") + var obj = component.createObject(top) + obj.destroy() + component.destroy() + } +} diff --git a/tests/auto/qmltest/events/tst_events.qml b/tests/auto/qmltest/events/tst_events.qml new file mode 100644 index 0000000000..bc358d95a6 --- /dev/null +++ b/tests/auto/qmltest/events/tst_events.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +Rectangle { + width: 50; height: 50 + id: top + focus: true + + property bool leftKeyPressed: false + property bool leftKeyReleased: false + + Keys.onLeftPressed: { + leftKeyPressed = true + } + + Keys.onReleased: { + if (event.key == Qt.Key_Left) + leftKeyReleased = true + } + + property bool mouseHasBeenClicked: false + + MouseArea { + anchors.fill: parent + onClicked: { + mouseHasBeenClicked = true + } + } + + TestCase { + name: "Events" + when: windowShown // Must have this line for events to work. + + function test_key_click() { + keyClick(Qt.Key_Left) + tryCompare(top, "leftKeyPressed", true) + tryCompare(top, "leftKeyReleased", true) + } + + function test_mouse_click() { + mouseClick(top, 25, 30) + tryCompare(top, "mouseHasBeenClicked", true) + } + } +} diff --git a/tests/auto/qmltest/qdecarativebinding/tst_binding.qml b/tests/auto/qmltest/qdecarativebinding/tst_binding.qml new file mode 100644 index 0000000000..5c883c52d1 --- /dev/null +++ b/tests/auto/qmltest/qdecarativebinding/tst_binding.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +Rectangle { + id: screen + width: 320; height: 240 + property string text + property bool changeColor: false + + Text { id: s1; text: "Hello" } + Rectangle { id: r1; width: 1; height: 1; color: "yellow" } + Rectangle { id: r2; width: 1; height: 1; color: "red" } + + Binding { target: screen; property: "text"; value: s1.text; id: binding1 } + Binding { target: screen; property: "color"; value: r1.color } + Binding { target: screen; property: "color"; when: screen.changeColor == true; value: r2.color; id: binding3 } + + TestCase { + name: "Binding" + + function test_binding() { + compare(screen.color, "#ffff00") // Yellow + compare(screen.text, "Hello") + verify(!binding3.when) + + screen.changeColor = true + compare(screen.color, "#ff0000") // Red + + verify(binding1.target == screen) + compare(binding1.property, "text") + compare(binding1.value, "Hello") + } + } +} diff --git a/tests/auto/qmltest/qdecarativebinding/tst_binding2.qml b/tests/auto/qmltest/qdecarativebinding/tst_binding2.qml new file mode 100644 index 0000000000..85e1951ba7 --- /dev/null +++ b/tests/auto/qmltest/qdecarativebinding/tst_binding2.qml @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +Rectangle { + id: screen + width: 320; height: 240 + property string text + property bool changeColor: false + + Text { id: s1; text: "Hello" } + Rectangle { id: r1; width: 1; height: 1; color: "yellow" } + Rectangle { id: r2; width: 1; height: 1; color: "red" } + + Binding { target: screen; property: "text"; value: s1.text } + Binding { target: screen; property: "color"; value: r1.color } + Binding { target: screen; property: "color"; value: r2.color; when: screen.changeColor == true } + + TestCase { + name: "Binding2" + + function test_binding2() { + compare(screen.color, "#ffff00") // Yellow + compare(screen.text, "Hello") + + screen.changeColor = true + compare(screen.color, "#ff0000") // Red + } + } +} diff --git a/tests/auto/qmltest/qdecarativeborderimage/InvalidSciFile.qml b/tests/auto/qmltest/qdecarativeborderimage/InvalidSciFile.qml new file mode 100644 index 0000000000..d8a6d89b92 --- /dev/null +++ b/tests/auto/qmltest/qdecarativeborderimage/InvalidSciFile.qml @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +BorderImage { + source: "invalid.sci" + width: 300 + height: 300 +} diff --git a/tests/auto/qmltest/qdecarativeborderimage/colors-round.sci b/tests/auto/qmltest/qdecarativeborderimage/colors-round.sci new file mode 100644 index 0000000000..5d2f49f0e1 --- /dev/null +++ b/tests/auto/qmltest/qdecarativeborderimage/colors-round.sci @@ -0,0 +1,7 @@ +border.left:10 +border.top:20 +border.right:30 +border.bottom:40 +horizontalTileRule:Round +verticalTileRule:Repeat +source:colors.png diff --git a/tests/auto/qmltest/qdecarativeborderimage/colors.png b/tests/auto/qmltest/qdecarativeborderimage/colors.png new file mode 100644 index 0000000000000000000000000000000000000000..dfb62f3d64e95a26d2ea1e87065e7892fa7814a3 GIT binary patch literal 1655 zcmZuydpy%?7@t&fDVMV4vO+|Cv_2vZwqhn*<$7YdtQ>7ka+_twI&or-NX?}XhV)6t ztj^hlwL>Q*TWE2T^RtzEPPJ*_%yEj&@8{Hi=ktEv`}cXC@AJIx`@9!|0{qtNnd`w| zu=NByE(GkEP+hkcY~9JO17N2`5AnmoZa=kr15Vb)9P-1#K0~Fpy`cnL(M`sO(?Rhe zsA^#yDj@|rX9)hjI>0(TZFj4poa0g$Y|R1z=Y51Ztg3FOStJ(CX?O z*^xN(xT@hJ^8JS?pX5l)h>I6-iF!=pDdjCY2e#x@jscOiOHG6=v+ z1_elx?C9izwhAsrD}ViBGr1|=*iA09_ja;Ae)VF}u5lYQ%f`iR@i1Z+?PLhafFYgw6Md*mZu7Q^VwF^Q^pS_-F`agpn);VIn7Qv%*NH8Vo%RZ+n?|@w7)v?{_q3jqY_bNQl?#0 z>op#?a_SwUU8E`xBpRH))4x!v-=vp>LkJtx@4}6vPH&&v9%o$i;2YDzxk!V{gfv{b;Kabo z(!3tE(u>*a@%VHZQg~?`7u8JFc~JjmAf-bYdylz^R>VcZE|)qFzY%PkstSGNo30@< zZ@j`^E<^s*`osp+DYWm@PJv~HGrTtR`zqpP+|`D$;<09BR*L~naahKM86HfX;wx*9 z;Bg8^8R?4bl^Zc+pR@D31<`!^^6bOjUij7=`CGt(`})c+deJQD{m)H&;g!^ujOC^2 zw<0rcyVkv}2PN5)V>SAIE%}vGfJI#?X9T}p8b))*uvTp{pCw46ft`sh)WGvtY=?uH zZDf}(Q-h0*lhoVU_k_`cxfru+CbMKIK@b{sgR0|IiRJDR1-IlEXp~leHgPKLWTpJ^ zJ!nrOj~$aP(v1{eo1<()?j=2U(kvd>35Bng>XS?8u!`)Wuox!u4wk6JoOn~>m`J{y z)_s0U06KH=yG_y`g{lSKs+sX2qA(8ef-EbP1zB!==1k>OabZCL1Pc@F>P(gXY ztWv6e9?=~Ogco;3w9BIDtp6$!`jb(olA9o`FKeFWu^Dr_#<34|I*V-oM`n-zEcufWg4?D~@$ zvA)~L^0f+Jw&ud~(d<($h@}Z*+w`i4Df7#M7;3NR>gCJJsCY{NmX8mg;=*Sq1;o zAKaI{8rI|5+|%1ngZlMk0Xg0M?7I+iNb45r9b+6w5=ubo&8cxVUrBV|@U(&KQJ4%X z^hxBz1&BV#d9?q`sT~jqFWZpi_r#13X}XSP?Lmhm-B@vV4A?lP<(`EAJ^T9Hy&->~ x$B1?qG+l|*ELu!z8*v-RAbd22fMK%Fuxz4#;h6n&F!(FL2)+Th+t}#y{{lV;B) 10; + }; + } + + function Human(year) { + var privateVar = 1; + this.year = year; + this.isOld = function() { + return year > 80; + }; + } + + var car = new Car(30); + var carSame = new Car(30); + var carDiff = new Car(10); + var human = new Human(30); + + var diff = { + year: 30 + }; + + var same = { + year: 30, + isOld: function () {} + }; + + compare(qtest_compareInternal(car, car), true); + compare(qtest_compareInternal(car, carDiff), false); + compare(qtest_compareInternal(car, carSame), true); + compare(qtest_compareInternal(car, human), false); + } + + + function test_complex_instances_nesting() { + //"Complex Instances Nesting (with function value in literals and/or in nested instances)" + function A(fn) { + this.a = {}; + this.fn = fn; + this.b = {a: []}; + this.o = {}; + this.fn1 = fn; + } + function B(fn) { + this.fn = fn; + this.fn1 = function () {}; + this.a = new A(function () {}); + } + + function fnOutside() { + } + + function C(fn) { + function fnInside() { + } + this.x = 10; + this.fn = fn; + this.fn1 = function () {}; + this.fn2 = fnInside; + this.fn3 = { + a: true, + b: fnOutside // ok make reference to a function in all instances scope + }; + this.o1 = {}; + + // This function will be ignored. + // Even if it is not visible for all instances (e.g. locked in a closures), + // it is from a property that makes part of an instance (e.g. from the C constructor) + this.b1 = new B(function () {}); + this.b2 = new B({ + x: { + b2: new B(function() {}) + } + }); + } + + function D(fn) { + function fnInside() { + } + this.x = 10; + this.fn = fn; + this.fn1 = function () {}; + this.fn2 = fnInside; + this.fn3 = { + a: true, + b: fnOutside, // ok make reference to a function in all instances scope + + // This function won't be ingored. + // It isn't visible for all C insances + // and it is not in a property of an instance. (in an Object instances e.g. the object literal) + c: fnInside + }; + this.o1 = {}; + + // This function will be ignored. + // Even if it is not visible for all instances (e.g. locked in a closures), + // it is from a property that makes part of an instance (e.g. from the C constructor) + this.b1 = new B(function () {}); + this.b2 = new B({ + x: { + b2: new B(function() {}) + } + }); + } + + function E(fn) { + function fnInside() { + } + this.x = 10; + this.fn = fn; + this.fn1 = function () {}; + this.fn2 = fnInside; + this.fn3 = { + a: true, + b: fnOutside // ok make reference to a function in all instances scope + }; + this.o1 = {}; + + // This function will be ignored. + // Even if it is not visible for all instances (e.g. locked in a closures), + // it is from a property that makes part of an instance (e.g. from the C constructor) + this.b1 = new B(function () {}); + this.b2 = new B({ + x: { + b1: new B({a: function() {}}), + b2: new B(function() {}) + } + }); + } + + + var a1 = new A(function () {}); + var a2 = new A(function () {}); + expectFail("", "Don't know if we want to take over function checking like in QUnit") + compare(qtest_compareInternal(a1, a2), true); + + compare(qtest_compareInternal(a1, a2), true); // different instances + + var b1 = new B(function () {}); + var b2 = new B(function () {}); + compare(qtest_compareInternal(a1, a2), true); + + var c1 = new C(function () {}); + var c2 = new C(function () {}); + compare(qtest_compareInternal(c1, c2), true); + + var d1 = new D(function () {}); + var d2 = new D(function () {}); + compare(qtest_compareInternal(d1, d2), false); + + var e1 = new E(function () {}); + var e2 = new E(function () {}); + compare(qtest_compareInternal(e1, e2), false); + + } +} diff --git a/tests/auto/qmltest/selftests/tst_compare_quickobjects.qml b/tests/auto/qmltest/selftests/tst_compare_quickobjects.qml new file mode 100644 index 0000000000..7a8dc74347 --- /dev/null +++ b/tests/auto/qmltest/selftests/tst_compare_quickobjects.qml @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +TestCase { + name: "SelfTests_compare_QuickObjects" + id: testParent + + Rectangle { + id: item1 + color: "#000000" + } + Rectangle { + id: item2 + color: "#000000" + } + Rectangle { + id: item3 + color: "#ffffff" + } + + Component { + id: item4 + + Rectangle { + color: "#ffffff" + } + } + + function test_quickobjects() { + compare(qtest_compareInternal(item1, item1), true, "Identical QtQuick instances"); + compare(qtest_compareInternal(item1, item3), false, "QtQuick instances with different properties"); + + expectFail("", "Unsure if we want this."); + compare(qtest_compareInternal(item1, item2), true, "QtQuick instances with same properties"); + + expectFail("", "Unsure if we want this."); + compare(qtest_compareInternal(item4.createObject(testParent), + item4.createObject(testParent)), true, "QtQuick dynamic instances"); + } +} diff --git a/tests/auto/qmltest/selftests/tst_selftests.qml b/tests/auto/qmltest/selftests/tst_selftests.qml new file mode 100644 index 0000000000..16a539e895 --- /dev/null +++ b/tests/auto/qmltest/selftests/tst_selftests.qml @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +TestCase { + name: "SelfTests" + + // Replace the TestResult functions in "testCase" with hooks + // that record the events but don't send them to QTestLib. + QtObject { + id: functions + property string failmsg: "cleaned" + property string actual: "" + property string expected: "" + property variant functionsToRun: [] + + function fail(msg, file, line) { + failmsg = msg + } + + function verify(cond, msg, file, line) { + if (cond) { + failmsg = "verify-ok" + return true + } else { + failmsg = msg + return false + } + } + + function compare(success, msg, act, exp, file, line) { + if (success) { + failmsg = "compare-ok" + actual = "" + expected = "" + return true + } else { + failmsg = msg + actual = act + expected = exp + return false + } + } + + function skipSingle(msg, file, line) { + failmsg = "skipSingle:" + msg + } + + function skipAll(msg, file, line) { + failmsg = "skipAll:" + msg + } + } + + TestCase { + id: testCase + when: false + optional: true + qtest_results: functions + } + + function init() { + compare(functions.failmsg, "cleaned") // Checks for previous cleanup() + functions.failmsg = "invalid" + } + + function cleanup() { + functions.failmsg = "cleaned" + } + + function test_fail() { + compare(functions.failmsg, "invalid") // Checks that init() was run + + var caught = false + try { + testCase.fail("foo") + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "foo") + caught = true + } + verify(caught) + + caught = false + try { + testCase.fail() + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "") + caught = true + } + verify(caught) + + caught = false + try { + testCase.fail(false) + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "false") + caught = true + } + verify(caught) + + caught = false + try { + testCase.fail(3) + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "3") + caught = true + } + verify(caught) + } + + function test_verify() { + compare(functions.failmsg, "invalid") // Checks that init() was run + + try { + testCase.verify(true) + } catch (e) { + fail("verify(true) did not succeed") + } + compare(functions.failmsg, "verify-ok") + + var caught = false; + try { + testCase.verify(false, "foo") + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "foo") + caught = true + } + verify(caught) + + caught = false; + try { + testCase.verify(false) + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "") + caught = true + } + verify(caught) + } + + function test_compare() { + compare(functions.failmsg, "invalid") // Checks that init() was run + + try { + testCase.compare(23, 23) + } catch (e) { + fail("compare(23, 23) did not succeed") + } + compare(functions.failmsg, "compare-ok") + + var caught = false; + try { + testCase.compare(23, 42, "foo") + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "foo") + compare(functions.actual, "23") + compare(functions.expected, "42") + caught = true + } + verify(caught) + + caught = false; + try { + testCase.compare("abcdef", 42) + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "Compared values are not the same") + compare(functions.actual, "abcdef") + compare(functions.expected, "42") + caught = true + } + verify(caught) + + caught = false; + try { + testCase.compare(Qt.vector3d(1, 2, 3), Qt.vector3d(-1, 2, 3), "x") + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "x") + compare(functions.actual, "Qt.vector3d(1, 2, 3)") + compare(functions.expected, "Qt.vector3d(-1, 2, 3)") + caught = true + } + verify(caught) + + caught = false; + try { + testCase.compare(Qt.vector3d(1, 2, 3), Qt.vector3d(1, -2, 3), "y") + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "y") + compare(functions.actual, "Qt.vector3d(1, 2, 3)") + compare(functions.expected, "Qt.vector3d(1, -2, 3)") + caught = true + } + verify(caught) + + caught = false; + try { + testCase.compare(Qt.vector3d(1, 2, 3), Qt.vector3d(1, 2, -3), "z") + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "z") + compare(functions.actual, "Qt.vector3d(1, 2, 3)") + compare(functions.expected, "Qt.vector3d(1, 2, -3)") + caught = true + } + verify(caught) + + caught = false; + try { + testCase.compare(Qt.vector3d(1, 2, 3), Qt.vector3d(1, 2, 3)) + } catch (e) { + fail("vector compare did not succeed") + } + compare(functions.failmsg, "compare-ok") + } + + function test_skip() { + compare(functions.failmsg, "invalid") // Checks that init() was run + + var caught = false + try { + testCase.skip("foo") + } catch (e) { + compare(e.message, "QtQuickTest::skip") + compare(functions.failmsg, "skipSingle:foo") + caught = true + } + verify(caught) + + caught = false + try { + testCase.skip() + } catch (e) { + compare(e.message, "QtQuickTest::skip") + compare(functions.failmsg, "skipSingle:") + caught = true + } + verify(caught) + + caught = false + try { + testCase.skipAll("foo") + } catch (e) { + compare(e.message, "QtQuickTest::skip") + compare(functions.failmsg, "skipAll:foo") + caught = true + } + verify(caught) + + caught = false + try { + testCase.skipAll() + } catch (e) { + compare(e.message, "QtQuickTest::skip") + compare(functions.failmsg, "skipAll:") + caught = true + } + verify(caught) + } +} diff --git a/tests/auto/qmltest/tst_qmltest.cpp b/tests/auto/qmltest/tst_qmltest.cpp new file mode 100644 index 0000000000..37d47ac0d0 --- /dev/null +++ b/tests/auto/qmltest/tst_qmltest.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +QUICK_TEST_MAIN(qmlauto) diff --git a/tools/qmltestrunner/main.cpp b/tools/qmltestrunner/main.cpp new file mode 100644 index 0000000000..47f34ee65e --- /dev/null +++ b/tools/qmltestrunner/main.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#ifdef QT_OPENGL_LIB +#include +#endif + +#ifdef QT_OPENGL_LIB + +static QWidget *qmltestrunner_create_gl_viewport() +{ + return new QGLWidget(); +} + +#endif + +int main(int argc, char **argv) +{ +#ifdef QT_OPENGL_LIB + bool isOpenGL = false; + for (int index = 1; index < argc; ++index) { + if (strcmp(argv[index], "-opengl") == 0) { + isOpenGL = true; + break; + } + } + if (isOpenGL) { + return quick_test_main(argc, argv, "qmltestrunner", + qmltestrunner_create_gl_viewport, "."); + } else +#endif + { + return quick_test_main(argc, argv, "qmltestrunner", 0, "."); + } +} diff --git a/tools/qmltestrunner/qmltestrunner.pro b/tools/qmltestrunner/qmltestrunner.pro new file mode 100644 index 0000000000..b2aabf6355 --- /dev/null +++ b/tools/qmltestrunner/qmltestrunner.pro @@ -0,0 +1,11 @@ +TEMPLATE = app +TARGET = qmltestrunner +CONFIG += warn_on qmltestcase +SOURCES += main.cpp + +contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles2) { + QT += opengl +} + +target.path = $$[QT_INSTALL_BINS] +INSTALLS += target diff --git a/tools/tools.pro b/tools/tools.pro index ec83a1e76a..b1a896b7f8 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -1,2 +1,4 @@ TEMPLATE = subdirs SUBDIRS += qmlviewer qmlscene qmlplugindump +contains(QT_CONFIG, quicktest): SUBDIRS += qmltestrunner +