blob: 22074602b7878e557606a5b55aa6e2ab0cf8a486 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQmlContext>
#include <QDebug>
#include <QScopedPointer>
#include <private/qqmlglobal_p.h>
#include <private/qquickvaluetypes_p.h>
#include "../../shared/util.h"
#include "testtypes.h"
QT_BEGIN_NAMESPACE
extern int qt_defaultDpi(void);
QT_END_NAMESPACE
// There is some overlap between the qqmllanguage and qqmlvaluetypes
// test here, but it needs to be separate to ensure that no QML plugins
// are loaded prior to these tests, which could contaminate the type
// system with more providers.
class tst_qqmlvaluetypeproviders : public QQmlDataTest
{
Q_OBJECT
public:
tst_qqmlvaluetypeproviders() {}
private slots:
void initTestCase();
void qtqmlValueTypes(); // This test function _must_ be the first test function run.
void qtquickValueTypes();
void comparisonSemantics();
void cppIntegration();
void jsObjectConversion();
void invokableFunctions();
void userType();
void changedSignal();
};
void tst_qqmlvaluetypeproviders::initTestCase()
{
QQmlDataTest::initTestCase();
registerTypes();
}
void tst_qqmlvaluetypeproviders::qtqmlValueTypes()
{
QQmlEngine e;
QQmlComponent component(&e, testFileUrl("qtqmlValueTypes.qml"));
QVERIFY(!component.isError());
QVERIFY(component.errors().isEmpty());
QObject *object = component.create();
QVERIFY(object != nullptr);
QVERIFY(object->property("qtqmlTypeSuccess").toBool());
QVERIFY(object->property("qtquickTypeSuccess").toBool());
delete object;
}
void tst_qqmlvaluetypeproviders::qtquickValueTypes()
{
QQmlEngine e;
QQmlComponent component(&e, testFileUrl("qtquickValueTypes.qml"));
QVERIFY(!component.isError());
QVERIFY(component.errors().isEmpty());
QObject *object = component.create();
QVERIFY(object != nullptr);
QVERIFY(object->property("qtqmlTypeSuccess").toBool());
QVERIFY(object->property("qtquickTypeSuccess").toBool());
delete object;
}
void tst_qqmlvaluetypeproviders::comparisonSemantics()
{
QQmlEngine e;
QQmlComponent component(&e, testFileUrl("comparisonSemantics.qml"));
QVERIFY(!component.isError());
QVERIFY(component.errors().isEmpty());
QObject *object = component.create();
QVERIFY(object != nullptr);
QVERIFY(object->property("comparisonSuccess").toBool());
delete object;
}
void tst_qqmlvaluetypeproviders::cppIntegration()
{
QQmlEngine e;
QQmlComponent component(&e, testFileUrl("cppIntegration.qml"));
QVERIFY(!component.isError());
QVERIFY(component.errors().isEmpty());
QObject *object = component.create();
QVERIFY(object != nullptr);
// ensure accessing / comparing / assigning cpp-defined props
// and qml-defined props works in QML.
QVERIFY(object->property("success").toBool());
// ensure types match
QCOMPARE(object->property("g").userType(), object->property("rectf").userType());
QCOMPARE(object->property("p").userType(), object->property("pointf").userType());
QCOMPARE(object->property("z").userType(), object->property("sizef").userType());
QCOMPARE(object->property("v2").userType(), object->property("vector2").userType());
QCOMPARE(object->property("v3").userType(), object->property("vector").userType());
QCOMPARE(object->property("v4").userType(), object->property("vector4").userType());
QCOMPARE(object->property("q").userType(), object->property("quaternion").userType());
QCOMPARE(object->property("m").userType(), object->property("matrix").userType());
QCOMPARE(object->property("c").userType(), object->property("color").userType());
QCOMPARE(object->property("f").userType(), object->property("font").userType());
// ensure values match
QCOMPARE(object->property("g").value<QRectF>(), object->property("rectf").value<QRectF>());
QCOMPARE(object->property("p").value<QPointF>(), object->property("pointf").value<QPointF>());
QCOMPARE(object->property("z").value<QSizeF>(), object->property("sizef").value<QSizeF>());
QCOMPARE(object->property("v2").value<QVector2D>(), object->property("vector2").value<QVector2D>());
QCOMPARE(object->property("v3").value<QVector3D>(), object->property("vector").value<QVector3D>());
QCOMPARE(object->property("v4").value<QVector4D>(), object->property("vector4").value<QVector4D>());
QCOMPARE(object->property("q").value<QQuaternion>(), object->property("quaternion").value<QQuaternion>());
QCOMPARE(object->property("m").value<QMatrix4x4>(), object->property("matrix").value<QMatrix4x4>());
QCOMPARE(object->property("c").value<QColor>(), object->property("color").value<QColor>());
QCOMPARE(object->property("f").value<QFont>(), object->property("font").value<QFont>());
delete object;
}
void tst_qqmlvaluetypeproviders::jsObjectConversion()
{
QQmlEngine e;
QQmlComponent component(&e, testFileUrl("jsObjectConversion.qml"));
QVERIFY(!component.isError());
QVERIFY(component.errors().isEmpty());
QObject *object = component.create();
QVERIFY(object != nullptr);
QVERIFY(object->property("qtquickTypeSuccess").toBool());
delete object;
}
void tst_qqmlvaluetypeproviders::invokableFunctions()
{
QQmlEngine e;
QQmlComponent component(&e, testFileUrl("invokableFunctions.qml"));
QVERIFY(!component.isError());
QVERIFY(component.errors().isEmpty());
QObject *object = component.create();
QVERIFY(object != nullptr);
QVERIFY(object->property("complete").toBool());
QVERIFY(object->property("success").toBool());
delete object;
}
namespace {
// A value-type class to export to QML
class TestValue
{
public:
TestValue() : m_p1(0), m_p2(0.0) {}
TestValue(int p1, double p2) : m_p1(p1), m_p2(p2) {}
TestValue(const TestValue &other) : m_p1(other.m_p1), m_p2(other.m_p2) {}
~TestValue() {}
TestValue &operator=(const TestValue &other) { m_p1 = other.m_p1; m_p2 = other.m_p2; return *this; }
int property1() const { return m_p1; }
void setProperty1(int p1) { m_p1 = p1; }
double property2() const { return m_p2; }
void setProperty2(double p2) { m_p2 = p2; }
bool operator==(const TestValue &other) const { return (m_p1 == other.m_p1) && (m_p2 == other.m_p2); }
bool operator!=(const TestValue &other) const { return !operator==(other); }
bool operator<(const TestValue &other) const { if (m_p1 < other.m_p1) return true; return m_p2 < other.m_p2; }
private:
int m_p1;
double m_p2;
};
}
Q_DECLARE_METATYPE(TestValue);
namespace {
class TestValueType
{
TestValue v;
Q_GADGET
Q_PROPERTY(int property1 READ property1 WRITE setProperty1)
Q_PROPERTY(double property2 READ property2 WRITE setProperty2)
public:
Q_INVOKABLE QString toString() const { return QString::number(property1()) + QLatin1Char(',') + QString::number(property2()); }
int property1() const { return v.property1(); }
void setProperty1(int p1) { v.setProperty1(p1); }
double property2() const { return v.property2(); }
void setProperty2(double p2) { v.setProperty2(p2); }
};
class TestValueTypeProvider : public QQmlValueTypeProvider
{
public:
const QMetaObject *getMetaObjectForMetaType(int type)
{
if (type == qMetaTypeId<TestValue>())
return &TestValueType::staticMetaObject;
return nullptr;
}
};
TestValueTypeProvider *getValueTypeProvider()
{
static TestValueTypeProvider valueTypeProvider;
return &valueTypeProvider;
}
bool initializeProviders()
{
QQml_addValueTypeProvider(getValueTypeProvider());
return true;
}
const bool initialized = initializeProviders();
class TestValueExporter : public QObject
{
Q_OBJECT
Q_PROPERTY(TestValue testValue READ testValue WRITE setTestValue)
public:
TestValue testValue() const { return m_testValue; }
void setTestValue(const TestValue &v) { m_testValue = v; }
Q_INVOKABLE TestValue getTestValue() const { return TestValue(333, 666.999); }
private:
TestValue m_testValue;
};
}
void tst_qqmlvaluetypeproviders::userType()
{
Q_ASSERT(initialized);
Q_ASSERT(qMetaTypeId<TestValue>() >= QMetaType::User);
qRegisterMetaType<TestValue>();
QMetaType::registerComparators<TestValue>();
qmlRegisterType<TestValueExporter>("Test", 1, 0, "TestValueExporter");
TestValueExporter exporter;
QQmlEngine e;
e.rootContext()->setContextProperty("testValueExporter", &exporter);
QQmlComponent component(&e, testFileUrl("userType.qml"));
QScopedPointer<QObject> obj(component.create());
QVERIFY(obj != nullptr);
QCOMPARE(obj->property("success").toBool(), true);
}
void tst_qqmlvaluetypeproviders::changedSignal()
{
QQmlEngine e;
QQmlComponent component(&e, testFileUrl("changedSignal.qml"));
QVERIFY(!component.isError());
QVERIFY(component.errors().isEmpty());
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QVERIFY(object->property("complete").toBool());
QVERIFY(object->property("success").toBool());
}
QTEST_MAIN(tst_qqmlvaluetypeproviders)
#include "tst_qqmlvaluetypeproviders.moc"