1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include <QtCore/qdebug.h>
43 #include <QtCore/qtimer.h>
44 #include <QtCore/qdir.h>
45 #include <QtCore/qfileinfo.h>
46 #include <QtQml/qjsengine.h>
48 #include <QtQml/qqmlcomponent.h>
49 #include <QtQml/qqmlengine.h>
51 #include <private/qquickworkerscript_p.h>
52 #include <private/qqmlengine_p.h>
53 #include "../../shared/util.h"
55 class tst_QQuickWorkerScript : public QQmlDataTest
59 tst_QQuickWorkerScript() {}
63 void messaging_data();
64 void messaging_sendQObjectList();
65 void messaging_sendJsObject();
66 void messaging_sendExternalObject();
67 void script_with_pragma();
68 void script_included();
69 void scriptError_onLoad();
70 void scriptError_onCall();
74 void waitForEchoMessage(QQuickWorkerScript *worker) {
76 QVERIFY(connect(worker, SIGNAL(done()), &loop, SLOT(quit())));
78 timer.setSingleShot(true);
79 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
82 QVERIFY(timer.isActive());
88 void tst_QQuickWorkerScript::source()
90 QQmlComponent component(&m_engine, testFileUrl("worker.qml"));
91 QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
93 const QMetaObject *mo = worker->metaObject();
96 QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
97 waitForEchoMessage(worker);
98 QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(), value);
100 QUrl source = testFileUrl("script_fixed_return.js");
101 worker->setSource(source);
102 QCOMPARE(worker->source(), source);
103 QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
104 waitForEchoMessage(worker);
105 QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(), qVariantFromValue(QString("Hello_World")));
107 qApp->processEvents();
111 void tst_QQuickWorkerScript::messaging()
113 QFETCH(QVariant, value);
115 QQmlComponent component(&m_engine, testFileUrl("worker.qml"));
116 QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
117 QVERIFY(worker != 0);
119 QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
120 waitForEchoMessage(worker);
122 const QMetaObject *mo = worker->metaObject();
123 QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(), value);
125 qApp->processEvents();
129 void tst_QQuickWorkerScript::messaging_data()
131 QTest::addColumn<QVariant>("value");
133 QTest::newRow("invalid") << QVariant();
134 QTest::newRow("bool") << qVariantFromValue(true);
135 QTest::newRow("int") << qVariantFromValue(1001);
136 QTest::newRow("real") << qVariantFromValue(10334.375);
137 QTest::newRow("string") << qVariantFromValue(QString("More cheeeese, Gromit!"));
138 QTest::newRow("variant list") << qVariantFromValue((QVariantList() << "a" << "b" << "c"));
139 QTest::newRow("date time") << qVariantFromValue(QDateTime::currentDateTime());
141 // QtScript's QScriptValue -> QRegExp uses RegExp2 pattern syntax
142 QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive, QRegExp::RegExp2));
146 void tst_QQuickWorkerScript::messaging_sendQObjectList()
148 // Not allowed to send QObjects other than QQuickListModelWorkerAgent
149 // instances. If objects are sent in a list, they will be sent as 'undefined'
152 QQmlComponent component(&m_engine, testFileUrl("worker.qml"));
153 QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
154 QVERIFY(worker != 0);
156 QVariantList objects;
157 for (int i=0; i<3; i++)
158 objects << qVariantFromValue(new QObject(this));
160 QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, qVariantFromValue(objects))));
161 waitForEchoMessage(worker);
163 const QMetaObject *mo = worker->metaObject();
164 QVariantList result = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariantList>();
165 QCOMPARE(result, (QVariantList() << QVariant() << QVariant() << QVariant()));
167 qApp->processEvents();
171 void tst_QQuickWorkerScript::messaging_sendJsObject()
173 QQmlComponent component(&m_engine, testFileUrl("worker.qml"));
174 QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
175 QVERIFY(worker != 0);
177 // Properties are in alphabetical order to enable string-based comparison after
178 // QVariant roundtrip, since the properties will be stored in a QVariantMap.
179 QString jsObject = "{'haste': 1125, 'name': 'zyz', 'spell power': 3101}";
182 map.insert("haste", 1125);
183 map.insert("name", "zyz");
184 map.insert("spell power", 3101);
186 QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, qVariantFromValue(map))));
187 waitForEchoMessage(worker);
189 QVariant result = qVariantFromValue(false);
190 QVERIFY(QMetaObject::invokeMethod(worker, "compareLiteralResponse", Qt::DirectConnection,
191 Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, jsObject)));
192 QVERIFY(result.toBool());
194 qApp->processEvents();
198 void tst_QQuickWorkerScript::messaging_sendExternalObject()
200 QQmlComponent component(&m_engine, testFileUrl("externalObjectWorker.qml"));
201 QObject *obj = component.create();
203 QMetaObject::invokeMethod(obj, "testExternalObject");
204 QTest::qWait(100); // shouldn't crash.
208 void tst_QQuickWorkerScript::script_with_pragma()
212 QQmlComponent component(&m_engine, testFileUrl("worker_pragma.qml"));
213 QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
214 QVERIFY(worker != 0);
216 QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
217 waitForEchoMessage(worker);
219 const QMetaObject *mo = worker->metaObject();
220 QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(), value);
222 qApp->processEvents();
226 void tst_QQuickWorkerScript::script_included()
228 QQmlComponent component(&m_engine, testFileUrl("worker_include.qml"));
229 QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
230 QVERIFY(worker != 0);
232 QString value("Hello");
234 QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
235 waitForEchoMessage(worker);
237 const QMetaObject *mo = worker->metaObject();
238 QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).toString(), value + " World");
240 qApp->processEvents();
244 static QString qquickworkerscript_lastWarning;
245 static void qquickworkerscript_warningsHandler(QtMsgType type, const QMessageLogContext &, const QString &msg)
247 if (type == QtWarningMsg)
248 qquickworkerscript_lastWarning = msg;
251 void tst_QQuickWorkerScript::scriptError_onLoad()
253 QQmlComponent component(&m_engine, testFileUrl("worker_error_onLoad.qml"));
255 QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler);
256 QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
257 QVERIFY(worker != 0);
259 QTRY_COMPARE(qquickworkerscript_lastWarning,
260 testFileUrl("script_error_onLoad.js").toString() + QLatin1String(":3: SyntaxError: Unexpected identifier"));
262 qInstallMessageHandler(previousMsgHandler);
263 qApp->processEvents();
267 void tst_QQuickWorkerScript::scriptError_onCall()
269 QQmlComponent component(&m_engine, testFileUrl("worker_error_onCall.qml"));
270 QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
271 QVERIFY(worker != 0);
273 QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler);
275 QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
277 QTRY_COMPARE(qquickworkerscript_lastWarning,
278 testFileUrl("script_error_onCall.js").toString() + QLatin1String(":4: ReferenceError: getData is not defined"));
280 qInstallMessageHandler(previousMsgHandler);
281 qApp->processEvents();
285 // Rapidly create and destroy worker scripts to test resources are being disposed
286 // in the correct isolate
287 void tst_QQuickWorkerScript::stressDispose()
289 for (int ii = 0; ii < 100; ++ii) {
291 QQmlComponent component(&engine, testFileUrl("stressDispose.qml"));
292 QObject *o = component.create();
298 QTEST_MAIN(tst_QQuickWorkerScript)
300 #include "tst_qquickworkerscript.moc"