| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the test suite of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 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 The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 21 | ** included in the packaging of this file. Please review the following |
| 22 | ** information to ensure the GNU General Public License requirements will |
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 24 | ** |
| 25 | ** $QT_END_LICENSE$ |
| 26 | ** |
| 27 | ****************************************************************************/ |
| 28 | #include <qtest.h> |
| 29 | #include "../../shared/util.h" |
| 30 | #include <QtQml/qqmlengine.h> |
| 31 | #include <QtQml/qqmlcontext.h> |
| 32 | #include <QtQml/qqmlpropertymap.h> |
| 33 | #include <QtQml/qqmlcomponent.h> |
| 34 | #include <QtQuick/private/qquicktext_p.h> |
| 35 | #include <QSignalSpy> |
| 36 | #include <QDebug> |
| 37 | |
| 38 | class tst_QQmlPropertyMap : public QQmlDataTest |
| 39 | { |
| 40 | Q_OBJECT |
| 41 | public: |
| 42 | tst_QQmlPropertyMap() {} |
| 43 | |
| 44 | private slots: |
| 45 | void initTestCase(); |
| 46 | |
| 47 | void insert(); |
| 48 | void operatorInsert(); |
| 49 | void operatorValue(); |
| 50 | void clear(); |
| 51 | void changed(); |
| 52 | void count(); |
| 53 | void controlledWrite(); |
| 54 | |
| 55 | void crashBug(); |
| 56 | void QTBUG_17868(); |
| 57 | void metaObjectAccessibility(); |
| 58 | void QTBUG_31226(); |
| 59 | void QTBUG_29836(); |
| 60 | void QTBUG_35233(); |
| 61 | void disallowExtending(); |
| 62 | void QTBUG_35906(); |
| 63 | void QTBUG_48136(); |
| 64 | void lookupsInSubTypes(); |
| 65 | }; |
| 66 | |
| 67 | class LazyPropertyMap : public QQmlPropertyMap, public QQmlParserStatus |
| 68 | { |
| 69 | Q_OBJECT |
| 70 | Q_INTERFACES(QQmlParserStatus) |
| 71 | |
| 72 | Q_PROPERTY(int someFixedProperty READ someFixedProperty WRITE setSomeFixedProperty NOTIFY someFixedPropertyChanged) |
| 73 | public: |
| 74 | LazyPropertyMap() |
| 75 | : QQmlPropertyMap(this, /*parent*/nullptr) |
| 76 | {} |
| 77 | |
| 78 | virtual void classBegin() {} |
| 79 | virtual void componentComplete() { |
| 80 | insert(QStringLiteral("lateProperty" ), QStringLiteral("lateValue" )); |
| 81 | } |
| 82 | |
| 83 | int someFixedProperty() const { return value; } |
| 84 | void setSomeFixedProperty(int v) { value = v; emit someFixedPropertyChanged(); } |
| 85 | |
| 86 | signals: |
| 87 | void someFixedPropertyChanged(); |
| 88 | |
| 89 | private: |
| 90 | int value = 0; |
| 91 | }; |
| 92 | |
| 93 | class SimplePropertyMap: public QQmlPropertyMap |
| 94 | { |
| 95 | Q_OBJECT |
| 96 | public: |
| 97 | SimplePropertyMap() : QQmlPropertyMap(this, nullptr) {} |
| 98 | }; |
| 99 | |
| 100 | void tst_QQmlPropertyMap::initTestCase() |
| 101 | { |
| 102 | QQmlDataTest::initTestCase(); |
| 103 | qmlRegisterType<LazyPropertyMap>(uri: "QTBUG_35233" , versionMajor: 1, versionMinor: 0, qmlName: "LazyPropertyMap" ); |
| 104 | qmlRegisterType<SimplePropertyMap>(uri: "Test" , versionMajor: 1, versionMinor: 0, qmlName: "SimplePropertyMap" ); |
| 105 | } |
| 106 | |
| 107 | void tst_QQmlPropertyMap::insert() |
| 108 | { |
| 109 | QQmlPropertyMap map; |
| 110 | map.insert(key: QLatin1String("key1" ),value: 100); |
| 111 | map.insert(key: QLatin1String("key2" ),value: 200); |
| 112 | QCOMPARE(map.keys().count(), 2); |
| 113 | QVERIFY(map.contains(QLatin1String("key1" ))); |
| 114 | |
| 115 | QCOMPARE(map.value(QLatin1String("key1" )), QVariant(100)); |
| 116 | QCOMPARE(map.value(QLatin1String("key2" )), QVariant(200)); |
| 117 | |
| 118 | map.insert(key: QLatin1String("key1" ),value: "Hello World" ); |
| 119 | QCOMPARE(map.value(QLatin1String("key1" )), QVariant("Hello World" )); |
| 120 | |
| 121 | //inserting property names same with existing method(signal, slot, method) names is not allowed |
| 122 | //QQmlPropertyMap has an invokable keys() method |
| 123 | QTest::ignoreMessage(type: QtWarningMsg, message: "Creating property with name \"keys\" is not permitted, conflicts with internal symbols." ); |
| 124 | map.insert(key: QLatin1String("keys" ), value: 1); |
| 125 | QCOMPARE(map.keys().count(), 2); |
| 126 | QVERIFY(!map.contains(QLatin1String("keys" ))); |
| 127 | QVERIFY(map.value(QLatin1String("keys" )).isNull()); |
| 128 | |
| 129 | //QQmlPropertyMap has a deleteLater() slot |
| 130 | QTest::ignoreMessage(type: QtWarningMsg, message: "Creating property with name \"deleteLater\" is not permitted, conflicts with internal symbols." ); |
| 131 | map.insert(key: QLatin1String("deleteLater" ), value: 1); |
| 132 | QCOMPARE(map.keys().count(), 2); |
| 133 | QVERIFY(!map.contains(QLatin1String("deleteLater" ))); |
| 134 | QVERIFY(map.value(QLatin1String("deleteLater" )).isNull()); |
| 135 | |
| 136 | //QQmlPropertyMap has an valueChanged() signal |
| 137 | QTest::ignoreMessage(type: QtWarningMsg, message: "Creating property with name \"valueChanged\" is not permitted, conflicts with internal symbols." ); |
| 138 | map.insert(key: QLatin1String("valueChanged" ), value: 1); |
| 139 | QCOMPARE(map.keys().count(), 2); |
| 140 | QVERIFY(!map.contains(QLatin1String("valueChanged" ))); |
| 141 | QVERIFY(map.value(QLatin1String("valueChanged" )).isNull()); |
| 142 | |
| 143 | //but 'valueChange' should be ok |
| 144 | map.insert(key: QLatin1String("valueChange" ), value: 1); |
| 145 | QCOMPARE(map.keys().count(), 3); |
| 146 | QVERIFY(map.contains(QLatin1String("valueChange" ))); |
| 147 | QCOMPARE(map.value(QLatin1String("valueChange" )), QVariant(1)); |
| 148 | |
| 149 | //'valueCHANGED' should be ok, too |
| 150 | map.insert(key: QLatin1String("valueCHANGED" ), value: 1); |
| 151 | QCOMPARE(map.keys().count(), 4); |
| 152 | QVERIFY(map.contains(QLatin1String("valueCHANGED" ))); |
| 153 | QCOMPARE(map.value(QLatin1String("valueCHANGED" )), QVariant(1)); |
| 154 | } |
| 155 | |
| 156 | void tst_QQmlPropertyMap::operatorInsert() |
| 157 | { |
| 158 | QQmlPropertyMap map; |
| 159 | map[QLatin1String("key1" )] = 100; |
| 160 | map[QLatin1String("key2" )] = 200; |
| 161 | QCOMPARE(map.keys().count(), 2); |
| 162 | |
| 163 | QCOMPARE(map.value(QLatin1String("key1" )), QVariant(100)); |
| 164 | QCOMPARE(map.value(QLatin1String("key2" )), QVariant(200)); |
| 165 | |
| 166 | map[QLatin1String("key1" )] = "Hello World" ; |
| 167 | QCOMPARE(map.value(QLatin1String("key1" )), QVariant("Hello World" )); |
| 168 | } |
| 169 | |
| 170 | void tst_QQmlPropertyMap::operatorValue() |
| 171 | { |
| 172 | QQmlPropertyMap map; |
| 173 | map.insert(key: QLatin1String("key1" ),value: 100); |
| 174 | map.insert(key: QLatin1String("key2" ),value: 200); |
| 175 | QCOMPARE(map.count(), 2); |
| 176 | QVERIFY(map.contains(QLatin1String("key1" ))); |
| 177 | |
| 178 | const QQmlPropertyMap &constMap = map; |
| 179 | |
| 180 | QCOMPARE(constMap.value(QLatin1String("key1" )), QVariant(100)); |
| 181 | QCOMPARE(constMap.value(QLatin1String("key2" )), QVariant(200)); |
| 182 | QCOMPARE(constMap[QLatin1String("key1" )], constMap.value(QLatin1String("key1" ))); |
| 183 | QCOMPARE(constMap[QLatin1String("key2" )], constMap.value(QLatin1String("key2" ))); |
| 184 | } |
| 185 | |
| 186 | void tst_QQmlPropertyMap::clear() |
| 187 | { |
| 188 | QQmlPropertyMap map; |
| 189 | map.insert(key: QLatin1String("key1" ),value: 100); |
| 190 | QCOMPARE(map.keys().count(), 1); |
| 191 | |
| 192 | QCOMPARE(map.value(QLatin1String("key1" )), QVariant(100)); |
| 193 | |
| 194 | map.clear(key: QLatin1String("key1" )); |
| 195 | QCOMPARE(map.keys().count(), 1); |
| 196 | QVERIFY(map.contains(QLatin1String("key1" ))); |
| 197 | QCOMPARE(map.value(QLatin1String("key1" )), QVariant()); |
| 198 | } |
| 199 | |
| 200 | void tst_QQmlPropertyMap::changed() |
| 201 | { |
| 202 | QQmlPropertyMap map; |
| 203 | QSignalSpy spy(&map, SIGNAL(valueChanged(QString,QVariant))); |
| 204 | map.insert(key: QLatin1String("key1" ),value: 100); |
| 205 | map.insert(key: QLatin1String("key2" ),value: 200); |
| 206 | QCOMPARE(spy.count(), 0); |
| 207 | |
| 208 | map.clear(key: QLatin1String("key1" )); |
| 209 | QCOMPARE(spy.count(), 0); |
| 210 | |
| 211 | //make changes in QML |
| 212 | QQmlEngine engine; |
| 213 | QQmlContext *ctxt = engine.rootContext(); |
| 214 | ctxt->setContextProperty(QLatin1String("testdata" ), &map); |
| 215 | QQmlComponent component(&engine); |
| 216 | component.setData("import QtQuick 2.0\nText { text: { testdata.key1 = 'Hello World'; 'X' } }" , |
| 217 | baseUrl: QUrl::fromLocalFile(localfile: "" )); |
| 218 | QVERIFY(component.isReady()); |
| 219 | QQuickText *txt = qobject_cast<QQuickText*>(object: component.create()); |
| 220 | QVERIFY(txt); |
| 221 | QCOMPARE(txt->text(), QString('X')); |
| 222 | QCOMPARE(spy.count(), 1); |
| 223 | QList<QVariant> arguments = spy.takeFirst(); |
| 224 | QCOMPARE(arguments.count(), 2); |
| 225 | QCOMPARE(arguments.at(0).toString(),QLatin1String("key1" )); |
| 226 | QCOMPARE(arguments.at(1).value<QVariant>(),QVariant("Hello World" )); |
| 227 | QCOMPARE(map.value(QLatin1String("key1" )), QVariant("Hello World" )); |
| 228 | } |
| 229 | |
| 230 | void tst_QQmlPropertyMap::count() |
| 231 | { |
| 232 | QQmlPropertyMap map; |
| 233 | QCOMPARE(map.isEmpty(), true); |
| 234 | map.insert(key: QLatin1String("key1" ),value: 100); |
| 235 | map.insert(key: QLatin1String("key2" ),value: 200); |
| 236 | QCOMPARE(map.count(), 2); |
| 237 | QCOMPARE(map.isEmpty(), false); |
| 238 | |
| 239 | map.insert(key: QLatin1String("key3" ),value: "Hello World" ); |
| 240 | QCOMPARE(map.count(), 3); |
| 241 | |
| 242 | //clearing doesn't remove the key |
| 243 | map.clear(key: QLatin1String("key3" )); |
| 244 | QCOMPARE(map.count(), 3); |
| 245 | QCOMPARE(map.size(), map.count()); |
| 246 | } |
| 247 | |
| 248 | class MyPropertyMap : public QQmlPropertyMap |
| 249 | { |
| 250 | Q_OBJECT |
| 251 | protected: |
| 252 | virtual QVariant updateValue(const QString &key, const QVariant &src) |
| 253 | { |
| 254 | if (key == QLatin1String("key1" )) { |
| 255 | // 'key1' must be all uppercase |
| 256 | const QString original(src.toString()); |
| 257 | return QVariant(original.toUpper()); |
| 258 | } |
| 259 | |
| 260 | return src; |
| 261 | } |
| 262 | }; |
| 263 | |
| 264 | void tst_QQmlPropertyMap::controlledWrite() |
| 265 | { |
| 266 | MyPropertyMap map; |
| 267 | QCOMPARE(map.isEmpty(), true); |
| 268 | |
| 269 | //make changes in QML |
| 270 | QQmlEngine engine; |
| 271 | QQmlContext *ctxt = engine.rootContext(); |
| 272 | ctxt->setContextProperty(QLatin1String("testdata" ), &map); |
| 273 | |
| 274 | const char *qmlSource = |
| 275 | "import QtQuick 2.0\n" |
| 276 | "Item { Component.onCompleted: { testdata.key1 = 'Hello World'; testdata.key2 = 'Goodbye' } }" ; |
| 277 | |
| 278 | QQmlComponent component(&engine); |
| 279 | component.setData(qmlSource, baseUrl: QUrl::fromLocalFile(localfile: "" )); |
| 280 | QVERIFY(component.isReady()); |
| 281 | |
| 282 | QObject *obj = component.create(); |
| 283 | QVERIFY(obj); |
| 284 | delete obj; |
| 285 | |
| 286 | QCOMPARE(map.value(QLatin1String("key1" )), QVariant("HELLO WORLD" )); |
| 287 | QCOMPARE(map.value(QLatin1String("key2" )), QVariant("Goodbye" )); |
| 288 | } |
| 289 | |
| 290 | void tst_QQmlPropertyMap::crashBug() |
| 291 | { |
| 292 | QQmlPropertyMap map; |
| 293 | |
| 294 | QQmlEngine engine; |
| 295 | QQmlContext context(&engine); |
| 296 | context.setContextProperty("map" , &map); |
| 297 | |
| 298 | QQmlComponent c(&engine); |
| 299 | c.setData("import QtQuick 2.0\nBinding { target: map; property: \"myProp\"; value: 10 + 23 }" ,baseUrl: QUrl()); |
| 300 | QObject *obj = c.create(context: &context); |
| 301 | delete obj; |
| 302 | } |
| 303 | |
| 304 | void tst_QQmlPropertyMap::QTBUG_17868() |
| 305 | { |
| 306 | QQmlPropertyMap map; |
| 307 | |
| 308 | QQmlEngine engine; |
| 309 | QQmlContext context(&engine); |
| 310 | context.setContextProperty("map" , &map); |
| 311 | map.insert(key: "key" , value: 1); |
| 312 | QQmlComponent c(&engine); |
| 313 | c.setData("import QtQuick 2.0\nItem {property bool error:false; Component.onCompleted: {try{ map.keys(); }catch(e) {error=true;}}}" ,baseUrl: QUrl()); |
| 314 | QObject *obj = c.create(context: &context); |
| 315 | QVERIFY(obj); |
| 316 | QVERIFY(!obj->property("error" ).toBool()); |
| 317 | delete obj; |
| 318 | |
| 319 | } |
| 320 | |
| 321 | class MyEnhancedPropertyMap : public QQmlPropertyMap |
| 322 | { |
| 323 | Q_OBJECT |
| 324 | public: |
| 325 | MyEnhancedPropertyMap() : QQmlPropertyMap(this, nullptr) {} |
| 326 | bool testSlotCalled() const { return m_testSlotCalled; } |
| 327 | |
| 328 | signals: |
| 329 | void testSignal(); |
| 330 | |
| 331 | public slots: |
| 332 | void testSlot() { m_testSlotCalled = true; } |
| 333 | |
| 334 | private: |
| 335 | bool m_testSlotCalled = false; |
| 336 | }; |
| 337 | |
| 338 | void tst_QQmlPropertyMap::metaObjectAccessibility() |
| 339 | { |
| 340 | QQmlTestMessageHandler messageHandler; |
| 341 | |
| 342 | QQmlEngine engine; |
| 343 | |
| 344 | MyEnhancedPropertyMap map; |
| 345 | |
| 346 | // Verify that signals and slots of QQmlPropertyMap-derived classes are accessible |
| 347 | QObject::connect(sender: &map, SIGNAL(testSignal()), receiver: &engine, SIGNAL(quit())); |
| 348 | QObject::connect(sender: &engine, SIGNAL(quit()), receiver: &map, SLOT(testSlot())); |
| 349 | |
| 350 | QCOMPARE(map.metaObject()->className(), "MyEnhancedPropertyMap" ); |
| 351 | |
| 352 | QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); |
| 353 | } |
| 354 | |
| 355 | void tst_QQmlPropertyMap::QTBUG_31226() |
| 356 | { |
| 357 | /* Instantiate a property map from QML, and verify that property changes |
| 358 | * made from C++ are visible from QML */ |
| 359 | QQmlEngine engine; |
| 360 | QQmlContext context(&engine); |
| 361 | qmlRegisterType<QQmlPropertyMap>(uri: "QTBUG_31226" , versionMajor: 1, versionMinor: 0, qmlName: "PropertyMap" ); |
| 362 | QQmlComponent c(&engine); |
| 363 | c.setData("import QtQuick 2.0\nimport QTBUG_31226 1.0\n" |
| 364 | "Item {\n" |
| 365 | " property string myProp\n" |
| 366 | " PropertyMap { id: qmlPropertyMap; objectName: \"qmlPropertyMap\" }\n" |
| 367 | " Timer { interval: 5; running: true; onTriggered: { myProp = qmlPropertyMap.greeting; } }\n" |
| 368 | "}" , |
| 369 | baseUrl: QUrl()); |
| 370 | QObject *obj = c.create(context: &context); |
| 371 | QVERIFY(obj); |
| 372 | |
| 373 | QQmlPropertyMap *qmlPropertyMap = obj->findChild<QQmlPropertyMap*>(aName: QString("qmlPropertyMap" )); |
| 374 | QVERIFY(qmlPropertyMap); |
| 375 | |
| 376 | qmlPropertyMap->insert(key: "greeting" , value: QString("Hello world!" )); |
| 377 | QTRY_COMPARE(obj->property("myProp" ).toString(), QString("Hello world!" )); |
| 378 | |
| 379 | delete obj; |
| 380 | |
| 381 | } |
| 382 | |
| 383 | void tst_QQmlPropertyMap::QTBUG_29836() |
| 384 | { |
| 385 | MyEnhancedPropertyMap map; |
| 386 | QCOMPARE(map.testSlotCalled(), false); |
| 387 | |
| 388 | QQmlEngine engine; |
| 389 | QQmlContext context(&engine); |
| 390 | context.setContextProperty("enhancedMap" , &map); |
| 391 | QQmlComponent c(&engine); |
| 392 | c.setData("import QtQuick 2.0\n" |
| 393 | "Item {\n" |
| 394 | " Timer { interval: 5; running: true; onTriggered: enhancedMap.testSlot() }\n" |
| 395 | "}" , |
| 396 | baseUrl: QUrl()); |
| 397 | QObject *obj = c.create(context: &context); |
| 398 | QVERIFY(obj); |
| 399 | |
| 400 | QTRY_COMPARE(map.testSlotCalled(), true); |
| 401 | |
| 402 | delete obj; |
| 403 | |
| 404 | } |
| 405 | |
| 406 | void tst_QQmlPropertyMap::QTBUG_35233() |
| 407 | { |
| 408 | QQmlEngine engine; |
| 409 | QQmlComponent component(&engine); |
| 410 | component.setData("import QtQml 2.0\n" |
| 411 | "import QTBUG_35233 1.0\n" |
| 412 | "QtObject {\n" |
| 413 | " property QtObject testMap: LazyPropertyMap {\n" |
| 414 | " id: map\n" |
| 415 | " }\n" |
| 416 | " property QtObject sibling: QtObject {\n" |
| 417 | " objectName: \"sibling\"\n" |
| 418 | " property string testValue: map.lateProperty\n" |
| 419 | " }\n" |
| 420 | "}" , baseUrl: QUrl()); |
| 421 | QScopedPointer<QObject> obj(component.create()); |
| 422 | QVERIFY(!obj.isNull()); |
| 423 | |
| 424 | QObject *sibling = obj->findChild<QObject*>(aName: "sibling" ); |
| 425 | QVERIFY(sibling); |
| 426 | QCOMPARE(sibling->property("testValue" ).toString(), QString("lateValue" )); |
| 427 | } |
| 428 | |
| 429 | void tst_QQmlPropertyMap::disallowExtending() |
| 430 | { |
| 431 | QQmlEngine engine; |
| 432 | QQmlComponent component(&engine); |
| 433 | component.setData("import QtQml 2.0\n" |
| 434 | "import QTBUG_35233 1.0\n" |
| 435 | "LazyPropertyMap {\n" |
| 436 | " id: blah\n" |
| 437 | " someFixedProperty: 42\n" |
| 438 | "}\n" , baseUrl: QUrl()); |
| 439 | QScopedPointer<QObject> obj(component.create()); |
| 440 | QVERIFY(!obj.isNull()); |
| 441 | |
| 442 | component.setData("import QtQml 2.0\n" |
| 443 | "import QTBUG_35233 1.0\n" |
| 444 | "LazyPropertyMap {\n" |
| 445 | " id: blah\n" |
| 446 | " property int someNewProperty;\n" |
| 447 | "}\n" , baseUrl: QUrl()); |
| 448 | obj.reset(other: component.create()); |
| 449 | QVERIFY(obj.isNull()); |
| 450 | QCOMPARE(component.errors().count(), 1); |
| 451 | QCOMPARE(component.errors().at(0).toString(), QStringLiteral("<Unknown File>:3:1: Fully dynamic types cannot declare new properties." )); |
| 452 | } |
| 453 | |
| 454 | void tst_QQmlPropertyMap::QTBUG_35906() |
| 455 | { |
| 456 | QQmlEngine engine; |
| 457 | QQmlComponent component(&engine); |
| 458 | component.setData("import QtQml 2.0\n" |
| 459 | "import QTBUG_35233 1.0\n" |
| 460 | "QtObject {\n" |
| 461 | " property int testValue: mapById.someFixedProperty\n" |
| 462 | "\n" |
| 463 | " property QtObject maProperty: LazyPropertyMap {\n" |
| 464 | " id: mapById\n" |
| 465 | " someFixedProperty: 42\n" |
| 466 | " }\n" |
| 467 | "}\n" , baseUrl: QUrl()); |
| 468 | QScopedPointer<QObject> obj(component.create()); |
| 469 | QVERIFY(!obj.isNull()); |
| 470 | QVariant value = obj->property(name: "testValue" ); |
| 471 | QCOMPARE(value.type(), QVariant::Int); |
| 472 | QCOMPARE(value.toInt(), 42); |
| 473 | } |
| 474 | |
| 475 | void tst_QQmlPropertyMap::QTBUG_48136() |
| 476 | { |
| 477 | static const char key[] = "mykey" ; |
| 478 | QQmlPropertyMap map; |
| 479 | |
| 480 | // |
| 481 | // Test that the notify signal is emitted correctly |
| 482 | // |
| 483 | |
| 484 | const int propIndex = map.metaObject()->indexOfProperty(name: key); |
| 485 | const QMetaProperty prop = map.metaObject()->property(index: propIndex); |
| 486 | QSignalSpy notifySpy(&map, QByteArray::number(QSIGNAL_CODE) + prop.notifySignal().methodSignature()); |
| 487 | |
| 488 | map.insert(key, value: 42); |
| 489 | QCOMPARE(notifySpy.count(), 1); |
| 490 | map.insert(key, value: 43); |
| 491 | QCOMPARE(notifySpy.count(), 2); |
| 492 | map.insert(key, value: 43); |
| 493 | QCOMPARE(notifySpy.count(), 2); |
| 494 | map.insert(key, value: 44); |
| 495 | QCOMPARE(notifySpy.count(), 3); |
| 496 | |
| 497 | // |
| 498 | // Test that the valueChanged signal is emitted correctly |
| 499 | // |
| 500 | QSignalSpy valueChangedSpy(&map, &QQmlPropertyMap::valueChanged); |
| 501 | map.setProperty(name: key, value: 44); |
| 502 | QCOMPARE(valueChangedSpy.count(), 0); |
| 503 | map.setProperty(name: key, value: 45); |
| 504 | QCOMPARE(valueChangedSpy.count(), 1); |
| 505 | map.setProperty(name: key, value: 45); |
| 506 | QCOMPARE(valueChangedSpy.count(), 1); |
| 507 | } |
| 508 | |
| 509 | void tst_QQmlPropertyMap::lookupsInSubTypes() |
| 510 | { |
| 511 | QQmlEngine engine; |
| 512 | QQmlComponent component(&engine, testFileUrl(fileName: "PropertyMapSubType.qml" )); |
| 513 | QTest::ignoreMessage(type: QtDebugMsg, message: "expected output" ); |
| 514 | QScopedPointer<QObject> object(component.create()); |
| 515 | QVERIFY(!object.isNull()); |
| 516 | QCOMPARE(object->property("newProperty" ).toInt(), 42); |
| 517 | } |
| 518 | |
| 519 | QTEST_MAIN(tst_QQmlPropertyMap) |
| 520 | |
| 521 | #include "tst_qqmlpropertymap.moc" |
| 522 | |