qtdeclarative/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp

438 lines
14 KiB
C++

/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and 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.
**
** 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, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** 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.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QDebug>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlproperty.h>
#include <QtQml/qqmlincubator.h>
#include <QtQuick>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
#include <qcolor.h>
#include "../../shared/util.h"
#include "testhttpserver.h"
#define SERVER_PORT 14450
class MyIC : public QObject, public QQmlIncubationController
{
Q_OBJECT
public:
MyIC() { startTimer(5); }
protected:
virtual void timerEvent(QTimerEvent*) {
incubateFor(5);
}
};
class ComponentWatcher : public QObject
{
Q_OBJECT
public:
ComponentWatcher(QQmlComponent *comp) : loading(0), error(0), ready(0) {
connect(comp, SIGNAL(statusChanged(QQmlComponent::Status)),
this, SLOT(statusChanged(QQmlComponent::Status)));
}
int loading;
int error;
int ready;
public slots:
void statusChanged(QQmlComponent::Status status) {
switch (status) {
case QQmlComponent::Loading:
++loading;
break;
case QQmlComponent::Error:
++error;
break;
case QQmlComponent::Ready:
++ready;
break;
default:
break;
}
}
};
class tst_qqmlcomponent : public QQmlDataTest
{
Q_OBJECT
public:
tst_qqmlcomponent() { engine.setIncubationController(&ic); }
private slots:
void null();
void loadEmptyUrl();
void qmlCreateObject();
void qmlCreateObjectWithProperties();
void qmlIncubateObject();
void qmlCreateParentReference();
void async();
void asyncHierarchy();
void componentUrlCanonicalization();
void onDestructionLookup();
void onDestructionCount();
void recursion();
void recursionContinuation();
private:
QQmlEngine engine;
MyIC ic;
};
void tst_qqmlcomponent::null()
{
{
QQmlComponent c;
QVERIFY(c.isNull());
}
{
QQmlComponent c(&engine);
QVERIFY(c.isNull());
}
}
void tst_qqmlcomponent::loadEmptyUrl()
{
QQmlComponent c(&engine);
c.loadUrl(QUrl());
QVERIFY(c.isError());
QCOMPARE(c.errors().count(), 1);
QQmlError error = c.errors().first();
QCOMPARE(error.url(), QUrl());
QCOMPARE(error.line(), -1);
QCOMPARE(error.column(), -1);
QCOMPARE(error.description(), QLatin1String("Invalid empty URL"));
}
void tst_qqmlcomponent::qmlIncubateObject()
{
QQmlComponent component(&engine, testFileUrl("incubateObject.qml"));
QObject *object = component.create();
QVERIFY(object != 0);
QCOMPARE(object->property("test1").toBool(), true);
QCOMPARE(object->property("test2").toBool(), false);
QTRY_VERIFY(object->property("test2").toBool() == true);
delete object;
}
void tst_qqmlcomponent::qmlCreateObject()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("createObject.qml"));
QObject *object = component.create();
QVERIFY(object != 0);
QObject *testObject1 = object->property("qobject").value<QObject*>();
QVERIFY(testObject1);
QVERIFY(testObject1->parent() == object);
QObject *testObject2 = object->property("declarativeitem").value<QObject*>();
QVERIFY(testObject2);
QVERIFY(testObject2->parent() == object);
QCOMPARE(testObject2->metaObject()->className(), "QQuickItem");
}
void tst_qqmlcomponent::qmlCreateObjectWithProperties()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("createObjectWithScript.qml"));
QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8());
QObject *object = component.create();
QVERIFY(object != 0);
QObject *testObject1 = object->property("declarativerectangle").value<QObject*>();
QVERIFY(testObject1);
QVERIFY(testObject1->parent() == object);
QCOMPARE(testObject1->property("x").value<int>(), 17);
QCOMPARE(testObject1->property("y").value<int>(), 17);
QCOMPARE(testObject1->property("color").value<QColor>(), QColor(255,255,255));
QCOMPARE(QQmlProperty::read(testObject1,"border.width").toInt(), 3);
QCOMPARE(QQmlProperty::read(testObject1,"innerRect.border.width").toInt(), 20);
delete testObject1;
QObject *testObject2 = object->property("declarativeitem").value<QObject*>();
QVERIFY(testObject2);
QVERIFY(testObject2->parent() == object);
//QCOMPARE(testObject2->metaObject()->className(), "QDeclarativeItem_QML_2");
QCOMPARE(testObject2->property("x").value<int>(), 17);
QCOMPARE(testObject2->property("y").value<int>(), 17);
QCOMPARE(testObject2->property("testBool").value<bool>(), true);
QCOMPARE(testObject2->property("testInt").value<int>(), 17);
QCOMPARE(testObject2->property("testObject").value<QObject*>(), object);
delete testObject2;
QObject *testBindingObj = object->property("bindingTestObject").value<QObject*>();
QVERIFY(testBindingObj);
QCOMPARE(testBindingObj->parent(), object);
QCOMPARE(testBindingObj->property("testValue").value<int>(), 300);
object->setProperty("width", 150);
QCOMPARE(testBindingObj->property("testValue").value<int>(), 150 * 3);
delete testBindingObj;
QObject *testBindingThisObj = object->property("bindingThisTestObject").value<QObject*>();
QVERIFY(testBindingThisObj);
QCOMPARE(testBindingThisObj->parent(), object);
QCOMPARE(testBindingThisObj->property("testValue").value<int>(), 900);
testBindingThisObj->setProperty("width", 200);
QCOMPARE(testBindingThisObj->property("testValue").value<int>(), 200 * 3);
delete testBindingThisObj;
}
void tst_qqmlcomponent::qmlCreateParentReference()
{
QQmlEngine engine;
QCOMPARE(engine.outputWarningsToStandardError(), true);
QQmlTestMessageHandler messageHandler;
QQmlComponent component(&engine, testFileUrl("createParentReference.qml"));
QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8());
QObject *object = component.create();
QVERIFY(object != 0);
QVERIFY(QMetaObject::invokeMethod(object, "createChild"));
delete object;
engine.setOutputWarningsToStandardError(false);
QCOMPARE(engine.outputWarningsToStandardError(), false);
QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
}
void tst_qqmlcomponent::async()
{
TestHTTPServer server(SERVER_PORT);
QVERIFY(server.isValid());
server.serveDirectory(dataDirectory());
QQmlComponent component(&engine);
ComponentWatcher watcher(&component);
component.loadUrl(QUrl("http://127.0.0.1:14450/TestComponent.qml"), QQmlComponent::Asynchronous);
QCOMPARE(watcher.loading, 1);
QTRY_VERIFY(component.isReady());
QCOMPARE(watcher.ready, 1);
QCOMPARE(watcher.error, 0);
QObject *object = component.create();
QVERIFY(object != 0);
delete object;
}
void tst_qqmlcomponent::asyncHierarchy()
{
TestHTTPServer server(SERVER_PORT);
QVERIFY(server.isValid());
server.serveDirectory(dataDirectory());
// ensure that the item hierarchy is compiled correctly.
QQmlComponent component(&engine);
ComponentWatcher watcher(&component);
component.loadUrl(QUrl("http://127.0.0.1:14450/TestComponent.2.qml"), QQmlComponent::Asynchronous);
QCOMPARE(watcher.loading, 1);
QTRY_VERIFY(component.isReady());
QCOMPARE(watcher.ready, 1);
QCOMPARE(watcher.error, 0);
QObject *root = component.create();
QVERIFY(root != 0);
// ensure that the parent-child relationship hierarchy is correct
// (use QQuickItem* for all children rather than types which are not publicly exported)
QQuickItem *c1 = root->findChild<QQuickItem*>("c1", Qt::FindDirectChildrenOnly);
QVERIFY(c1);
QQuickItem *c1c1 = c1->findChild<QQuickItem*>("c1c1", Qt::FindDirectChildrenOnly);
QVERIFY(c1c1);
QQuickItem *c1c2 = c1->findChild<QQuickItem*>("c1c2", Qt::FindDirectChildrenOnly);
QVERIFY(c1c2);
QQuickItem *c1c2c3 = c1c2->findChild<QQuickItem*>("c1c2c3", Qt::FindDirectChildrenOnly);
QVERIFY(c1c2c3);
QQuickItem *c2 = root->findChild<QQuickItem*>("c2", Qt::FindDirectChildrenOnly);
QVERIFY(c2);
QQuickItem *c2c1 = c2->findChild<QQuickItem*>("c2c1", Qt::FindDirectChildrenOnly);
QVERIFY(c2c1);
QQuickItem *c2c1c1 = c2c1->findChild<QQuickItem*>("c2c1c1", Qt::FindDirectChildrenOnly);
QVERIFY(c2c1c1);
QQuickItem *c2c1c2 = c2c1->findChild<QQuickItem*>("c2c1c2", Qt::FindDirectChildrenOnly);
QVERIFY(c2c1c2);
// ensure that values and bindings are assigned correctly
QVERIFY(root->property("success").toBool());
delete root;
}
void tst_qqmlcomponent::componentUrlCanonicalization()
{
// ensure that url canonicalization succeeds so that type information
// is not generated multiple times for the same component.
{
// load components via import
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != 0);
QVERIFY(object->property("success").toBool());
}
{
// load one of the components dynamically, which would trigger
// import of the other if it were not already loaded.
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.2.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != 0);
QVERIFY(object->property("success").toBool());
}
{
// load components with more deeply nested imports
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.3.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != 0);
QVERIFY(object->property("success").toBool());
}
{
// load components with unusually specified import paths
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.4.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != 0);
QVERIFY(object->property("success").toBool());
}
{
// Do not crash with various nonsense import paths
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.5.qml"));
QTest::ignoreMessage(QtWarningMsg, QLatin1String("QQmlComponent: Component is not ready").data());
QScopedPointer<QObject> object(component.create());
QVERIFY(object == 0);
}
}
void tst_qqmlcomponent::onDestructionLookup()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("onDestructionLookup.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != 0);
QVERIFY(object->property("success").toBool());
}
void tst_qqmlcomponent::onDestructionCount()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("onDestructionCount.qml"));
QLatin1String warning("Component.onDestruction");
{
// Warning should be emitted during create()
QTest::ignoreMessage(QtWarningMsg, warning.data());
QScopedPointer<QObject> object(component.create());
QVERIFY(object != 0);
}
// Warning should not be emitted any further
QCOMPARE(engine.outputWarningsToStandardError(), true);
QStringList warnings;
{
QQmlTestMessageHandler messageHandler;
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
QCoreApplication::processEvents();
warnings = messageHandler.messages();
}
engine.setOutputWarningsToStandardError(false);
QCOMPARE(engine.outputWarningsToStandardError(), false);
QCOMPARE(warnings.count(), 0);
}
void tst_qqmlcomponent::recursion()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("recursion.qml"));
QTest::ignoreMessage(QtWarningMsg, QLatin1String("QQmlComponent: Component creation is recursing - aborting").data());
QScopedPointer<QObject> object(component.create());
QVERIFY(object != 0);
// Sub-object creation does not succeed
QCOMPARE(object->property("success").toBool(), false);
}
void tst_qqmlcomponent::recursionContinuation()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("recursionContinuation.qml"));
for (int i = 0; i < 10; ++i)
QTest::ignoreMessage(QtWarningMsg, QLatin1String("QQmlComponent: Component creation is recursing - aborting").data());
QScopedPointer<QObject> object(component.create());
QVERIFY(object != 0);
// Eventual sub-object creation succeeds
QVERIFY(object->property("success").toBool());
}
QTEST_MAIN(tst_qqmlcomponent)
#include "tst_qqmlcomponent.moc"