diff options
Diffstat (limited to 'tests/auto/quickcontrols2/customization/tst_customization.cpp')
-rw-r--r-- | tests/auto/quickcontrols2/customization/tst_customization.cpp | 534 |
1 files changed, 0 insertions, 534 deletions
diff --git a/tests/auto/quickcontrols2/customization/tst_customization.cpp b/tests/auto/quickcontrols2/customization/tst_customization.cpp deleted file mode 100644 index 02c75978..00000000 --- a/tests/auto/quickcontrols2/customization/tst_customization.cpp +++ /dev/null @@ -1,534 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later 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 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/qtest.h> -#include <QtCore/private/qhooks_p.h> -#include <QtCore/qregularexpression.h> -#include <QtQml/qqmlengine.h> -#include <QtQml/qqmlcomponent.h> -#include <QtQuick/qquickitem.h> -#include <QtQuick/qquickwindow.h> -#include <QtQuickControls2/qquickstyle.h> -#include <QtQuickControls2/private/qquickstyle_p.h> -#include <QtQuickTemplates2/private/qquickcontrol_p_p.h> -#include "../shared/visualtestutil.h" - -using namespace QQuickVisualTestUtil; - -struct ControlInfo -{ - QString type; - QStringList delegates; -}; - -static const ControlInfo ControlInfos[] = { - { "AbstractButton", QStringList() << "background" << "contentItem" << "indicator" }, - { "ApplicationWindow", QStringList() << "background" }, - { "BusyIndicator", QStringList() << "background" << "contentItem" }, - { "Button", QStringList() << "background" << "contentItem" }, - { "CheckBox", QStringList() << "contentItem" << "indicator" }, - { "CheckDelegate", QStringList() << "background" << "contentItem" << "indicator" }, - { "ComboBox", QStringList() << "background" << "contentItem" << "indicator" }, // popup not created until needed - { "Container", QStringList() << "background" << "contentItem" }, - { "Control", QStringList() << "background" << "contentItem" }, - { "DelayButton", QStringList() << "background" << "contentItem" }, - { "Dial", QStringList() << "background" << "handle" }, - { "Dialog", QStringList() << "background" << "contentItem" }, - { "DialogButtonBox", QStringList() << "background" << "contentItem" }, - { "Drawer", QStringList() << "background" << "contentItem" }, - { "Frame", QStringList() << "background" << "contentItem" }, - { "GroupBox", QStringList() << "background" << "contentItem" << "label" }, - { "ItemDelegate", QStringList() << "background" << "contentItem" }, - { "Label", QStringList() << "background" }, - { "Menu", QStringList() << "background" << "contentItem" }, - { "MenuBar", QStringList() << "background" << "contentItem" }, - { "MenuBarItem", QStringList() << "background" << "contentItem" }, - { "MenuItem", QStringList() << "arrow" << "background" << "contentItem" << "indicator" }, - { "MenuSeparator", QStringList() << "background" << "contentItem" }, - { "Page", QStringList() << "background" << "contentItem" }, - { "PageIndicator", QStringList() << "background" << "contentItem" }, - { "Pane", QStringList() << "background" << "contentItem" }, - { "Popup", QStringList() << "background" << "contentItem" }, - { "ProgressBar", QStringList() << "background" << "contentItem" }, - { "RadioButton", QStringList() << "contentItem" << "indicator" }, - { "RadioDelegate", QStringList() << "background" << "contentItem" << "indicator" }, - { "RangeSlider", QStringList() << "background" << "first.handle" << "second.handle" }, - { "RoundButton", QStringList() << "background" << "contentItem" }, - { "ScrollBar", QStringList() << "background" << "contentItem" }, - { "ScrollIndicator", QStringList() << "background" << "contentItem" }, - { "ScrollView", QStringList() << "background" }, - { "Slider", QStringList() << "background" << "handle" }, - { "SpinBox", QStringList() << "background" << "contentItem" << "up.indicator" << "down.indicator" }, - { "StackView", QStringList() << "background" << "contentItem" }, - { "SwipeDelegate", QStringList() << "background" << "contentItem" }, - { "SwipeView", QStringList() << "background" << "contentItem" }, - { "Switch", QStringList() << "contentItem" << "indicator" }, - { "SwitchDelegate", QStringList() << "background" << "contentItem" << "indicator" }, - { "TabBar", QStringList() << "background" << "contentItem" }, - { "TabButton", QStringList() << "background" << "contentItem" }, - { "TextField", QStringList() << "background" }, - { "TextArea", QStringList() << "background" }, - { "ToolBar", QStringList() << "background" << "contentItem" }, - { "ToolButton", QStringList() << "background" << "contentItem" }, - { "ToolSeparator", QStringList() << "background" << "contentItem" }, - { "ToolTip", QStringList() << "background" << "contentItem" }, - { "Tumbler", QStringList() << "background" << "contentItem" } -}; - -class tst_customization : public QQmlDataTest -{ - Q_OBJECT - -private slots: - void initTestCase() override; - void cleanupTestCase(); - - void init(); - void cleanup(); - - void creation_data(); - void creation(); - - void override_data(); - void override(); - - void comboPopup(); - -private: - void reset(); - void addHooks(); - void removeHooks(); - - QObject* createControl(const QString &type, const QString &qml, QString *error); - - QQmlEngine *engine = nullptr; -}; - -typedef QHash<QObject *, QString> QObjectNameHash; -Q_GLOBAL_STATIC(QObjectNameHash, qt_objectNames) -Q_GLOBAL_STATIC(QStringList, qt_createdQObjects) -Q_GLOBAL_STATIC(QStringList, qt_destroyedQObjects) -Q_GLOBAL_STATIC(QStringList, qt_destroyedParentQObjects) -static int qt_unparentedItemCount = 0; - -class ItemParentListener : public QQuickItem -{ - Q_OBJECT - -public: - ItemParentListener() - { - m_slotIndex = metaObject()->indexOfSlot("onParentChanged()"); - m_signalIndex = QMetaObjectPrivate::signalIndex(QMetaMethod::fromSignal(&QQuickItem::parentChanged)); - } - - int signalIndex() const { return m_signalIndex; } - int slotIndex() const { return m_slotIndex; } - -public slots: - void onParentChanged() - { - const QQuickItem *item = qobject_cast<QQuickItem *>(sender()); - if (!item) - return; - - if (!item->parentItem()) - ++qt_unparentedItemCount; - } - -private: - int m_slotIndex; - int m_signalIndex; -}; -static ItemParentListener *qt_itemParentListener = nullptr; - -extern "C" Q_DECL_EXPORT void qt_addQObject(QObject *object) -{ - // objectName is not set at construction time - QObject::connect(object, &QObject::objectNameChanged, [object](const QString &objectName) { - QString oldObjectName = qt_objectNames()->value(object); - if (!oldObjectName.isEmpty()) - qt_createdQObjects()->removeOne(oldObjectName); - // Only track object names from our QML files, - // not e.g. contentItem object names (like "ApplicationWindow"). - if (objectName.contains("-")) { - qt_createdQObjects()->append(objectName); - qt_objectNames()->insert(object, objectName); - } - }); - - if (qt_itemParentListener) { - static const int signalIndex = qt_itemParentListener->signalIndex(); - static const int slotIndex = qt_itemParentListener->slotIndex(); - QMetaObject::connect(object, signalIndex, qt_itemParentListener, slotIndex); - } -} - -extern "C" Q_DECL_EXPORT void qt_removeQObject(QObject *object) -{ - QString objectName = object->objectName(); - if (!objectName.isEmpty()) - qt_destroyedQObjects()->append(objectName); - qt_objectNames()->remove(object); - - QObject *parent = object->parent(); - if (parent) { - QString parentName = parent->objectName(); - if (!parentName.isEmpty()) - qt_destroyedParentQObjects()->append(parentName); - } -} - -void tst_customization::initTestCase() -{ - QQmlDataTest::initTestCase(); - - qt_itemParentListener = new ItemParentListener; -} - -void tst_customization::cleanupTestCase() -{ - delete qt_itemParentListener; - qt_itemParentListener = nullptr; -} - -void tst_customization::init() -{ - engine = new QQmlEngine(this); - engine->addImportPath(testFile("styles")); - - qtHookData[QHooks::AddQObject] = reinterpret_cast<quintptr>(&qt_addQObject); - qtHookData[QHooks::RemoveQObject] = reinterpret_cast<quintptr>(&qt_removeQObject); -} - -void tst_customization::cleanup() -{ - qtHookData[QHooks::AddQObject] = 0; - qtHookData[QHooks::RemoveQObject] = 0; - - delete engine; - engine = nullptr; - - qmlClearTypeRegistrations(); - - reset(); -} - -void tst_customization::reset() -{ - qt_unparentedItemCount = 0; - qt_createdQObjects()->clear(); - qt_destroyedQObjects()->clear(); - qt_destroyedParentQObjects()->clear(); -} - -QObject* tst_customization::createControl(const QString &name, const QString &qml, QString *error) -{ - QQmlComponent component(engine); - component.setData("import QtQuick; import QtQuick.Window; import QtQuick.Controls; " + name.toUtf8() + " { " + qml.toUtf8() + " }", QUrl()); - QObject *obj = component.create(); - if (!obj) - *error = component.errorString(); - return obj; -} - -void tst_customization::creation_data() -{ - QTest::addColumn<QString>("style"); - QTest::addColumn<QString>("type"); - QTest::addColumn<QStringList>("delegates"); - - // the "empty" style does not contain any delegates - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable("empty:" + control.type)) << "empty" << control.type << QStringList(); - - // the "incomplete" style is missing bindings to the delegates (must be created regardless) - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable("incomplete:" + control.type)) << "incomplete" << control.type << control.delegates; - - // the "identified" style has IDs in the delegates (prevents deferred execution) - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable("identified:" + control.type)) << "identified" << control.type << control.delegates; - - // the "simple" style simulates a proper style and contains bindings to/in delegates - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable("simple:" + control.type)) << "simple" << control.type << control.delegates; - - // the "override" style overrides all delegates in the "simple" style - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable("override:" + control.type)) << "override" << control.type << control.delegates; -} - -void tst_customization::creation() -{ - QFETCH(QString, style); - QFETCH(QString, type); - QFETCH(QStringList, delegates); - - QQuickStyle::setStyle(style); - - QString error; - QScopedPointer<QObject> control(createControl(type, "", &error)); - QVERIFY2(control, qPrintable(error)); - - QByteArray templateType = "QQuick" + type.toUtf8(); - QVERIFY2(control->inherits(templateType), qPrintable(type + " does not inherit " + templateType + " (" + control->metaObject()->className() + ")")); - - // <control>-<style> - QString controlName = type.toLower() + "-" + style; - QCOMPARE(control->objectName(), controlName); - QVERIFY2(qt_createdQObjects()->removeOne(controlName), qPrintable(controlName + " was not created as expected")); - - for (QString delegate : qAsConst(delegates)) { - QStringList properties = delegate.split(".", Qt::SkipEmptyParts); - - // <control>-<delegate>-<style>(-<override>) - delegate.append("-" + style); - delegate.prepend(type.toLower() + "-"); - - QVERIFY2(qt_createdQObjects()->removeOne(delegate), qPrintable(delegate + " was not created as expected")); - - // verify that the delegate instance has the expected object name - // in case of grouped properties, we must query the properties step by step - QObject *instance = control.data(); - while (!properties.isEmpty()) { - QString property = properties.takeFirst(); - instance = instance->property(property.toUtf8()).value<QObject *>(); - QVERIFY2(instance, qPrintable("property was null: " + property)); - } - QCOMPARE(instance->objectName(), delegate); - } - - QEXPECT_FAIL("identified:ComboBox", "ComboBox::popup with an ID is created at construction time", Continue); - - QVERIFY2(qt_createdQObjects()->isEmpty(), qPrintable("unexpectedly created: " + qt_createdQObjects->join(", "))); - QVERIFY2(qt_destroyedQObjects()->isEmpty(), qPrintable("unexpectedly destroyed: " + qt_destroyedQObjects->join(", ") + " were unexpectedly destroyed")); - - QVERIFY2(qt_destroyedParentQObjects()->isEmpty(), qPrintable("delegates/children of: " + qt_destroyedParentQObjects->join(", ") + " were unexpectedly destroyed")); -} - -void tst_customization::override_data() -{ - QTest::addColumn<QString>("style"); - QTest::addColumn<QString>("type"); - QTest::addColumn<QStringList>("delegates"); - QTest::addColumn<QString>("nonDeferred"); - QTest::addColumn<bool>("identify"); - - // NOTE: delegates with IDs prevent deferred execution - - // default delegates with IDs, override with custom delegates with no IDs - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable("identified:" + control.type)) << "identified" << control.type << control.delegates << "identified" << false; - - // default delegates with no IDs, override with custom delegates with IDs - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable("simple:" + control.type)) << "simple" << control.type << control.delegates << "" << true; - - // default delegates with IDs, override with custom delegates with IDs - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable("overidentified:" + control.type)) << "identified" << control.type << control.delegates << "identified" << true; - -#ifndef Q_OS_MACOS // QTBUG-65671 - - // test that the built-in styles don't have undesired IDs in their delegates - const QStringList styles = QQuickStylePrivate::builtInStyles(); - for (const QString &style : styles) { - for (const ControlInfo &control : ControlInfos) - QTest::newRow(qPrintable(style + ":" + control.type)) << style << control.type << control.delegates << "" << false; - } - -#endif -} - -void tst_customization::override() -{ - QFETCH(QString, style); - QFETCH(QString, type); - QFETCH(QStringList, delegates); - QFETCH(QString, nonDeferred); - QFETCH(bool, identify); - - QQuickStyle::setStyle(style); - - QString qml; - qml += QString("objectName: '%1-%2-override'; ").arg(type.toLower()).arg(style); - for (const QString &delegate : delegates) { - QString id = identify ? QString("id: %1;").arg(delegate) : QString(); - qml += QString("%1: Item { %2 objectName: '%3-%1-%4-override' } ").arg(delegate).arg(id.replace(".", "")).arg(type.toLower()).arg(style); - } - - QString error; - QScopedPointer<QObject> control(createControl(type, qml, &error)); - QVERIFY2(control, qPrintable(error)); - - // If there are no intentional IDs in the default delegates nor in the overridden custom - // delegates, no item should get un-parented during the creation process. An item being - // unparented means that a delegate got destroyed, so there must be an internal ID in one - // of the delegates in the tested style. - if (!identify && nonDeferred.isEmpty()) { - QEXPECT_FAIL("Universal:ApplicationWindow", "ApplicationWindow.qml contains an intentionally unparented FocusRectangle", Continue); - QCOMPARE(qt_unparentedItemCount, 0); - } - - // <control>-<style>-override - QString controlName = type.toLower() + "-" + style + "-override"; - QCOMPARE(control->objectName(), controlName); - QVERIFY2(qt_createdQObjects()->removeOne(controlName), qPrintable(controlName + " was not created as expected")); - - for (QString delegate : qAsConst(delegates)) { - QStringList properties = delegate.split(".", Qt::SkipEmptyParts); - - // <control>-<delegate>-<style>(-override) - delegate.append("-" + style); - delegate.prepend(type.toLower() + "-"); - - if (!nonDeferred.isEmpty()) - QVERIFY2(qt_createdQObjects()->removeOne(delegate), qPrintable(delegate + " was not created as expected")); - - delegate.append("-override"); - QVERIFY2(qt_createdQObjects()->removeOne(delegate), qPrintable(delegate + " was not created as expected")); - - // verify that the delegate instance has the expected object name - // in case of grouped properties, we must query the properties step by step - QObject *instance = control.data(); - while (!properties.isEmpty()) { - QString property = properties.takeFirst(); - instance = instance->property(property.toUtf8()).value<QObject *>(); - QVERIFY2(instance, qPrintable("property was null: " + property)); - } - QCOMPARE(instance->objectName(), delegate); - } - - QEXPECT_FAIL("identified:ComboBox", "ComboBox::popup with an ID is created at construction time", Continue); - QEXPECT_FAIL("overidentified:ComboBox", "ComboBox::popup with an ID is created at construction time", Continue); - QVERIFY2(qt_createdQObjects()->isEmpty(), qPrintable("unexpectedly created: " + qt_createdQObjects->join(", "))); - - if (!nonDeferred.isEmpty()) { - // There were items for which deferred execution was not possible. - for (QString delegateName : qAsConst(delegates)) { - if (!delegateName.contains("-")) - delegateName.append("-" + nonDeferred); - delegateName.prepend(type.toLower() + "-"); - - const int delegateIndex = qt_destroyedQObjects()->indexOf(delegateName); - QVERIFY2(delegateIndex == -1, qPrintable(delegateName + " was unexpectedly destroyed")); - - const auto controlChildren = control->children(); - const auto childIt = std::find_if(controlChildren.constBegin(), controlChildren.constEnd(), [delegateName](const QObject *child) { - return child->objectName() == delegateName; - }); - // We test other delegates (like the background) here, so make sure we don't end up with XPASSes by using the wrong delegate. - if (delegateName.contains(QLatin1String("handle"))) { - QEXPECT_FAIL("identified:RangeSlider", "For some reason, items that are belong to grouped properties fail here", Abort); - QEXPECT_FAIL("overidentified:RangeSlider", "For some reason, items that are belong to grouped properties fail here", Abort); - } - if (delegateName.contains(QLatin1String("indicator"))) { - QEXPECT_FAIL("identified:SpinBox", "For some reason, items that are belong to grouped properties fail here", Abort); - QEXPECT_FAIL("overidentified:SpinBox", "For some reason, items that are belong to grouped properties fail here", Abort); - } - QVERIFY2(childIt != controlChildren.constEnd(), qPrintable(QString::fromLatin1( - "Expected delegate \"%1\" to still be a QObject child of \"%2\"").arg(delegateName).arg(controlName))); - - const auto *delegate = qobject_cast<QQuickItem*>(*childIt); - // Ensure that the item is hidden, etc. - QVERIFY(delegate); - QCOMPARE(delegate->isVisible(), false); - QCOMPARE(delegate->parentItem(), nullptr); - } - } - - QVERIFY2(qt_destroyedQObjects()->isEmpty(), qPrintable("unexpectedly destroyed: " + qt_destroyedQObjects->join(", "))); -} - -void tst_customization::comboPopup() -{ - QQuickStyle::setStyle("simple"); - - { - // test that ComboBox::popup is created when accessed - QQmlComponent component(engine); - component.setData("import QtQuick.Controls; ComboBox { }", QUrl()); - QScopedPointer<QQuickItem> comboBox(qobject_cast<QQuickItem *>(component.create())); - QVERIFY(comboBox); - - QVERIFY(!qt_createdQObjects()->contains("combobox-popup-simple")); - - QObject *popup = comboBox->property("popup").value<QObject *>(); - QVERIFY(popup); - QVERIFY(qt_createdQObjects()->contains("combobox-popup-simple")); - } - - reset(); - - { - // test that ComboBox::popup is created when it becomes visible - QQuickWindow window; - window.resize(300, 300); - window.show(); - window.requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(&window)); - - QQmlComponent component(engine); - component.setData("import QtQuick.Controls; ComboBox { }", QUrl()); - QScopedPointer<QQuickItem> comboBox(qobject_cast<QQuickItem *>(component.create())); - QVERIFY(comboBox); - - comboBox->setParentItem(window.contentItem()); - QVERIFY(!qt_createdQObjects()->contains("combobox-popup-simple")); - - QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); - QVERIFY(qt_createdQObjects()->contains("combobox-popup-simple")); - } - - reset(); - - { - // test that ComboBox::popup is completed upon component completion (if appropriate) - QQmlComponent component(engine); - component.setData("import QtQuick; import QtQuick.Controls; ComboBox { id: control; contentItem: Item { visible: !control.popup.visible } popup: Popup { property bool wasCompleted: false; Component.onCompleted: wasCompleted = true } }", QUrl()); - QScopedPointer<QQuickItem> comboBox(qobject_cast<QQuickItem *>(component.create())); - QVERIFY(comboBox); - - QObject *popup = comboBox->property("popup").value<QObject *>(); - QVERIFY(popup); - QCOMPARE(popup->property("wasCompleted"), QVariant(true)); - } -} - -QTEST_MAIN(tst_customization) - -#include "tst_customization.moc" |