2011-04-27 10:05:43 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2013-01-02 11:17:46 +00:00
|
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
2012-09-20 05:21:40 +00:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** This file is part of the test suite of the Qt Toolkit.
|
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
2012-09-20 05:21:40 +00:00
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
|
|
**
|
2011-04-27 10:05:43 +00:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-09-20 05:21:40 +00:00
|
|
|
** 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, Digia gives you certain additional
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
2011-04-27 10:05:43 +00:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
2011-05-24 11:43:28 +00:00
|
|
|
** GNU General Public License Usage
|
2012-09-20 05:21:40 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3.0 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
2012-01-24 03:37:23 +00:00
|
|
|
**
|
2011-04-27 10:05:43 +00:00
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
2012-05-03 22:32:45 +00:00
|
|
|
#include "../../shared/util.h"
|
2012-02-16 04:43:03 +00:00
|
|
|
#include <QQmlEngine>
|
|
|
|
#include <QQmlContext>
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <QNetworkAccessManager>
|
|
|
|
#include <QPointer>
|
|
|
|
#include <QDir>
|
2011-10-23 23:03:51 +00:00
|
|
|
#include <QStandardPaths>
|
2012-04-19 12:38:38 +00:00
|
|
|
#include <QSignalSpy>
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <QDebug>
|
2013-02-28 17:13:23 +00:00
|
|
|
#include <QBuffer>
|
2012-02-16 04:43:03 +00:00
|
|
|
#include <QQmlComponent>
|
|
|
|
#include <QQmlNetworkAccessManagerFactory>
|
|
|
|
#include <QQmlExpression>
|
2012-05-03 22:32:45 +00:00
|
|
|
#include <QQmlIncubationController>
|
|
|
|
#include <private/qqmlengine_p.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-05-03 22:32:45 +00:00
|
|
|
class tst_qqmlengine : public QQmlDataTest
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
2012-02-16 04:43:03 +00:00
|
|
|
tst_qqmlengine() {}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
private slots:
|
|
|
|
void rootContext();
|
|
|
|
void networkAccessManager();
|
2013-02-28 17:13:23 +00:00
|
|
|
void synchronousNetworkAccessManager();
|
2011-04-27 10:05:43 +00:00
|
|
|
void baseUrl();
|
|
|
|
void contextForObject();
|
|
|
|
void offlineStoragePath();
|
|
|
|
void clearComponentCache();
|
2012-05-03 22:32:45 +00:00
|
|
|
void trimComponentCache();
|
|
|
|
void trimComponentCache_data();
|
2012-05-21 03:40:22 +00:00
|
|
|
void repeatedCompilation();
|
|
|
|
void failedCompilation();
|
|
|
|
void failedCompilation_data();
|
2011-04-27 10:05:43 +00:00
|
|
|
void outputWarningsToStandardError();
|
|
|
|
void objectOwnership();
|
2011-07-25 05:47:40 +00:00
|
|
|
void multipleEngines();
|
2012-05-23 08:05:10 +00:00
|
|
|
void qtqmlModule_data();
|
|
|
|
void qtqmlModule();
|
2012-04-19 12:38:38 +00:00
|
|
|
|
|
|
|
public slots:
|
|
|
|
QObject *createAQObjectForOwnershipTest ()
|
|
|
|
{
|
|
|
|
static QObject *ptr = new QObject();
|
|
|
|
return ptr;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
};
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::rootContext()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QVERIFY(engine.rootContext());
|
|
|
|
|
|
|
|
QCOMPARE(engine.rootContext()->engine(), &engine);
|
|
|
|
QVERIFY(engine.rootContext()->parentContext() == 0);
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
NetworkAccessManagerFactory() : manager(0) {}
|
|
|
|
|
|
|
|
QNetworkAccessManager *create(QObject *parent) {
|
|
|
|
manager = new QNetworkAccessManager(parent);
|
|
|
|
return manager;
|
|
|
|
}
|
|
|
|
|
|
|
|
QNetworkAccessManager *manager;
|
|
|
|
};
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::networkAccessManager()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine *engine = new QQmlEngine;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
// Test QQmlEngine created manager
|
2011-04-27 10:05:43 +00:00
|
|
|
QPointer<QNetworkAccessManager> manager = engine->networkAccessManager();
|
|
|
|
QVERIFY(manager != 0);
|
|
|
|
delete engine;
|
|
|
|
|
|
|
|
// Test factory created manager
|
2012-02-16 04:43:03 +00:00
|
|
|
engine = new QQmlEngine;
|
2011-04-27 10:05:43 +00:00
|
|
|
NetworkAccessManagerFactory factory;
|
|
|
|
engine->setNetworkAccessManagerFactory(&factory);
|
|
|
|
QVERIFY(engine->networkAccessManagerFactory() == &factory);
|
|
|
|
QVERIFY(engine->networkAccessManager() == factory.manager);
|
|
|
|
delete engine;
|
|
|
|
}
|
|
|
|
|
2013-02-28 17:13:23 +00:00
|
|
|
class ImmediateReply : public QNetworkReply {
|
|
|
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
ImmediateReply() {
|
|
|
|
setFinished(true);
|
|
|
|
}
|
|
|
|
virtual qint64 readData(char* , qint64 ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
virtual void abort() { }
|
|
|
|
};
|
|
|
|
|
|
|
|
class ImmediateManager : public QNetworkAccessManager {
|
|
|
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
ImmediateManager(QObject *parent = 0) : QNetworkAccessManager(parent) {
|
|
|
|
}
|
|
|
|
|
|
|
|
QNetworkReply *createRequest(Operation, const QNetworkRequest & , QIODevice * outgoingData = 0) {
|
|
|
|
Q_UNUSED(outgoingData);
|
|
|
|
return new ImmediateReply;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class ImmediateFactory : public QQmlNetworkAccessManagerFactory {
|
|
|
|
|
|
|
|
public:
|
|
|
|
QNetworkAccessManager *create(QObject *) {
|
|
|
|
return new ImmediateManager;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_qqmlengine::synchronousNetworkAccessManager()
|
|
|
|
{
|
|
|
|
ImmediateFactory factory;
|
|
|
|
QQmlEngine engine;
|
|
|
|
engine.setNetworkAccessManagerFactory(&factory);
|
|
|
|
QQmlComponent c(&engine, QUrl("myScheme://test.qml"));
|
|
|
|
// reply is finished, so should not be in loading state.
|
|
|
|
QVERIFY(!c.isLoading());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::baseUrl()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QUrl cwd = QUrl::fromLocalFile(QDir::currentPath() + QDir::separator());
|
|
|
|
|
|
|
|
QCOMPARE(engine.baseUrl(), cwd);
|
|
|
|
QCOMPARE(engine.rootContext()->resolvedUrl(QUrl("main.qml")), cwd.resolved(QUrl("main.qml")));
|
|
|
|
|
|
|
|
QDir dir = QDir::current();
|
|
|
|
dir.cdUp();
|
|
|
|
QVERIFY(dir != QDir::current());
|
|
|
|
QDir::setCurrent(dir.path());
|
|
|
|
QVERIFY(QDir::current() == dir);
|
|
|
|
|
|
|
|
QUrl cwd2 = QUrl::fromLocalFile(QDir::currentPath() + QDir::separator());
|
|
|
|
QCOMPARE(engine.baseUrl(), cwd2);
|
|
|
|
QCOMPARE(engine.rootContext()->resolvedUrl(QUrl("main.qml")), cwd2.resolved(QUrl("main.qml")));
|
|
|
|
|
|
|
|
engine.setBaseUrl(cwd);
|
|
|
|
QCOMPARE(engine.baseUrl(), cwd);
|
|
|
|
QCOMPARE(engine.rootContext()->resolvedUrl(QUrl("main.qml")), cwd.resolved(QUrl("main.qml")));
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::contextForObject()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine *engine = new QQmlEngine;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Test null-object
|
2012-02-16 04:43:03 +00:00
|
|
|
QVERIFY(QQmlEngine::contextForObject(0) == 0);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Test an object with no context
|
|
|
|
QObject object;
|
2012-02-16 04:43:03 +00:00
|
|
|
QVERIFY(QQmlEngine::contextForObject(&object) == 0);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Test setting null-object
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine::setContextForObject(0, engine->rootContext());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Test setting null-context
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine::setContextForObject(&object, 0);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Test setting context
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine::setContextForObject(&object, engine->rootContext());
|
|
|
|
QVERIFY(QQmlEngine::contextForObject(&object) == engine->rootContext());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlContext context(engine->rootContext());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Try changing context
|
2012-02-16 04:43:03 +00:00
|
|
|
QTest::ignoreMessage(QtWarningMsg, "QQmlEngine::setContextForObject(): Object already has a QQmlContext");
|
|
|
|
QQmlEngine::setContextForObject(&object, &context);
|
|
|
|
QVERIFY(QQmlEngine::contextForObject(&object) == engine->rootContext());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Delete context
|
|
|
|
delete engine; engine = 0;
|
2012-02-16 04:43:03 +00:00
|
|
|
QVERIFY(QQmlEngine::contextForObject(&object) == 0);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::offlineStoragePath()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
|
|
|
// Without these set, QDesktopServices::storageLocation returns
|
|
|
|
// strings with extra "//" at the end. We set them to ignore this problem.
|
2012-02-16 04:43:03 +00:00
|
|
|
qApp->setApplicationName("tst_qqmlengine");
|
2011-04-27 10:05:43 +00:00
|
|
|
qApp->setOrganizationName("Nokia");
|
|
|
|
qApp->setOrganizationDomain("nokia.com");
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-04-19 03:50:39 +00:00
|
|
|
QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
|
|
|
|
|
|
|
QCOMPARE(dataLocation.isEmpty(), engine.offlineStoragePath().isEmpty());
|
|
|
|
|
|
|
|
QDir dir(dataLocation);
|
2011-04-27 10:05:43 +00:00
|
|
|
dir.mkpath("QML");
|
|
|
|
dir.cd("QML");
|
|
|
|
dir.mkpath("OfflineStorage");
|
|
|
|
dir.cd("OfflineStorage");
|
|
|
|
|
|
|
|
QCOMPARE(QDir::fromNativeSeparators(engine.offlineStoragePath()), dir.path());
|
|
|
|
|
|
|
|
engine.setOfflineStoragePath(QDir::homePath());
|
|
|
|
QCOMPARE(engine.offlineStoragePath(), QDir::homePath());
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::clearComponentCache()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Create original qml file
|
|
|
|
{
|
|
|
|
QFile file("temp.qml");
|
|
|
|
QVERIFY(file.open(QIODevice::WriteOnly));
|
2012-01-31 06:52:36 +00:00
|
|
|
file.write("import QtQuick 2.0\nQtObject {\nproperty int test: 10\n}\n");
|
2011-04-27 10:05:43 +00:00
|
|
|
file.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test "test" property
|
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlComponent component(&engine, "temp.qml");
|
2011-04-27 10:05:43 +00:00
|
|
|
QObject *obj = component.create();
|
|
|
|
QVERIFY(obj != 0);
|
|
|
|
QCOMPARE(obj->property("test").toInt(), 10);
|
|
|
|
delete obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modify qml file
|
|
|
|
{
|
|
|
|
QFile file("temp.qml");
|
|
|
|
QVERIFY(file.open(QIODevice::WriteOnly));
|
2012-01-31 06:52:36 +00:00
|
|
|
file.write("import QtQuick 2.0\nQtObject {\nproperty int test: 11\n}\n");
|
2011-04-27 10:05:43 +00:00
|
|
|
file.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test cache hit
|
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlComponent component(&engine, "temp.qml");
|
2011-04-27 10:05:43 +00:00
|
|
|
QObject *obj = component.create();
|
|
|
|
QVERIFY(obj != 0);
|
|
|
|
QCOMPARE(obj->property("test").toInt(), 10);
|
|
|
|
delete obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear cache
|
|
|
|
engine.clearComponentCache();
|
|
|
|
|
|
|
|
// Test cache refresh
|
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlComponent component(&engine, "temp.qml");
|
2011-04-27 10:05:43 +00:00
|
|
|
QObject *obj = component.create();
|
|
|
|
QVERIFY(obj != 0);
|
|
|
|
QCOMPARE(obj->property("test").toInt(), 11);
|
|
|
|
delete obj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-03 22:32:45 +00:00
|
|
|
struct ComponentCacheFunctions : public QObject, public QQmlIncubationController
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
QQmlEngine *engine;
|
|
|
|
|
|
|
|
ComponentCacheFunctions(QQmlEngine &e) : engine(&e) {}
|
|
|
|
|
|
|
|
Q_INVOKABLE void trim()
|
|
|
|
{
|
|
|
|
// Wait for any pending deletions to occur
|
|
|
|
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
engine->trimComponentCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_INVOKABLE bool isTypeLoaded(QString file)
|
|
|
|
{
|
|
|
|
return QQmlEnginePrivate::get(engine)->isTypeLoaded(tst_qqmlengine::instance()->testFileUrl(file));
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_INVOKABLE bool isScriptLoaded(QString file)
|
|
|
|
{
|
|
|
|
return QQmlEnginePrivate::get(engine)->isScriptLoaded(tst_qqmlengine::instance()->testFileUrl(file));
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_INVOKABLE void beginIncubation()
|
|
|
|
{
|
|
|
|
startTimer(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_INVOKABLE void waitForIncubation()
|
|
|
|
{
|
|
|
|
while (incubatingObjectCount() > 0) {
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
virtual void timerEvent(QTimerEvent *)
|
|
|
|
{
|
|
|
|
incubateFor(1000);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_qqmlengine::trimComponentCache()
|
|
|
|
{
|
|
|
|
QFETCH(QString, file);
|
|
|
|
|
|
|
|
QQmlEngine engine;
|
|
|
|
ComponentCacheFunctions componentCache(engine);
|
|
|
|
engine.rootContext()->setContextProperty("componentCache", &componentCache);
|
|
|
|
engine.setIncubationController(&componentCache);
|
|
|
|
|
|
|
|
QQmlComponent component(&engine, testFileUrl(file));
|
|
|
|
QVERIFY(component.isReady());
|
|
|
|
QScopedPointer<QObject> object(component.create());
|
|
|
|
QVERIFY(object != 0);
|
|
|
|
QCOMPARE(object->property("success").toBool(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_qqmlengine::trimComponentCache_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
|
|
|
|
// The various tests here are for two types of components: those that are
|
|
|
|
// empty apart from their inherited elements, and those that define new properties.
|
|
|
|
// For each there are five types of composition: extension, aggregation,
|
|
|
|
// aggregation via component, property and object-created-via-transient-component.
|
|
|
|
foreach (const QString &test, (QStringList() << "EmptyComponent"
|
|
|
|
<< "VMEComponent"
|
|
|
|
<< "EmptyExtendEmptyComponent"
|
|
|
|
<< "VMEExtendEmptyComponent"
|
|
|
|
<< "EmptyExtendVMEComponent"
|
|
|
|
<< "VMEExtendVMEComponent"
|
|
|
|
<< "EmptyAggregateEmptyComponent"
|
|
|
|
<< "VMEAggregateEmptyComponent"
|
|
|
|
<< "EmptyAggregateVMEComponent"
|
|
|
|
<< "VMEAggregateVMEComponent"
|
|
|
|
<< "EmptyPropertyEmptyComponent"
|
|
|
|
<< "VMEPropertyEmptyComponent"
|
|
|
|
<< "EmptyPropertyVMEComponent"
|
|
|
|
<< "VMEPropertyVMEComponent"
|
|
|
|
<< "VMETransientEmptyComponent"
|
|
|
|
<< "VMETransientVMEComponent")) {
|
|
|
|
// For these cases, we first test that the component instance keeps the components
|
|
|
|
// referenced, and then that the instantiated object keeps the components referenced
|
|
|
|
for (int i = 1; i <= 2; ++i) {
|
|
|
|
QString name(QString("%1-%2").arg(test).arg(i));
|
|
|
|
QString file(QString("test%1.%2.qml").arg(test).arg(i));
|
|
|
|
QTest::newRow(name.toLatin1().constData()) << file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that a transient component is correctly referenced
|
|
|
|
QTest::newRow("TransientComponent-1") << "testTransientComponent.1.qml";
|
|
|
|
QTest::newRow("TransientComponent-2") << "testTransientComponent.2.qml";
|
|
|
|
|
|
|
|
// Test that components can be reloaded after unloading
|
|
|
|
QTest::newRow("ReloadComponent") << "testReloadComponent.qml";
|
|
|
|
|
|
|
|
// Test that components are correctly referenced when dynamically loaded
|
|
|
|
QTest::newRow("LoaderComponent") << "testLoaderComponent.qml";
|
|
|
|
|
|
|
|
// Test that components are correctly referenced when incubated
|
|
|
|
QTest::newRow("IncubatedComponent") << "testIncubatedComponent.qml";
|
|
|
|
|
|
|
|
// Test that a top-level omponents is correctly referenced
|
|
|
|
QTest::newRow("TopLevelComponent") << "testTopLevelComponent.qml";
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// Test that scripts are unloaded when no longer referenced
|
|
|
|
QTest::newRow("ScriptComponent") << "testScriptComponent.qml";
|
|
|
|
}
|
|
|
|
|
2012-05-21 03:40:22 +00:00
|
|
|
void tst_qqmlengine::repeatedCompilation()
|
|
|
|
{
|
|
|
|
QQmlEngine engine;
|
|
|
|
|
|
|
|
for (int i = 0; i < 100; ++i) {
|
|
|
|
engine.collectGarbage();
|
|
|
|
engine.trimComponentCache();
|
|
|
|
|
|
|
|
QQmlComponent component(&engine, testFileUrl("repeatedCompilation.qml"));
|
|
|
|
QVERIFY(component.isReady());
|
|
|
|
QScopedPointer<QObject> object(component.create());
|
|
|
|
QVERIFY(object != 0);
|
|
|
|
QCOMPARE(object->property("success").toBool(), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_qqmlengine::failedCompilation()
|
|
|
|
{
|
|
|
|
QFETCH(QString, file);
|
|
|
|
|
|
|
|
QQmlEngine engine;
|
|
|
|
|
|
|
|
QQmlComponent component(&engine, testFileUrl(file));
|
|
|
|
QVERIFY(!component.isReady());
|
|
|
|
QScopedPointer<QObject> object(component.create());
|
|
|
|
QVERIFY(object == 0);
|
|
|
|
|
|
|
|
engine.collectGarbage();
|
|
|
|
engine.trimComponentCache();
|
|
|
|
engine.clearComponentCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_qqmlengine::failedCompilation_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
|
|
|
|
QTest::newRow("Invalid URL") << "failedCompilation.does.not.exist.qml";
|
|
|
|
QTest::newRow("Invalid content") << "failedCompilation.1.qml";
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::outputWarningsToStandardError()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QCOMPARE(engine.outputWarningsToStandardError(), true);
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlComponent c(&engine);
|
2012-01-31 06:52:36 +00:00
|
|
|
c.setData("import QtQuick 2.0; QtObject { property int a: undefined }", QUrl());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QVERIFY(c.isReady() == true);
|
|
|
|
|
2012-12-18 10:41:06 +00:00
|
|
|
QQmlTestMessageHandler messageHandler;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QObject *o = c.create();
|
|
|
|
|
|
|
|
QVERIFY(o != 0);
|
|
|
|
delete o;
|
|
|
|
|
2012-12-18 10:41:06 +00:00
|
|
|
QCOMPARE(messageHandler.messages().count(), 1);
|
|
|
|
QCOMPARE(messageHandler.messages().at(0), QLatin1String("<Unknown File>: Unable to assign [undefined] to int"));
|
|
|
|
messageHandler.clear();
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
engine.setOutputWarningsToStandardError(false);
|
|
|
|
QCOMPARE(engine.outputWarningsToStandardError(), false);
|
|
|
|
|
|
|
|
o = c.create();
|
|
|
|
|
|
|
|
QVERIFY(o != 0);
|
|
|
|
delete o;
|
|
|
|
|
2012-12-18 10:41:06 +00:00
|
|
|
QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::objectOwnership()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(0), QQmlEngine::CppOwnership);
|
|
|
|
QQmlEngine::setObjectOwnership(0, QQmlEngine::JavaScriptOwnership);
|
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(0), QQmlEngine::CppOwnership);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
QObject o;
|
2012-02-16 04:43:03 +00:00
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(&o), QQmlEngine::CppOwnership);
|
|
|
|
QQmlEngine::setObjectOwnership(&o, QQmlEngine::CppOwnership);
|
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(&o), QQmlEngine::CppOwnership);
|
|
|
|
QQmlEngine::setObjectOwnership(&o, QQmlEngine::JavaScriptOwnership);
|
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(&o), QQmlEngine::JavaScriptOwnership);
|
|
|
|
QQmlEngine::setObjectOwnership(&o, QQmlEngine::CppOwnership);
|
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(&o), QQmlEngine::CppOwnership);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine;
|
|
|
|
QQmlComponent c(&engine);
|
2012-01-31 06:52:36 +00:00
|
|
|
c.setData("import QtQuick 2.0; QtObject { property QtObject object: QtObject {} }", QUrl());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QObject *o = c.create();
|
|
|
|
QVERIFY(o != 0);
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(o), QQmlEngine::CppOwnership);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QObject *o2 = qvariant_cast<QObject *>(o->property("object"));
|
2012-02-16 04:43:03 +00:00
|
|
|
QCOMPARE(QQmlEngine::objectOwnership(o2), QQmlEngine::JavaScriptOwnership);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
delete o;
|
|
|
|
}
|
2012-04-19 12:38:38 +00:00
|
|
|
{
|
|
|
|
QObject *ptr = createAQObjectForOwnershipTest();
|
|
|
|
QSignalSpy spy(ptr, SIGNAL(destroyed()));
|
|
|
|
{
|
|
|
|
QQmlEngine engine;
|
|
|
|
QQmlComponent c(&engine);
|
|
|
|
engine.rootContext()->setContextProperty("test", this);
|
|
|
|
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::JavaScriptOwnership);
|
|
|
|
c.setData("import QtQuick 2.0; Item { property int data: test.createAQObjectForOwnershipTest() ? 0 : 1 }", QUrl());
|
|
|
|
QVERIFY(c.isReady());
|
|
|
|
QObject *o = c.create();
|
|
|
|
QVERIFY(o != 0);
|
|
|
|
}
|
|
|
|
QTRY_VERIFY(spy.count());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
QObject *ptr = new QObject();
|
|
|
|
QSignalSpy spy(ptr, SIGNAL(destroyed()));
|
|
|
|
{
|
|
|
|
QQmlEngine engine;
|
|
|
|
QQmlComponent c(&engine);
|
|
|
|
engine.rootContext()->setContextProperty("test", ptr);
|
|
|
|
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::JavaScriptOwnership);
|
|
|
|
c.setData("import QtQuick 2.0; QtObject { property var object: { var i = test; test ? 0 : 1 } }", QUrl());
|
|
|
|
QVERIFY(c.isReady());
|
|
|
|
QObject *o = c.create();
|
|
|
|
QVERIFY(o != 0);
|
|
|
|
engine.rootContext()->setContextProperty("test", 0);
|
|
|
|
}
|
|
|
|
QTRY_VERIFY(spy.count());
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2011-07-25 05:47:40 +00:00
|
|
|
// Test an object can be accessed by multiple engines
|
2012-02-16 04:43:03 +00:00
|
|
|
void tst_qqmlengine::multipleEngines()
|
2011-07-25 05:47:40 +00:00
|
|
|
{
|
|
|
|
QObject o;
|
|
|
|
o.setObjectName("TestName");
|
|
|
|
|
|
|
|
// Simultaneous engines
|
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine1;
|
|
|
|
QQmlEngine engine2;
|
2011-07-25 05:47:40 +00:00
|
|
|
engine1.rootContext()->setContextProperty("object", &o);
|
|
|
|
engine2.rootContext()->setContextProperty("object", &o);
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlExpression expr1(engine1.rootContext(), 0, QString("object.objectName"));
|
|
|
|
QQmlExpression expr2(engine2.rootContext(), 0, QString("object.objectName"));
|
2011-07-25 05:47:40 +00:00
|
|
|
|
|
|
|
QCOMPARE(expr1.evaluate().toString(), QString("TestName"));
|
|
|
|
QCOMPARE(expr2.evaluate().toString(), QString("TestName"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serial engines
|
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine1;
|
2011-07-25 05:47:40 +00:00
|
|
|
engine1.rootContext()->setContextProperty("object", &o);
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlExpression expr1(engine1.rootContext(), 0, QString("object.objectName"));
|
2011-07-25 05:47:40 +00:00
|
|
|
QCOMPARE(expr1.evaluate().toString(), QString("TestName"));
|
|
|
|
}
|
|
|
|
{
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlEngine engine1;
|
2011-07-25 05:47:40 +00:00
|
|
|
engine1.rootContext()->setContextProperty("object", &o);
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlExpression expr1(engine1.rootContext(), 0, QString("object.objectName"));
|
2011-07-25 05:47:40 +00:00
|
|
|
QCOMPARE(expr1.evaluate().toString(), QString("TestName"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-23 08:05:10 +00:00
|
|
|
void tst_qqmlengine::qtqmlModule_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QUrl>("testFile");
|
|
|
|
QTest::addColumn<QString>("expectedError");
|
|
|
|
QTest::addColumn<QStringList>("expectedWarnings");
|
|
|
|
|
|
|
|
QTest::newRow("import QtQml of correct version (2.0)")
|
|
|
|
<< testFileUrl("qtqmlModule.1.qml")
|
|
|
|
<< QString()
|
|
|
|
<< QStringList();
|
|
|
|
|
|
|
|
QTest::newRow("import QtQml of incorrect version (3.0)")
|
|
|
|
<< testFileUrl("qtqmlModule.2.qml")
|
|
|
|
<< QString(testFileUrl("qtqmlModule.2.qml").toString() + QLatin1String(":1 module \"QtQml\" version 3.0 is not installed\n"))
|
|
|
|
<< QStringList();
|
|
|
|
|
|
|
|
QTest::newRow("import QtQml of incorrect version (1.0)")
|
|
|
|
<< testFileUrl("qtqmlModule.3.qml")
|
|
|
|
<< QString(testFileUrl("qtqmlModule.3.qml").toString() + QLatin1String(":1 module \"QtQml\" version 1.0 is not installed\n"))
|
|
|
|
<< QStringList();
|
|
|
|
|
|
|
|
QTest::newRow("import QtQml of incorrect version (2.5)")
|
|
|
|
<< testFileUrl("qtqmlModule.4.qml")
|
|
|
|
<< QString(testFileUrl("qtqmlModule.4.qml").toString() + QLatin1String(":1 module \"QtQml\" version 2.5 is not installed\n"))
|
|
|
|
<< QStringList();
|
|
|
|
|
2012-11-24 19:36:33 +00:00
|
|
|
QTest::newRow("QtQml 2.0 module provides Component, QtObject, Connections, Binding and Timer")
|
2012-05-23 08:05:10 +00:00
|
|
|
<< testFileUrl("qtqmlModule.5.qml")
|
|
|
|
<< QString()
|
|
|
|
<< QStringList();
|
|
|
|
|
|
|
|
QTest::newRow("can import QtQml then QtQuick")
|
|
|
|
<< testFileUrl("qtqmlModule.6.qml")
|
|
|
|
<< QString()
|
|
|
|
<< QStringList();
|
|
|
|
|
|
|
|
QTest::newRow("can import QtQuick then QtQml")
|
|
|
|
<< testFileUrl("qtqmlModule.7.qml")
|
|
|
|
<< QString()
|
|
|
|
<< QStringList();
|
|
|
|
|
|
|
|
QTest::newRow("no import results in no QtObject availability")
|
|
|
|
<< testFileUrl("qtqmlModule.8.qml")
|
|
|
|
<< QString(testFileUrl("qtqmlModule.8.qml").toString() + QLatin1String(":4 QtObject is not a type\n"))
|
|
|
|
<< QStringList();
|
|
|
|
|
|
|
|
QTest::newRow("importing QtQml only results in no Item availability")
|
|
|
|
<< testFileUrl("qtqmlModule.9.qml")
|
|
|
|
<< QString(testFileUrl("qtqmlModule.9.qml").toString() + QLatin1String(":4 Item is not a type\n"))
|
|
|
|
<< QStringList();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that the engine registers the QtQml module
|
|
|
|
void tst_qqmlengine::qtqmlModule()
|
|
|
|
{
|
|
|
|
QFETCH(QUrl, testFile);
|
|
|
|
QFETCH(QString, expectedError);
|
|
|
|
QFETCH(QStringList, expectedWarnings);
|
|
|
|
|
|
|
|
foreach (const QString &w, expectedWarnings)
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, qPrintable(w));
|
|
|
|
|
|
|
|
QQmlEngine e;
|
|
|
|
QQmlComponent c(&e, testFile);
|
|
|
|
if (expectedError.isEmpty()) {
|
|
|
|
QObject *o = c.create();
|
|
|
|
QVERIFY(o);
|
|
|
|
delete o;
|
|
|
|
} else {
|
|
|
|
QCOMPARE(c.errorString(), expectedError);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QTEST_MAIN(tst_qqmlengine)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
#include "tst_qqmlengine.moc"
|