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 <QtQml/qqmlengine.h> |
30 | #include <QtQml/qqmlcomponent.h> |
31 | #include <QtQml/qqmlincubator.h> |
32 | #include <QtCore/qcoreapplication.h> |
33 | #include <QtCore/qfile.h> |
34 | #include <QtCore/qdebug.h> |
35 | #include <QtCore/qfileinfo.h> |
36 | #include <QtCore/qdir.h> |
37 | #include <QtCore/qscopeguard.h> |
38 | #include <QSignalSpy> |
39 | #include <QFont> |
40 | #include <QQmlFileSelector> |
41 | #include <QFileSelector> |
42 | #include <QEasingCurve> |
43 | #include <QScopeGuard> |
44 | |
45 | #include <private/qqmlproperty_p.h> |
46 | #include <private/qqmlmetatype_p.h> |
47 | #include <private/qqmlglobal_p.h> |
48 | #include <private/qqmlscriptstring_p.h> |
49 | #include <private/qqmlvmemetaobject_p.h> |
50 | #include <private/qqmlcomponent_p.h> |
51 | #include <private/qqmltype_p_p.h> |
52 | |
53 | #include "testtypes.h" |
54 | #include "testhttpserver.h" |
55 | |
56 | #include "../../shared/util.h" |
57 | |
58 | #if defined(Q_OS_MAC) |
59 | #include <unistd.h> |
60 | #endif |
61 | |
62 | DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES) |
63 | |
64 | static inline bool isCaseSensitiveFileSystem(const QString &path) { |
65 | Q_UNUSED(path) |
66 | #if defined(Q_OS_MAC) |
67 | return pathconf(path.toLatin1().constData(), _PC_CASE_SENSITIVE); |
68 | #elif defined(Q_OS_WIN) |
69 | return false; |
70 | #else |
71 | return true; |
72 | #endif |
73 | } |
74 | |
75 | /* |
76 | This test case covers QML language issues. This covers everything that does not |
77 | involve evaluating ECMAScript expressions and bindings. |
78 | |
79 | Evaluation of expressions and bindings is covered in qmlecmascript |
80 | */ |
81 | class tst_qqmllanguage : public QQmlDataTest |
82 | { |
83 | Q_OBJECT |
84 | |
85 | private slots: |
86 | void initTestCase(); |
87 | void cleanupTestCase(); |
88 | |
89 | void errors_data(); |
90 | void errors(); |
91 | |
92 | void insertedSemicolon_data(); |
93 | void insertedSemicolon(); |
94 | |
95 | void simpleObject(); |
96 | void simpleContainer(); |
97 | void interfaceProperty(); |
98 | void interfaceQList(); |
99 | void assignObjectToSignal(); |
100 | void assignObjectToVariant(); |
101 | void assignLiteralSignalProperty(); |
102 | void assignQmlComponent(); |
103 | void assignBasicTypes(); |
104 | void assignTypeExtremes(); |
105 | void assignCompositeToType(); |
106 | void assignLiteralToVariant(); |
107 | void assignLiteralToVar(); |
108 | void assignLiteralToJSValue(); |
109 | void assignNullStrings(); |
110 | void bindJSValueToVar(); |
111 | void bindJSValueToVariant(); |
112 | void bindJSValueToType(); |
113 | void bindTypeToJSValue(); |
114 | void customParserTypes(); |
115 | void customParserTypeInInlineComponent(); |
116 | void rootAsQmlComponent(); |
117 | void rootItemIsComponent(); |
118 | void inlineQmlComponents(); |
119 | void idProperty(); |
120 | void autoNotifyConnection(); |
121 | void assignSignal(); |
122 | void assignSignalFunctionExpression(); |
123 | void overrideSignal_data(); |
124 | void overrideSignal(); |
125 | void dynamicProperties(); |
126 | void dynamicPropertiesNested(); |
127 | void listProperties(); |
128 | void listPropertiesInheritanceNoCrash(); |
129 | void badListItemType(); |
130 | void dynamicObjectProperties(); |
131 | void dynamicSignalsAndSlots(); |
132 | void simpleBindings(); |
133 | void noDoubleEvaluationForFlushedBindings_data(); |
134 | void noDoubleEvaluationForFlushedBindings(); |
135 | void autoComponentCreation(); |
136 | void autoComponentCreationInGroupProperty(); |
137 | void propertyValueSource(); |
138 | void requiredProperty(); |
139 | void requiredPropertyFromCpp_data(); |
140 | void requiredPropertyFromCpp(); |
141 | void attachedProperties(); |
142 | void dynamicObjects(); |
143 | void customVariantTypes(); |
144 | void valueTypes(); |
145 | void cppnamespace(); |
146 | void aliasProperties(); |
147 | void aliasPropertiesAndSignals(); |
148 | void aliasPropertyChangeSignals(); |
149 | void qtbug_89822(); |
150 | void componentCompositeType(); |
151 | void i18n(); |
152 | void i18n_data(); |
153 | void onCompleted(); |
154 | void onDestruction(); |
155 | void scriptString(); |
156 | void scriptStringJs(); |
157 | void scriptStringWithoutSourceCode(); |
158 | void scriptStringComparison(); |
159 | void defaultPropertyListOrder(); |
160 | void declaredPropertyValues(); |
161 | void dontDoubleCallClassBegin(); |
162 | void reservedWords_data(); |
163 | void reservedWords(); |
164 | void inlineAssignmentsOverrideBindings(); |
165 | void nestedComponentRoots(); |
166 | void registrationOrder(); |
167 | void readonly(); |
168 | void readonlyObjectProperties(); |
169 | void receivers(); |
170 | void registeredCompositeType(); |
171 | void registeredCompositeTypeWithEnum(); |
172 | void registeredCompositeTypeWithAttachedProperty(); |
173 | void implicitImportsLast(); |
174 | |
175 | void basicRemote_data(); |
176 | void basicRemote(); |
177 | void importsBuiltin_data(); |
178 | void importsBuiltin(); |
179 | void importsLocal_data(); |
180 | void importsLocal(); |
181 | void importsRemote_data(); |
182 | void importsRemote(); |
183 | void importsInstalled_data(); |
184 | void importsInstalled(); |
185 | void importsInstalledRemote_data(); |
186 | void importsInstalledRemote(); |
187 | void importsPath_data(); |
188 | void importsPath(); |
189 | void importsOrder_data(); |
190 | void importsOrder(); |
191 | void importIncorrectCase(); |
192 | void importJs_data(); |
193 | void importJs(); |
194 | void importJsModule_data(); |
195 | void importJsModule(); |
196 | void explicitSelfImport(); |
197 | void importInternalType(); |
198 | |
199 | void qmlAttachedPropertiesObjectMethod(); |
200 | void customOnProperty(); |
201 | void variantNotify(); |
202 | |
203 | void revisions(); |
204 | void revisionOverloads(); |
205 | |
206 | void subclassedUncreateableRevision_data(); |
207 | void subclassedUncreateableRevision(); |
208 | |
209 | void subclassedExtendedUncreateableRevision_data(); |
210 | void subclassedExtendedUncreateableRevision(); |
211 | |
212 | void uncreatableTypesAsProperties(); |
213 | |
214 | void propertyInit(); |
215 | void remoteLoadCrash(); |
216 | void signalWithDefaultArg(); |
217 | void signalParameterTypes(); |
218 | void functionParameterTypes(); |
219 | |
220 | // regression tests for crashes |
221 | void crash1(); |
222 | void crash2(); |
223 | |
224 | void globalEnums(); |
225 | void lowercaseEnumRuntime_data(); |
226 | void lowercaseEnumRuntime(); |
227 | void lowercaseEnumCompileTime_data(); |
228 | void lowercaseEnumCompileTime(); |
229 | void scopedEnum(); |
230 | void scopedEnumsWithNameClash(); |
231 | void scopedEnumsWithResolvedNameClash(); |
232 | void qmlEnums(); |
233 | void literals_data(); |
234 | void literals(); |
235 | |
236 | void objectDeletionNotify_data(); |
237 | void objectDeletionNotify(); |
238 | |
239 | void scopedProperties(); |
240 | |
241 | void deepProperty(); |
242 | |
243 | void compositeSingletonProperties(); |
244 | void compositeSingletonSameEngine(); |
245 | void compositeSingletonDifferentEngine(); |
246 | void compositeSingletonNonTypeError(); |
247 | void compositeSingletonQualifiedNamespace(); |
248 | void compositeSingletonModule(); |
249 | void compositeSingletonModuleVersioned(); |
250 | void compositeSingletonModuleQualified(); |
251 | void compositeSingletonInstantiateError(); |
252 | void compositeSingletonDynamicPropertyError(); |
253 | void compositeSingletonDynamicSignalAndJavaScriptPragma(); |
254 | void compositeSingletonQmlRegisterTypeError(); |
255 | void compositeSingletonQmldirNoPragmaError(); |
256 | void compositeSingletonQmlDirError(); |
257 | void compositeSingletonRemote(); |
258 | void compositeSingletonSelectors(); |
259 | void compositeSingletonRegistered(); |
260 | void compositeSingletonCircular(); |
261 | |
262 | void singletonsHaveContextAndEngine(); |
263 | |
264 | void customParserBindingScopes(); |
265 | void customParserEvaluateEnum(); |
266 | void customParserProperties(); |
267 | void customParserWithExtendedObject(); |
268 | void nestedCustomParsers(); |
269 | |
270 | void preservePropertyCacheOnGroupObjects(); |
271 | void propertyCacheInSync(); |
272 | |
273 | void rootObjectInCreationNotForSubObjects(); |
274 | void lazyDeferredSubObject(); |
275 | void deferredProperties(); |
276 | void executeDeferredPropertiesOnce(); |
277 | |
278 | void noChildEvents(); |
279 | |
280 | void earlyIdObjectAccess(); |
281 | |
282 | void deleteSingletons(); |
283 | |
284 | void arrayBuffer_data(); |
285 | void arrayBuffer(); |
286 | |
287 | void defaultListProperty(); |
288 | void namespacedPropertyTypes(); |
289 | |
290 | void qmlTypeCanBeResolvedByName_data(); |
291 | void qmlTypeCanBeResolvedByName(); |
292 | |
293 | void instanceof_data(); |
294 | void instanceof(); |
295 | |
296 | void concurrentLoadQmlDir(); |
297 | |
298 | void accessDeletedObject(); |
299 | |
300 | void lowercaseTypeNames(); |
301 | |
302 | void thisInQmlScope(); |
303 | |
304 | void valueTypeGroupPropertiesInBehavior(); |
305 | |
306 | void retrieveQmlTypeId(); |
307 | |
308 | void polymorphicFunctionLookup(); |
309 | void anchorsToParentInPropertyChanges(); |
310 | |
311 | void typeWrapperToVariant(); |
312 | |
313 | void extendedForeignTypes(); |
314 | |
315 | void inlineComponent(); |
316 | void inlineComponent_data(); |
317 | void inlineComponentReferenceCycle_data(); |
318 | void inlineComponentReferenceCycle(); |
319 | void nestedInlineComponentNotAllowed(); |
320 | void inlineComponentStaticTypeResolution(); |
321 | void inlineComponentInSingleton(); |
322 | void nonExistingInlineComponent_data(); |
323 | void nonExistingInlineComponent(); |
324 | void inlineComponentFoundBeforeOtherImports(); |
325 | void inlineComponentDuplicateNameError(); |
326 | |
327 | void selfReference(); |
328 | void selfReferencingSingleton(); |
329 | |
330 | void listContainingDeletedObject(); |
331 | void overrideSingleton(); |
332 | |
333 | void arrayToContainer(); |
334 | void qualifiedScopeInCustomParser(); |
335 | void accessNullPointerPropertyCache(); |
336 | void bareInlineComponent(); |
337 | |
338 | void hangOnWarning(); |
339 | |
340 | void ambiguousContainingType(); |
341 | |
342 | private: |
343 | QQmlEngine engine; |
344 | QStringList defaultImportPathList; |
345 | |
346 | void testType(const QString& qml, const QString& type, const QString& error, bool partialMatch = false); |
347 | |
348 | // When calling into JavaScript, the specific type of the return value can differ if that return |
349 | // value is a number. This is not only the case for non-integral numbers, or numbers that do not |
350 | // fit into the (signed) integer range, but it also depends on which optimizations are run. So, |
351 | // to check if the return value is of a number type, use this method instead of checking against |
352 | // a specific userType. |
353 | static bool isJSNumberType(int userType) |
354 | { |
355 | return userType == (int) QVariant::Int |
356 | || userType == (int) QVariant::UInt |
357 | || userType == (int) QVariant::Double; |
358 | } |
359 | |
360 | void getSingletonInstance(QQmlEngine& engine, const char* fileName, const char* propertyName, QObject** result /* out */); |
361 | void getSingletonInstance(QObject* o, const char* propertyName, QObject** result /* out */); |
362 | }; |
363 | |
364 | #define DETERMINE_ERRORS(errorfile,expected,actual)\ |
365 | QList<QByteArray> expected; \ |
366 | QList<QByteArray> actual; \ |
367 | do { \ |
368 | QFile file(testFile(errorfile)); \ |
369 | QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); \ |
370 | QByteArray data = file.readAll(); \ |
371 | file.close(); \ |
372 | expected = data.split('\n'); \ |
373 | expected.removeAll(QByteArray("")); \ |
374 | QList<QQmlError> errors = component.errors(); \ |
375 | for (int ii = 0; ii < errors.count(); ++ii) { \ |
376 | const QQmlError &error = errors.at(ii); \ |
377 | QByteArray errorStr = QByteArray::number(error.line()) + ':' + \ |
378 | QByteArray::number(error.column()) + ':' + \ |
379 | error.description().toUtf8(); \ |
380 | actual << errorStr; \ |
381 | } \ |
382 | } while (false); |
383 | |
384 | #define VERIFY_ERRORS(errorfile) \ |
385 | if (!errorfile) { \ |
386 | if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) \ |
387 | qWarning() << "Unexpected Errors:" << component.errors(); \ |
388 | QVERIFY2(!component.isError(), qPrintable(component.errorString())); \ |
389 | QVERIFY(component.errors().isEmpty()); \ |
390 | } else { \ |
391 | DETERMINE_ERRORS(errorfile,expected,actual);\ |
392 | if (qgetenv("DEBUG") != "" && expected != actual) \ |
393 | qWarning() << "Expected:" << expected << "Actual:" << actual; \ |
394 | if (qgetenv("QDECLARATIVELANGUAGE_UPDATEERRORS") != "" && expected != actual) {\ |
395 | QFile file(testFile(errorfile)); \ |
396 | QVERIFY(file.open(QIODevice::WriteOnly)); \ |
397 | for (int ii = 0; ii < actual.count(); ++ii) { \ |
398 | file.write(actual.at(ii)); file.write("\n"); \ |
399 | } \ |
400 | file.close(); \ |
401 | } else { \ |
402 | QCOMPARE(actual, expected); \ |
403 | } \ |
404 | } |
405 | |
406 | void tst_qqmllanguage::cleanupTestCase() |
407 | { |
408 | if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) |
409 | QVERIFY(QFile::remove(testFile(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")))); |
410 | } |
411 | |
412 | void tst_qqmllanguage::insertedSemicolon_data() |
413 | { |
414 | QTest::addColumn<QString>(name: "file"); |
415 | QTest::addColumn<QString>(name: "errorFile"); |
416 | QTest::addColumn<bool>(name: "create"); |
417 | |
418 | QTest::newRow(dataTag: "insertedSemicolon.1") << "insertedSemicolon.1.qml"<< "insertedSemicolon.1.errors.txt"<< false; |
419 | } |
420 | |
421 | void tst_qqmllanguage::insertedSemicolon() |
422 | { |
423 | QFETCH(QString, file); |
424 | QFETCH(QString, errorFile); |
425 | QFETCH(bool, create); |
426 | |
427 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
428 | |
429 | QScopedPointer<QObject> object; |
430 | |
431 | if(create) { |
432 | object.reset(other: component.create()); |
433 | QVERIFY(object.isNull()); |
434 | } |
435 | |
436 | VERIFY_ERRORS(errorFile.toLatin1().constData()); |
437 | } |
438 | |
439 | void tst_qqmllanguage::errors_data() |
440 | { |
441 | QTest::addColumn<QString>(name: "file"); |
442 | QTest::addColumn<QString>(name: "errorFile"); |
443 | QTest::addColumn<bool>(name: "create"); |
444 | |
445 | QTest::newRow(dataTag: "nonexistantProperty.1") << "nonexistantProperty.1.qml"<< "nonexistantProperty.1.errors.txt"<< false; |
446 | QTest::newRow(dataTag: "nonexistantProperty.2") << "nonexistantProperty.2.qml"<< "nonexistantProperty.2.errors.txt"<< false; |
447 | QTest::newRow(dataTag: "nonexistantProperty.3") << "nonexistantProperty.3.qml"<< "nonexistantProperty.3.errors.txt"<< false; |
448 | QTest::newRow(dataTag: "nonexistantProperty.4") << "nonexistantProperty.4.qml"<< "nonexistantProperty.4.errors.txt"<< false; |
449 | QTest::newRow(dataTag: "nonexistantProperty.5") << "nonexistantProperty.5.qml"<< "nonexistantProperty.5.errors.txt"<< false; |
450 | QTest::newRow(dataTag: "nonexistantProperty.6") << "nonexistantProperty.6.qml"<< "nonexistantProperty.6.errors.txt"<< false; |
451 | QTest::newRow(dataTag: "nonexistantProperty.7") << "nonexistantProperty.7.qml"<< "nonexistantProperty.7.errors.txt"<< false; |
452 | QTest::newRow(dataTag: "nonexistantProperty.8") << "nonexistantProperty.8.qml"<< "nonexistantProperty.8.errors.txt"<< false; |
453 | |
454 | QTest::newRow(dataTag: "wrongType (string for int)") << "wrongType.1.qml"<< "wrongType.1.errors.txt"<< false; |
455 | QTest::newRow(dataTag: "wrongType (int for bool)") << "wrongType.2.qml"<< "wrongType.2.errors.txt"<< false; |
456 | QTest::newRow(dataTag: "wrongType (bad rect)") << "wrongType.3.qml"<< "wrongType.3.errors.txt"<< false; |
457 | |
458 | QTest::newRow(dataTag: "wrongType (invalid enum)") << "wrongType.4.qml"<< "wrongType.4.errors.txt"<< false; |
459 | QTest::newRow(dataTag: "wrongType (int for uint)") << "wrongType.5.qml"<< "wrongType.5.errors.txt"<< false; |
460 | QTest::newRow(dataTag: "wrongType (string for real)") << "wrongType.6.qml"<< "wrongType.6.errors.txt"<< false; |
461 | QTest::newRow(dataTag: "wrongType (int for color)") << "wrongType.7.qml"<< "wrongType.7.errors.txt"<< false; |
462 | QTest::newRow(dataTag: "wrongType (int for date)") << "wrongType.8.qml"<< "wrongType.8.errors.txt"<< false; |
463 | QTest::newRow(dataTag: "wrongType (int for time)") << "wrongType.9.qml"<< "wrongType.9.errors.txt"<< false; |
464 | QTest::newRow(dataTag: "wrongType (int for datetime)") << "wrongType.10.qml"<< "wrongType.10.errors.txt"<< false; |
465 | QTest::newRow(dataTag: "wrongType (string for point)") << "wrongType.11.qml"<< "wrongType.11.errors.txt"<< false; |
466 | QTest::newRow(dataTag: "wrongType (color for size)") << "wrongType.12.qml"<< "wrongType.12.errors.txt"<< false; |
467 | QTest::newRow(dataTag: "wrongType (number string for int)") << "wrongType.13.qml"<< "wrongType.13.errors.txt"<< false; |
468 | QTest::newRow(dataTag: "wrongType (int for string)") << "wrongType.14.qml"<< "wrongType.14.errors.txt"<< false; |
469 | QTest::newRow(dataTag: "wrongType (int for url)") << "wrongType.15.qml"<< "wrongType.15.errors.txt"<< false; |
470 | QTest::newRow(dataTag: "wrongType (invalid object)") << "wrongType.16.qml"<< "wrongType.16.errors.txt"<< false; |
471 | QTest::newRow(dataTag: "wrongType (int for enum)") << "wrongType.17.qml"<< "wrongType.17.errors.txt"<< false; |
472 | |
473 | QTest::newRow(dataTag: "readOnly.1") << "readOnly.1.qml"<< "readOnly.1.errors.txt"<< false; |
474 | QTest::newRow(dataTag: "readOnly.2") << "readOnly.2.qml"<< "readOnly.2.errors.txt"<< false; |
475 | QTest::newRow(dataTag: "readOnly.3") << "readOnly.3.qml"<< "readOnly.3.errors.txt"<< false; |
476 | QTest::newRow(dataTag: "readOnly.4") << "readOnly.4.qml"<< "readOnly.4.errors.txt"<< false; |
477 | QTest::newRow(dataTag: "readOnly.5") << "readOnly.5.qml"<< "readOnly.5.errors.txt"<< false; |
478 | |
479 | QTest::newRow(dataTag: "listAssignment.1") << "listAssignment.1.qml"<< "listAssignment.1.errors.txt"<< false; |
480 | QTest::newRow(dataTag: "listAssignment.2") << "listAssignment.2.qml"<< "listAssignment.2.errors.txt"<< false; |
481 | QTest::newRow(dataTag: "listAssignment.3") << "listAssignment.3.qml"<< "listAssignment.3.errors.txt"<< false; |
482 | |
483 | QTest::newRow(dataTag: "invalidID.1") << "invalidID.qml"<< "invalidID.errors.txt"<< false; |
484 | QTest::newRow(dataTag: "invalidID.2") << "invalidID.2.qml"<< "invalidID.2.errors.txt"<< false; |
485 | QTest::newRow(dataTag: "invalidID.3") << "invalidID.3.qml"<< "invalidID.3.errors.txt"<< false; |
486 | QTest::newRow(dataTag: "invalidID.4") << "invalidID.4.qml"<< "invalidID.4.errors.txt"<< false; |
487 | QTest::newRow(dataTag: "invalidID.5") << "invalidID.5.qml"<< "invalidID.5.errors.txt"<< false; |
488 | QTest::newRow(dataTag: "invalidID.6") << "invalidID.6.qml"<< "invalidID.6.errors.txt"<< false; |
489 | QTest::newRow(dataTag: "invalidID.7") << "invalidID.7.qml"<< "invalidID.7.errors.txt"<< false; |
490 | QTest::newRow(dataTag: "invalidID.8") << "invalidID.8.qml"<< "invalidID.8.errors.txt"<< false; |
491 | QTest::newRow(dataTag: "invalidID.9") << "invalidID.9.qml"<< "invalidID.9.errors.txt"<< false; |
492 | QTest::newRow(dataTag: "invalidID.10") << "invalidID.10.qml"<< "invalidID.10.errors.txt"<< false; |
493 | |
494 | QTest::newRow(dataTag: "scriptString.1") << "scriptString.1.qml"<< "scriptString.1.errors.txt"<< false; |
495 | QTest::newRow(dataTag: "scriptString.2") << "scriptString.2.qml"<< "scriptString.2.errors.txt"<< false; |
496 | |
497 | QTest::newRow(dataTag: "unsupportedProperty") << "unsupportedProperty.qml"<< "unsupportedProperty.errors.txt"<< false; |
498 | QTest::newRow(dataTag: "nullDotProperty") << "nullDotProperty.qml"<< "nullDotProperty.errors.txt"<< true; |
499 | QTest::newRow(dataTag: "fakeDotProperty") << "fakeDotProperty.qml"<< "fakeDotProperty.errors.txt"<< false; |
500 | QTest::newRow(dataTag: "duplicateIDs") << "duplicateIDs.qml"<< "duplicateIDs.errors.txt"<< false; |
501 | QTest::newRow(dataTag: "unregisteredObject") << "unregisteredObject.qml"<< "unregisteredObject.errors.txt"<< false; |
502 | QTest::newRow(dataTag: "empty") << "empty.qml"<< "empty.errors.txt"<< false; |
503 | QTest::newRow(dataTag: "missingObject") << "missingObject.qml"<< "missingObject.errors.txt"<< false; |
504 | QTest::newRow(dataTag: "failingComponent") << "failingComponentTest.qml"<< "failingComponent.errors.txt"<< false; |
505 | QTest::newRow(dataTag: "missingSignal") << "missingSignal.qml"<< "missingSignal.errors.txt"<< false; |
506 | QTest::newRow(dataTag: "missingSignal2") << "missingSignal.2.qml"<< "missingSignal.2.errors.txt"<< false; |
507 | QTest::newRow(dataTag: "finalOverride") << "finalOverride.qml"<< "finalOverride.errors.txt"<< false; |
508 | QTest::newRow(dataTag: "customParserIdNotAllowed") << "customParserIdNotAllowed.qml"<< "customParserIdNotAllowed.errors.txt"<< false; |
509 | |
510 | QTest::newRow(dataTag: "nullishCoalescing_LHS_Or") << "nullishCoalescing_LHS_Or.qml"<< "nullishCoalescing_LHS_Or.errors.txt"<< false; |
511 | QTest::newRow(dataTag: "nullishCoalescing_LHS_And") << "nullishCoalescing_LHS_And.qml"<< "nullishCoalescing_LHS_And.errors.txt"<< false; |
512 | QTest::newRow(dataTag: "nullishCoalescing_RHS_Or") << "nullishCoalescing_RHS_Or.qml"<< "nullishCoalescing_RHS_Or.errors.txt"<< false; |
513 | QTest::newRow(dataTag: "nullishCoalescing_RHS_And") << "nullishCoalescing_RHS_And.qml"<< "nullishCoalescing_RHS_And.errors.txt"<< false; |
514 | |
515 | QTest::newRow(dataTag: "invalidGroupedProperty.1") << "invalidGroupedProperty.1.qml"<< "invalidGroupedProperty.1.errors.txt"<< false; |
516 | QTest::newRow(dataTag: "invalidGroupedProperty.2") << "invalidGroupedProperty.2.qml"<< "invalidGroupedProperty.2.errors.txt"<< false; |
517 | QTest::newRow(dataTag: "invalidGroupedProperty.3") << "invalidGroupedProperty.3.qml"<< "invalidGroupedProperty.3.errors.txt"<< false; |
518 | QTest::newRow(dataTag: "invalidGroupedProperty.4") << "invalidGroupedProperty.4.qml"<< "invalidGroupedProperty.4.errors.txt"<< false; |
519 | QTest::newRow(dataTag: "invalidGroupedProperty.5") << "invalidGroupedProperty.5.qml"<< "invalidGroupedProperty.5.errors.txt"<< false; |
520 | QTest::newRow(dataTag: "invalidGroupedProperty.6") << "invalidGroupedProperty.6.qml"<< "invalidGroupedProperty.6.errors.txt"<< false; |
521 | QTest::newRow(dataTag: "invalidGroupedProperty.7") << "invalidGroupedProperty.7.qml"<< "invalidGroupedProperty.7.errors.txt"<< true; |
522 | QTest::newRow(dataTag: "invalidGroupedProperty.8") << "invalidGroupedProperty.8.qml"<< "invalidGroupedProperty.8.errors.txt"<< false; |
523 | QTest::newRow(dataTag: "invalidGroupedProperty.9") << "invalidGroupedProperty.9.qml"<< "invalidGroupedProperty.9.errors.txt"<< false; |
524 | QTest::newRow(dataTag: "invalidGroupedProperty.10") << "invalidGroupedProperty.10.qml"<< "invalidGroupedProperty.10.errors.txt"<< false; |
525 | |
526 | QTest::newRow(dataTag: "importNamespaceConflict") << "importNamespaceConflict.qml"<< "importNamespaceConflict.errors.txt"<< false; |
527 | QTest::newRow(dataTag: "importVersionMissing (builtin)") << "importVersionMissingBuiltIn.qml"<< "importVersionMissingBuiltIn.errors.txt"<< false; |
528 | QTest::newRow(dataTag: "importVersionMissing (installed)") << "importVersionMissingInstalled.qml"<< "importVersionMissingInstalled.errors.txt"<< false; |
529 | QTest::newRow(dataTag: "importNonExist (installed)") << "importNonExist.qml"<< "importNonExist.errors.txt"<< false; |
530 | QTest::newRow(dataTag: "importNonExistOlder (installed)") << "importNonExistOlder.qml"<< "importNonExistOlder.errors.txt"<< false; |
531 | QTest::newRow(dataTag: "importNewerVersion (installed)") << "importNewerVersion.qml"<< "importNewerVersion.errors.txt"<< false; |
532 | QTest::newRow(dataTag: "invalidImportID") << "invalidImportID.qml"<< "invalidImportID.errors.txt"<< false; |
533 | QTest::newRow(dataTag: "importFile") << "importFile.qml"<< "importFile.errors.txt"<< false; |
534 | |
535 | QTest::newRow(dataTag: "signal.1") << "signal.1.qml"<< "signal.1.errors.txt"<< false; |
536 | QTest::newRow(dataTag: "signal.2") << "signal.2.qml"<< "signal.2.errors.txt"<< false; |
537 | QTest::newRow(dataTag: "signal.3") << "signal.3.qml"<< "signal.3.errors.txt"<< false; |
538 | QTest::newRow(dataTag: "signal.4") << "signal.4.qml"<< "signal.4.errors.txt"<< false; |
539 | QTest::newRow(dataTag: "signal.5") << "signal.5.qml"<< "signal.5.errors.txt"<< false; |
540 | QTest::newRow(dataTag: "signal.6") << "signal.6.qml"<< "signal.6.errors.txt"<< false; |
541 | |
542 | QTest::newRow(dataTag: "method.1") << "method.1.qml"<< "method.1.errors.txt"<< false; |
543 | |
544 | QTest::newRow(dataTag: "property.1") << "property.1.qml"<< "property.1.errors.txt"<< false; |
545 | QTest::newRow(dataTag: "property.2") << "property.2.qml"<< "property.2.errors.txt"<< false; |
546 | QTest::newRow(dataTag: "property.3") << "property.3.qml"<< "property.3.errors.txt"<< false; |
547 | QTest::newRow(dataTag: "property.4") << "property.4.qml"<< "property.4.errors.txt"<< false; |
548 | QTest::newRow(dataTag: "property.6") << "property.6.qml"<< "property.6.errors.txt"<< false; |
549 | QTest::newRow(dataTag: "property.7") << "property.7.qml"<< "property.7.errors.txt"<< false; |
550 | |
551 | QTest::newRow(dataTag: "importScript.1") << "importscript.1.qml"<< "importscript.1.errors.txt"<< false; |
552 | |
553 | QTest::newRow(dataTag: "Component.1") << "component.1.qml"<< "component.1.errors.txt"<< false; |
554 | QTest::newRow(dataTag: "Component.2") << "component.2.qml"<< "component.2.errors.txt"<< false; |
555 | QTest::newRow(dataTag: "Component.3") << "component.3.qml"<< "component.3.errors.txt"<< false; |
556 | QTest::newRow(dataTag: "Component.4") << "component.4.qml"<< "component.4.errors.txt"<< false; |
557 | QTest::newRow(dataTag: "Component.5") << "component.5.qml"<< "component.5.errors.txt"<< false; |
558 | QTest::newRow(dataTag: "Component.6") << "component.6.qml"<< "component.6.errors.txt"<< false; |
559 | QTest::newRow(dataTag: "Component.7") << "component.7.qml"<< "component.7.errors.txt"<< false; |
560 | QTest::newRow(dataTag: "Component.8") << "component.8.qml"<< "component.8.errors.txt"<< false; |
561 | QTest::newRow(dataTag: "Component.9") << "component.9.qml"<< "component.9.errors.txt"<< false; |
562 | |
563 | QTest::newRow(dataTag: "MultiSet.1") << "multiSet.1.qml"<< "multiSet.1.errors.txt"<< false; |
564 | QTest::newRow(dataTag: "MultiSet.2") << "multiSet.2.qml"<< "multiSet.2.errors.txt"<< false; |
565 | QTest::newRow(dataTag: "MultiSet.3") << "multiSet.3.qml"<< "multiSet.3.errors.txt"<< false; |
566 | QTest::newRow(dataTag: "MultiSet.4") << "multiSet.4.qml"<< "multiSet.4.errors.txt"<< false; |
567 | QTest::newRow(dataTag: "MultiSet.5") << "multiSet.5.qml"<< "multiSet.5.errors.txt"<< false; |
568 | QTest::newRow(dataTag: "MultiSet.6") << "multiSet.6.qml"<< "multiSet.6.errors.txt"<< false; |
569 | QTest::newRow(dataTag: "MultiSet.7") << "multiSet.7.qml"<< "multiSet.7.errors.txt"<< false; |
570 | QTest::newRow(dataTag: "MultiSet.8") << "multiSet.8.qml"<< "multiSet.8.errors.txt"<< false; |
571 | QTest::newRow(dataTag: "MultiSet.9") << "multiSet.9.qml"<< "multiSet.9.errors.txt"<< false; |
572 | QTest::newRow(dataTag: "MultiSet.10") << "multiSet.10.qml"<< "multiSet.10.errors.txt"<< false; |
573 | QTest::newRow(dataTag: "MultiSet.11") << "multiSet.11.qml"<< "multiSet.11.errors.txt"<< false; |
574 | |
575 | QTest::newRow(dataTag: "dynamicMeta.1") << "dynamicMeta.1.qml"<< "dynamicMeta.1.errors.txt"<< false; |
576 | QTest::newRow(dataTag: "dynamicMeta.2") << "dynamicMeta.2.qml"<< "dynamicMeta.2.errors.txt"<< false; |
577 | QTest::newRow(dataTag: "dynamicMeta.3") << "dynamicMeta.3.qml"<< "dynamicMeta.3.errors.txt"<< false; |
578 | QTest::newRow(dataTag: "dynamicMeta.4") << "dynamicMeta.4.qml"<< "dynamicMeta.4.errors.txt"<< false; |
579 | QTest::newRow(dataTag: "dynamicMeta.5") << "dynamicMeta.5.qml"<< "dynamicMeta.5.errors.txt"<< false; |
580 | |
581 | QTest::newRow(dataTag: "invalidAlias.1") << "invalidAlias.1.qml"<< "invalidAlias.1.errors.txt"<< false; |
582 | QTest::newRow(dataTag: "invalidAlias.2") << "invalidAlias.2.qml"<< "invalidAlias.2.errors.txt"<< false; |
583 | QTest::newRow(dataTag: "invalidAlias.3") << "invalidAlias.3.qml"<< "invalidAlias.3.errors.txt"<< false; |
584 | QTest::newRow(dataTag: "invalidAlias.4") << "invalidAlias.4.qml"<< "invalidAlias.4.errors.txt"<< false; |
585 | QTest::newRow(dataTag: "invalidAlias.5") << "invalidAlias.5.qml"<< "invalidAlias.5.errors.txt"<< false; |
586 | QTest::newRow(dataTag: "invalidAlias.6") << "invalidAlias.6.qml"<< "invalidAlias.6.errors.txt"<< false; |
587 | QTest::newRow(dataTag: "invalidAlias.7") << "invalidAlias.7.qml"<< "invalidAlias.7.errors.txt"<< false; |
588 | QTest::newRow(dataTag: "invalidAlias.8") << "invalidAlias.8.qml"<< "invalidAlias.8.errors.txt"<< false; |
589 | QTest::newRow(dataTag: "invalidAlias.9") << "invalidAlias.9.qml"<< "invalidAlias.9.errors.txt"<< false; |
590 | QTest::newRow(dataTag: "invalidAlias.10") << "invalidAlias.10.qml"<< "invalidAlias.10.errors.txt"<< false; |
591 | QTest::newRow(dataTag: "invalidAlias.11") << "invalidAlias.11.qml"<< "invalidAlias.11.errors.txt"<< false; |
592 | QTest::newRow(dataTag: "invalidAlias.12") << "invalidAlias.12.qml"<< "invalidAlias.12.errors.txt"<< false; |
593 | QTest::newRow(dataTag: "invalidAlias.13") << "invalidAlias.13.qml"<< "invalidAlias.13.errors.txt"<< false; |
594 | |
595 | QTest::newRow(dataTag: "invalidAttachedProperty.1") << "invalidAttachedProperty.1.qml"<< "invalidAttachedProperty.1.errors.txt"<< false; |
596 | QTest::newRow(dataTag: "invalidAttachedProperty.2") << "invalidAttachedProperty.2.qml"<< "invalidAttachedProperty.2.errors.txt"<< false; |
597 | QTest::newRow(dataTag: "invalidAttachedProperty.3") << "invalidAttachedProperty.3.qml"<< "invalidAttachedProperty.3.errors.txt"<< false; |
598 | QTest::newRow(dataTag: "invalidAttachedProperty.4") << "invalidAttachedProperty.4.qml"<< "invalidAttachedProperty.4.errors.txt"<< false; |
599 | QTest::newRow(dataTag: "invalidAttachedProperty.5") << "invalidAttachedProperty.5.qml"<< "invalidAttachedProperty.5.errors.txt"<< false; |
600 | QTest::newRow(dataTag: "invalidAttachedProperty.6") << "invalidAttachedProperty.6.qml"<< "invalidAttachedProperty.6.errors.txt"<< false; |
601 | QTest::newRow(dataTag: "invalidAttachedProperty.7") << "invalidAttachedProperty.7.qml"<< "invalidAttachedProperty.7.errors.txt"<< false; |
602 | QTest::newRow(dataTag: "invalidAttachedProperty.8") << "invalidAttachedProperty.8.qml"<< "invalidAttachedProperty.8.errors.txt"<< false; |
603 | QTest::newRow(dataTag: "invalidAttachedProperty.9") << "invalidAttachedProperty.9.qml"<< "invalidAttachedProperty.9.errors.txt"<< false; |
604 | QTest::newRow(dataTag: "invalidAttachedProperty.10") << "invalidAttachedProperty.10.qml"<< "invalidAttachedProperty.10.errors.txt"<< false; |
605 | QTest::newRow(dataTag: "invalidAttachedProperty.11") << "invalidAttachedProperty.11.qml"<< "invalidAttachedProperty.11.errors.txt"<< false; |
606 | QTest::newRow(dataTag: "invalidAttachedProperty.12") << "invalidAttachedProperty.12.qml"<< "invalidAttachedProperty.12.errors.txt"<< false; |
607 | QTest::newRow(dataTag: "invalidAttachedProperty.13") << "invalidAttachedProperty.13.qml"<< "invalidAttachedProperty.13.errors.txt"<< false; |
608 | |
609 | QTest::newRow(dataTag: "assignValueToSignal") << "assignValueToSignal.qml"<< "assignValueToSignal.errors.txt"<< false; |
610 | QTest::newRow(dataTag: "emptySignal") << "emptySignal.qml"<< "emptySignal.errors.txt"<< false; |
611 | |
612 | QTest::newRow(dataTag: "nestedErrors") << "nestedErrors.qml"<< "nestedErrors.errors.txt"<< false; |
613 | QTest::newRow(dataTag: "defaultGrouped") << "defaultGrouped.qml"<< "defaultGrouped.errors.txt"<< false; |
614 | QTest::newRow(dataTag: "doubleSignal") << "doubleSignal.qml"<< "doubleSignal.errors.txt"<< false; |
615 | QTest::newRow(dataTag: "missingValueTypeProperty") << "missingValueTypeProperty.qml"<< "missingValueTypeProperty.errors.txt"<< false; |
616 | QTest::newRow(dataTag: "objectValueTypeProperty") << "objectValueTypeProperty.qml"<< "objectValueTypeProperty.errors.txt"<< false; |
617 | QTest::newRow(dataTag: "enumTypes") << "enumTypes.qml"<< "enumTypes.errors.txt"<< false; |
618 | QTest::newRow(dataTag: "noCreation") << "noCreation.qml"<< "noCreation.errors.txt"<< false; |
619 | QTest::newRow(dataTag: "destroyedSignal") << "destroyedSignal.qml"<< "destroyedSignal.errors.txt"<< false; |
620 | QTest::newRow(dataTag: "assignToNamespace") << "assignToNamespace.qml"<< "assignToNamespace.errors.txt"<< false; |
621 | QTest::newRow(dataTag: "invalidOn") << "invalidOn.qml"<< "invalidOn.errors.txt"<< false; |
622 | QTest::newRow(dataTag: "invalidProperty") << "invalidProperty.qml"<< "invalidProperty.errors.txt"<< false; |
623 | QTest::newRow(dataTag: "nonScriptableProperty") << "nonScriptableProperty.qml"<< "nonScriptableProperty.errors.txt"<< false; |
624 | QTest::newRow(dataTag: "notAvailable") << "notAvailable.qml"<< "notAvailable.errors.txt"<< false; |
625 | QTest::newRow(dataTag: "singularProperty") << "singularProperty.qml"<< "singularProperty.errors.txt"<< false; |
626 | QTest::newRow(dataTag: "singularProperty.2") << "singularProperty.2.qml"<< "singularProperty.2.errors.txt"<< false; |
627 | |
628 | QTest::newRow(dataTag: "scopedEnumList") << "scopedEnumList.qml"<< "scopedEnumList.errors.txt"<< false; |
629 | QTest::newRow(dataTag: "lowercase enum value") << "lowercaseQmlEnum.1.qml"<< "lowercaseQmlEnum.1.errors.txt"<< false; |
630 | QTest::newRow(dataTag: "lowercase enum type") << "lowercaseQmlEnum.2.qml"<< "lowercaseQmlEnum.2.errors.txt"<< false; |
631 | QTest::newRow(dataTag: "string enum value") << "invalidQmlEnumValue.1.qml"<< "invalidQmlEnumValue.1.errors.txt"<< false; |
632 | QTest::newRow(dataTag: "identifier enum type") << "invalidQmlEnumValue.2.qml"<< "invalidQmlEnumValue.2.errors.txt"<< false; |
633 | QTest::newRow(dataTag: "enum value too large") << "invalidQmlEnumValue.3.qml"<< "invalidQmlEnumValue.3.errors.txt"<< false; |
634 | QTest::newRow(dataTag: "non-integer enum value") << "invalidQmlEnumValue.4.qml"<< "invalidQmlEnumValue.4.errors.txt"<< false; |
635 | |
636 | const QString expectedError = isCaseSensitiveFileSystem(path: dataDirectory()) ? |
637 | QStringLiteral("incorrectCase.errors.sensitive.txt") : |
638 | QStringLiteral("incorrectCase.errors.insensitive.txt"); |
639 | QTest::newRow(dataTag: "incorrectCase") << "incorrectCase.qml"<< expectedError << false; |
640 | |
641 | QTest::newRow(dataTag: "metaobjectRevision.1") << "metaobjectRevision.1.qml"<< "metaobjectRevision.1.errors.txt"<< false; |
642 | QTest::newRow(dataTag: "metaobjectRevision.2") << "metaobjectRevision.2.qml"<< "metaobjectRevision.2.errors.txt"<< false; |
643 | QTest::newRow(dataTag: "metaobjectRevision.3") << "metaobjectRevision.3.qml"<< "metaobjectRevision.3.errors.txt"<< false; |
644 | |
645 | QTest::newRow(dataTag: "invalidRoot.1") << "invalidRoot.1.qml"<< "invalidRoot.1.errors.txt"<< false; |
646 | QTest::newRow(dataTag: "invalidRoot.2") << "invalidRoot.2.qml"<< "invalidRoot.2.errors.txt"<< false; |
647 | QTest::newRow(dataTag: "invalidRoot.3") << "invalidRoot.3.qml"<< "invalidRoot.3.errors.txt"<< false; |
648 | QTest::newRow(dataTag: "invalidRoot.4") << "invalidRoot.4.qml"<< "invalidRoot.4.errors.txt"<< false; |
649 | |
650 | QTest::newRow(dataTag: "invalidTypeName.1") << "invalidTypeName.1.qml"<< "invalidTypeName.1.errors.txt"<< false; |
651 | QTest::newRow(dataTag: "invalidTypeName.2") << "invalidTypeName.2.qml"<< "invalidTypeName.2.errors.txt"<< false; |
652 | QTest::newRow(dataTag: "invalidTypeName.3") << "invalidTypeName.3.qml"<< "invalidTypeName.3.errors.txt"<< false; |
653 | QTest::newRow(dataTag: "invalidTypeName.4") << "invalidTypeName.4.qml"<< "invalidTypeName.4.errors.txt"<< false; |
654 | |
655 | QTest::newRow(dataTag: "Major version isolation") << "majorVersionIsolation.qml"<< "majorVersionIsolation.errors.txt"<< false; |
656 | |
657 | QTest::newRow(dataTag: "badCompositeRegistration.1") << "badCompositeRegistration.1.qml"<< "badCompositeRegistration.1.errors.txt"<< false; |
658 | QTest::newRow(dataTag: "badCompositeRegistration.2") << "badCompositeRegistration.2.qml"<< "badCompositeRegistration.2.errors.txt"<< false; |
659 | |
660 | QTest::newRow(dataTag: "assignComponentToWrongType") << "assignComponentToWrongType.qml"<< "assignComponentToWrongType.errors.txt"<< false; |
661 | QTest::newRow(dataTag: "cyclicAlias") << "cyclicAlias.qml"<< "cyclicAlias.errors.txt"<< false; |
662 | |
663 | QTest::newRow(dataTag: "fuzzed.1") << "fuzzed.1.qml"<< "fuzzed.1.errors.txt"<< false; |
664 | QTest::newRow(dataTag: "fuzzed.2") << "fuzzed.2.qml"<< "fuzzed.2.errors.txt"<< false; |
665 | QTest::newRow(dataTag: "fuzzed.3") << "fuzzed.3.qml"<< "fuzzed.3.errors.txt"<< false; |
666 | |
667 | QTest::newRow(dataTag: "bareQmlImport") << "bareQmlImport.qml"<< "bareQmlImport.errors.txt"<< false; |
668 | |
669 | QTest::newRow(dataTag: "typeAnnotations.2") << "typeAnnotations.2.qml"<< "typeAnnotations.2.errors.txt"<< false; |
670 | |
671 | QTest::newRow(dataTag: "propertyUnknownType") << "propertyUnknownType.qml"<< "propertyUnknownType.errors.txt"<< false; |
672 | |
673 | QTest::newRow(dataTag: "selfInstantiation") << "SelfInstantiation.qml"<< "SelfInstantiation.errors.txt"<< false; |
674 | } |
675 | |
676 | void tst_qqmllanguage::errors() |
677 | { |
678 | #ifdef Q_OS_ANDROID |
679 | if (qstrcmp(QTest::currentDataTag(), "fuzzed.2") == 0) { |
680 | QSKIP("Gives different errors on Android"); |
681 | /* Only gives one error on Android: |
682 | |
683 | qrc:/data/fuzzed.2.qml:1:1: " |
684 | import" |
685 | ^ |
686 | So, it seems to complain about the first import (which is understandable) |
687 | */ |
688 | } |
689 | #endif |
690 | QFETCH(QString, file); |
691 | QFETCH(QString, errorFile); |
692 | QFETCH(bool, create); |
693 | |
694 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
695 | QTRY_VERIFY(!component.isLoading()); |
696 | |
697 | QScopedPointer<QObject> object; |
698 | |
699 | if (create) { |
700 | object.reset(other: component.create()); |
701 | QVERIFY(object.isNull()); |
702 | } |
703 | |
704 | VERIFY_ERRORS(errorFile.toLatin1().constData()); |
705 | } |
706 | |
707 | void tst_qqmllanguage::simpleObject() |
708 | { |
709 | QQmlComponent component(&engine, testFileUrl(fileName: "simpleObject.qml")); |
710 | VERIFY_ERRORS(0); |
711 | QScopedPointer<QObject> object(component.create()); |
712 | QVERIFY(object != nullptr); |
713 | } |
714 | |
715 | void tst_qqmllanguage::simpleContainer() |
716 | { |
717 | QQmlComponent component(&engine, testFileUrl(fileName: "simpleContainer.qml")); |
718 | VERIFY_ERRORS(0); |
719 | QScopedPointer<MyContainer> container(qobject_cast<MyContainer*>(object: component.create())); |
720 | QVERIFY(container != nullptr); |
721 | QCOMPARE(container->getChildren()->count(),2); |
722 | } |
723 | |
724 | void tst_qqmllanguage::interfaceProperty() |
725 | { |
726 | QQmlComponent component(&engine, testFileUrl(fileName: "interfaceProperty.qml")); |
727 | VERIFY_ERRORS(0); |
728 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create())); |
729 | QVERIFY(object != nullptr); |
730 | QVERIFY(object->interface()); |
731 | QCOMPARE(object->interface()->id, 913); |
732 | } |
733 | |
734 | void tst_qqmllanguage::interfaceQList() |
735 | { |
736 | QQmlComponent component(&engine, testFileUrl(fileName: "interfaceQList.qml")); |
737 | VERIFY_ERRORS(0); |
738 | QScopedPointer<MyContainer> container(qobject_cast<MyContainer*>(object: component.create())); |
739 | QVERIFY(container != nullptr); |
740 | QCOMPARE(container->getQListInterfaces()->count(), 2); |
741 | for(int ii = 0; ii < 2; ++ii) |
742 | QCOMPARE(container->getQListInterfaces()->at(ii)->id, 913); |
743 | } |
744 | |
745 | void tst_qqmllanguage::assignObjectToSignal() |
746 | { |
747 | QQmlComponent component(&engine, testFileUrl(fileName: "assignObjectToSignal.qml")); |
748 | VERIFY_ERRORS(0); |
749 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create())); |
750 | QVERIFY(object != nullptr); |
751 | QTest::ignoreMessage(type: QtWarningMsg, message: "MyQmlObject::basicSlot"); |
752 | emit object->basicSignal(); |
753 | } |
754 | |
755 | void tst_qqmllanguage::assignObjectToVariant() |
756 | { |
757 | QQmlComponent component(&engine, testFileUrl(fileName: "assignObjectToVariant.qml")); |
758 | VERIFY_ERRORS(0); |
759 | QScopedPointer<QObject> object(component.create()); |
760 | QVERIFY(object != nullptr); |
761 | QVariant v = object->property(name: "a"); |
762 | QVERIFY(v.userType() == qMetaTypeId<QObject *>()); |
763 | } |
764 | |
765 | void tst_qqmllanguage::assignLiteralSignalProperty() |
766 | { |
767 | QQmlComponent component(&engine, testFileUrl(fileName: "assignLiteralSignalProperty.qml")); |
768 | VERIFY_ERRORS(0); |
769 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create())); |
770 | QVERIFY(object != nullptr); |
771 | QCOMPARE(object->onLiteralSignal(), 10); |
772 | } |
773 | |
774 | // Test is an external component can be loaded and assigned (to a qlist) |
775 | void tst_qqmllanguage::assignQmlComponent() |
776 | { |
777 | QQmlComponent component(&engine, testFileUrl(fileName: "assignQmlComponent.qml")); |
778 | VERIFY_ERRORS(0); |
779 | QScopedPointer<MyContainer> object(qobject_cast<MyContainer *>(object: component.create())); |
780 | QVERIFY(object != nullptr); |
781 | QCOMPARE(object->getChildren()->count(), 1); |
782 | QObject *child = object->getChildren()->at(i: 0); |
783 | QCOMPARE(child->property("x"), QVariant(10)); |
784 | QCOMPARE(child->property("y"), QVariant(11)); |
785 | } |
786 | |
787 | // Test literal assignment to all the basic types |
788 | void tst_qqmllanguage::assignBasicTypes() |
789 | { |
790 | QQmlComponent component(&engine, testFileUrl(fileName: "assignBasicTypes.qml")); |
791 | VERIFY_ERRORS(0); |
792 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
793 | QVERIFY(object != nullptr); |
794 | QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); |
795 | QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); |
796 | QCOMPARE(object->qtEnumProperty(), Qt::RichText); |
797 | QCOMPARE(object->mirroredEnumProperty(), MyTypeObject::MirroredEnumVal3); |
798 | QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue); |
799 | QCOMPARE(object->stringProperty(), QString("Hello World!")); |
800 | QCOMPARE(object->uintProperty(), uint(10)); |
801 | QCOMPARE(object->intProperty(), -19); |
802 | QCOMPARE((float)object->realProperty(), float(23.2)); |
803 | QCOMPARE((float)object->doubleProperty(), float(-19.7)); |
804 | QCOMPARE((float)object->floatProperty(), float(8.5)); |
805 | QCOMPARE(object->colorProperty(), QColor("red")); |
806 | QCOMPARE(object->dateProperty(), QDate(1982, 11, 25)); |
807 | QCOMPARE(object->timeProperty(), QTime(11, 11, 32)); |
808 | QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1))); |
809 | QCOMPARE(object->pointProperty(), QPoint(99,13)); |
810 | QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3)); |
811 | QCOMPARE(object->sizeProperty(), QSize(99, 13)); |
812 | QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2)); |
813 | QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200)); |
814 | QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99)); |
815 | QCOMPARE(object->boolProperty(), true); |
816 | QCOMPARE(object->variantProperty(), QVariant("Hello World!")); |
817 | QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2f)); |
818 | QCOMPARE(object->vector2Property(), QVector2D(2, 3)); |
819 | QCOMPARE(object->vector4Property(), QVector4D(10, 1, 2.2f, 2.3f)); |
820 | const QUrl encoded = QUrl::fromEncoded(url: "main.qml?with%3cencoded%3edata", mode: QUrl::TolerantMode); |
821 | QCOMPARE(object->urlProperty(), component.url().resolved(encoded)); |
822 | QVERIFY(object->objectProperty() != nullptr); |
823 | MyTypeObject *child = qobject_cast<MyTypeObject *>(object: object->objectProperty()); |
824 | QVERIFY(child != nullptr); |
825 | QCOMPARE(child->intProperty(), 8); |
826 | |
827 | //these used to go via script. Ensure they no longer do |
828 | QCOMPARE(object->property("qtEnumTriggeredChange").toBool(), false); |
829 | QCOMPARE(object->property("mirroredEnumTriggeredChange").toBool(), false); |
830 | } |
831 | |
832 | // Test edge case type assignments |
833 | void tst_qqmllanguage::assignTypeExtremes() |
834 | { |
835 | QQmlComponent component(&engine, testFileUrl(fileName: "assignTypeExtremes.qml")); |
836 | VERIFY_ERRORS(0); |
837 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
838 | QVERIFY(object != nullptr); |
839 | QCOMPARE(object->uintProperty(), 0xEE6B2800); |
840 | QCOMPARE(object->intProperty(), -0x77359400); |
841 | } |
842 | |
843 | // Test that a composite type can assign to a property of its base type |
844 | void tst_qqmllanguage::assignCompositeToType() |
845 | { |
846 | QQmlComponent component(&engine, testFileUrl(fileName: "assignCompositeToType.qml")); |
847 | VERIFY_ERRORS(0); |
848 | QScopedPointer<QObject> object(component.create()); |
849 | QVERIFY(object != nullptr); |
850 | } |
851 | |
852 | // Test that literals are stored correctly in variant properties |
853 | void tst_qqmllanguage::assignLiteralToVariant() |
854 | { |
855 | QQmlComponent component(&engine, testFileUrl(fileName: "assignLiteralToVariant.qml")); |
856 | VERIFY_ERRORS(0); |
857 | QScopedPointer<QObject> object(component.create()); |
858 | QVERIFY(object != nullptr); |
859 | |
860 | QVERIFY(isJSNumberType(object->property("test1").userType())); |
861 | QVERIFY(isJSNumberType(object->property("test2").userType())); |
862 | QCOMPARE(object->property("test3").userType(), (int)QVariant::String); |
863 | QCOMPARE(object->property("test4").userType(), (int)QVariant::Color); |
864 | QCOMPARE(object->property("test5").userType(), (int)QVariant::RectF); |
865 | QCOMPARE(object->property("test6").userType(), (int)QVariant::PointF); |
866 | QCOMPARE(object->property("test7").userType(), (int)QVariant::SizeF); |
867 | QCOMPARE(object->property("test8").userType(), (int)QVariant::Vector3D); |
868 | QCOMPARE(object->property("test9").userType(), (int)QVariant::String); |
869 | QCOMPARE(object->property("test10").userType(), (int)QVariant::Bool); |
870 | QCOMPARE(object->property("test11").userType(), (int)QVariant::Bool); |
871 | QCOMPARE(object->property("test12").userType(), (int)QVariant::Vector4D); |
872 | |
873 | QCOMPARE(object->property("test1"), QVariant(1)); |
874 | QCOMPARE(object->property("test2"), QVariant((double)1.7)); |
875 | QVERIFY(object->property("test3") == QVariant(QString(QLatin1String( "Hello world!")))); |
876 | QCOMPARE(object->property("test4"), QVariant(QColor::fromRgb(0xFF008800))); |
877 | QVERIFY(object->property("test5") == QVariant(QRectF(10, 10, 10, 10))); |
878 | QVERIFY(object->property("test6") == QVariant(QPointF(10, 10))); |
879 | QVERIFY(object->property("test7") == QVariant(QSizeF(10, 10))); |
880 | QVERIFY(object->property("test8") == QVariant(QVector3D(100, 100, 100))); |
881 | QCOMPARE(object->property("test9"), QVariant(QString(QLatin1String( "#FF008800")))); |
882 | QCOMPARE(object->property("test10"), QVariant(bool(true))); |
883 | QCOMPARE(object->property("test11"), QVariant(bool(false))); |
884 | QVERIFY(object->property("test12") == QVariant(QVector4D(100, 100, 100, 100))); |
885 | } |
886 | |
887 | // Test that literals are stored correctly in "var" properties |
888 | // Note that behaviour differs from "variant" properties in that |
889 | // no conversion from "special strings" to QVariants is performed. |
890 | void tst_qqmllanguage::assignLiteralToVar() |
891 | { |
892 | QQmlComponent component(&engine, testFileUrl(fileName: "assignLiteralToVar.qml")); |
893 | VERIFY_ERRORS(0); |
894 | QScopedPointer<QObject> object(component.create()); |
895 | QVERIFY(object != nullptr); |
896 | |
897 | QVERIFY(isJSNumberType(object->property("test1").userType())); |
898 | QCOMPARE(object->property("test2").userType(), (int)QMetaType::Double); |
899 | QCOMPARE(object->property("test3").userType(), (int)QVariant::String); |
900 | QCOMPARE(object->property("test4").userType(), (int)QVariant::String); |
901 | QCOMPARE(object->property("test5").userType(), (int)QVariant::String); |
902 | QCOMPARE(object->property("test6").userType(), (int)QVariant::String); |
903 | QCOMPARE(object->property("test7").userType(), (int)QVariant::String); |
904 | QCOMPARE(object->property("test8").userType(), (int)QVariant::String); |
905 | QCOMPARE(object->property("test9").userType(), (int)QVariant::String); |
906 | QCOMPARE(object->property("test10").userType(), (int)QVariant::Bool); |
907 | QCOMPARE(object->property("test11").userType(), (int)QVariant::Bool); |
908 | QCOMPARE(object->property("test12").userType(), (int)QVariant::Color); |
909 | QCOMPARE(object->property("test13").userType(), (int)QVariant::RectF); |
910 | QCOMPARE(object->property("test14").userType(), (int)QVariant::PointF); |
911 | QCOMPARE(object->property("test15").userType(), (int)QVariant::SizeF); |
912 | QCOMPARE(object->property("test16").userType(), (int)QVariant::Vector3D); |
913 | QVERIFY(isJSNumberType(object->property("variantTest1Bound").userType())); |
914 | QVERIFY(isJSNumberType(object->property("test1Bound").userType())); |
915 | |
916 | QCOMPARE(object->property("test1"), QVariant(5)); |
917 | QCOMPARE(object->property("test2"), QVariant((double)1.7)); |
918 | QCOMPARE(object->property("test3"), QVariant(QString(QLatin1String( "Hello world!")))); |
919 | QCOMPARE(object->property("test4"), QVariant(QString(QLatin1String( "#FF008800")))); |
920 | QCOMPARE(object->property("test5"), QVariant(QString(QLatin1String( "10,10,10x10")))); |
921 | QCOMPARE(object->property("test6"), QVariant(QString(QLatin1String( "10,10")))); |
922 | QCOMPARE(object->property("test7"), QVariant(QString(QLatin1String( "10x10")))); |
923 | QCOMPARE(object->property("test8"), QVariant(QString(QLatin1String( "100,100,100")))); |
924 | QCOMPARE(object->property("test9"), QVariant(QString(QLatin1String( "#FF008800")))); |
925 | QCOMPARE(object->property("test10"), QVariant(bool(true))); |
926 | QCOMPARE(object->property("test11"), QVariant(bool(false))); |
927 | QCOMPARE(object->property("test12"), QVariant(QColor::fromRgbF(0.2, 0.3, 0.4, 0.5))); |
928 | QCOMPARE(object->property("test13"), QVariant(QRectF(10, 10, 10, 10))); |
929 | QCOMPARE(object->property("test14"), QVariant(QPointF(10, 10))); |
930 | QCOMPARE(object->property("test15"), QVariant(QSizeF(10, 10))); |
931 | QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100))); |
932 | QCOMPARE(object->property("variantTest1Bound"), QVariant(9)); |
933 | QCOMPARE(object->property("test1Bound"), QVariant(11)); |
934 | } |
935 | |
936 | void tst_qqmllanguage::assignLiteralToJSValue() |
937 | { |
938 | QQmlComponent component(&engine, testFileUrl(fileName: "assignLiteralToJSValue.qml")); |
939 | VERIFY_ERRORS(0); |
940 | QScopedPointer<QObject> root(component.create()); |
941 | QVERIFY(root != nullptr); |
942 | |
943 | { |
944 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test1"); |
945 | QJSValue value = object->qjsvalue(); |
946 | QVERIFY(value.isNumber()); |
947 | QCOMPARE(value.toNumber(), qreal(5)); |
948 | } { |
949 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test2"); |
950 | QJSValue value = object->qjsvalue(); |
951 | QVERIFY(value.isNumber()); |
952 | QCOMPARE(value.toNumber(), qreal(1.7)); |
953 | } { |
954 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test3"); |
955 | QJSValue value = object->qjsvalue(); |
956 | QVERIFY(value.isString()); |
957 | QCOMPARE(value.toString(), QString(QLatin1String("Hello world!"))); |
958 | }{ |
959 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test4"); |
960 | QJSValue value = object->qjsvalue(); |
961 | QVERIFY(value.isString()); |
962 | QCOMPARE(value.toString(), QString(QLatin1String("#FF008800"))); |
963 | } { |
964 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test5"); |
965 | QJSValue value = object->qjsvalue(); |
966 | QVERIFY(value.isString()); |
967 | QCOMPARE(value.toString(), QString(QLatin1String("10,10,10x10"))); |
968 | } { |
969 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test6"); |
970 | QJSValue value = object->qjsvalue(); |
971 | QVERIFY(value.isString()); |
972 | QCOMPARE(value.toString(), QString(QLatin1String("10,10"))); |
973 | } { |
974 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test7"); |
975 | QJSValue value = object->qjsvalue(); |
976 | QVERIFY(value.isString()); |
977 | QCOMPARE(value.toString(), QString(QLatin1String("10x10"))); |
978 | } { |
979 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test8"); |
980 | QJSValue value = object->qjsvalue(); |
981 | QVERIFY(value.isString()); |
982 | QCOMPARE(value.toString(), QString(QLatin1String("100,100,100"))); |
983 | } { |
984 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test9"); |
985 | QJSValue value = object->qjsvalue(); |
986 | QVERIFY(value.isString()); |
987 | QCOMPARE(value.toString(), QString(QLatin1String("#FF008800"))); |
988 | } { |
989 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test10"); |
990 | QJSValue value = object->qjsvalue(); |
991 | QVERIFY(value.isBool()); |
992 | QCOMPARE(value.toBool(), true); |
993 | } { |
994 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test11"); |
995 | QJSValue value = object->qjsvalue(); |
996 | QVERIFY(value.isBool()); |
997 | QCOMPARE(value.toBool(), false); |
998 | } { |
999 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test20"); |
1000 | QJSValue value = object->qjsvalue(); |
1001 | QVERIFY(value.isCallable()); |
1002 | QCOMPARE(value.call(QList<QJSValue> () << QJSValue(4)).toInt(), 12); |
1003 | } { |
1004 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test21"); |
1005 | QJSValue value = object->qjsvalue(); |
1006 | QVERIFY(value.isUndefined()); |
1007 | } { |
1008 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test22"); |
1009 | QJSValue value = object->qjsvalue(); |
1010 | QVERIFY(value.isNull()); |
1011 | } { |
1012 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test1Bound"); |
1013 | QJSValue value = object->qjsvalue(); |
1014 | QVERIFY(value.isNumber()); |
1015 | QCOMPARE(value.toNumber(), qreal(9)); |
1016 | } { |
1017 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test20Bound"); |
1018 | QJSValue value = object->qjsvalue(); |
1019 | QVERIFY(value.isNumber()); |
1020 | QCOMPARE(value.toNumber(), qreal(27)); |
1021 | } { |
1022 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "test23"); |
1023 | QJSValue value = object->qjsvalue(); |
1024 | QVERIFY(value.isQObject()); |
1025 | QCOMPARE(value.toQObject()->objectName(), "blah"); |
1026 | } |
1027 | } |
1028 | |
1029 | void tst_qqmllanguage::assignNullStrings() |
1030 | { |
1031 | QQmlComponent component(&engine, testFileUrl(fileName: "assignNullStrings.qml")); |
1032 | VERIFY_ERRORS(0); |
1033 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
1034 | QVERIFY(object != nullptr); |
1035 | QVERIFY(object->stringProperty().isNull()); |
1036 | QVERIFY(object->byteArrayProperty().isNull()); |
1037 | QMetaObject::invokeMethod(obj: object.data(), member: "assignNullStringsFromJs", type: Qt::DirectConnection); |
1038 | QVERIFY(object->stringProperty().isNull()); |
1039 | QVERIFY(object->byteArrayProperty().isNull()); |
1040 | } |
1041 | |
1042 | void tst_qqmllanguage::bindJSValueToVar() |
1043 | { |
1044 | QQmlComponent component(&engine, testFileUrl(fileName: "assignLiteralToJSValue.qml")); |
1045 | |
1046 | VERIFY_ERRORS(0); |
1047 | QScopedPointer<QObject> root(component.create()); |
1048 | QVERIFY(root != nullptr); |
1049 | |
1050 | QObject *object = root->findChild<QObject *>(aName: "varProperties"); |
1051 | |
1052 | QVERIFY(isJSNumberType(object->property("test1").userType())); |
1053 | QVERIFY(isJSNumberType(object->property("test2").userType())); |
1054 | QCOMPARE(object->property("test3").userType(), (int)QVariant::String); |
1055 | QCOMPARE(object->property("test4").userType(), (int)QVariant::String); |
1056 | QCOMPARE(object->property("test5").userType(), (int)QVariant::String); |
1057 | QCOMPARE(object->property("test6").userType(), (int)QVariant::String); |
1058 | QCOMPARE(object->property("test7").userType(), (int)QVariant::String); |
1059 | QCOMPARE(object->property("test8").userType(), (int)QVariant::String); |
1060 | QCOMPARE(object->property("test9").userType(), (int)QVariant::String); |
1061 | QCOMPARE(object->property("test10").userType(), (int)QVariant::Bool); |
1062 | QCOMPARE(object->property("test11").userType(), (int)QVariant::Bool); |
1063 | QCOMPARE(object->property("test12").userType(), (int)QVariant::Color); |
1064 | QCOMPARE(object->property("test13").userType(), (int)QVariant::RectF); |
1065 | QCOMPARE(object->property("test14").userType(), (int)QVariant::PointF); |
1066 | QCOMPARE(object->property("test15").userType(), (int)QVariant::SizeF); |
1067 | QCOMPARE(object->property("test16").userType(), (int)QVariant::Vector3D); |
1068 | QVERIFY(isJSNumberType(object->property("test1Bound").userType())); |
1069 | QVERIFY(isJSNumberType(object->property("test20Bound").userType())); |
1070 | |
1071 | QCOMPARE(object->property("test1"), QVariant(5)); |
1072 | QCOMPARE(object->property("test2"), QVariant((double)1.7)); |
1073 | QCOMPARE(object->property("test3"), QVariant(QString(QLatin1String( "Hello world!")))); |
1074 | QCOMPARE(object->property("test4"), QVariant(QString(QLatin1String( "#FF008800")))); |
1075 | QCOMPARE(object->property("test5"), QVariant(QString(QLatin1String( "10,10,10x10")))); |
1076 | QCOMPARE(object->property("test6"), QVariant(QString(QLatin1String( "10,10")))); |
1077 | QCOMPARE(object->property("test7"), QVariant(QString(QLatin1String( "10x10")))); |
1078 | QCOMPARE(object->property("test8"), QVariant(QString(QLatin1String( "100,100,100")))); |
1079 | QCOMPARE(object->property("test9"), QVariant(QString(QLatin1String( "#FF008800")))); |
1080 | QCOMPARE(object->property("test10"), QVariant(bool(true))); |
1081 | QCOMPARE(object->property("test11"), QVariant(bool(false))); |
1082 | QCOMPARE(object->property("test12"), QVariant(QColor::fromRgbF(0.2, 0.3, 0.4, 0.5))); |
1083 | QCOMPARE(object->property("test13"), QVariant(QRectF(10, 10, 10, 10))); |
1084 | QCOMPARE(object->property("test14"), QVariant(QPointF(10, 10))); |
1085 | QCOMPARE(object->property("test15"), QVariant(QSizeF(10, 10))); |
1086 | QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100))); |
1087 | QCOMPARE(object->property("test1Bound"), QVariant(9)); |
1088 | QCOMPARE(object->property("test20Bound"), QVariant(27)); |
1089 | } |
1090 | |
1091 | void tst_qqmllanguage::bindJSValueToVariant() |
1092 | { |
1093 | QQmlComponent component(&engine, testFileUrl(fileName: "assignLiteralToJSValue.qml")); |
1094 | |
1095 | VERIFY_ERRORS(0); |
1096 | QScopedPointer<QObject> root(component.create()); |
1097 | QVERIFY(root != nullptr); |
1098 | |
1099 | QObject *object = root->findChild<QObject *>(aName: "variantProperties"); |
1100 | |
1101 | QVERIFY(isJSNumberType(object->property("test1").userType())); |
1102 | QVERIFY(isJSNumberType(object->property("test2").userType())); |
1103 | QCOMPARE(object->property("test3").userType(), (int)QVariant::String); |
1104 | QCOMPARE(object->property("test4").userType(), (int)QVariant::String); |
1105 | QCOMPARE(object->property("test5").userType(), (int)QVariant::String); |
1106 | QCOMPARE(object->property("test6").userType(), (int)QVariant::String); |
1107 | QCOMPARE(object->property("test7").userType(), (int)QVariant::String); |
1108 | QCOMPARE(object->property("test8").userType(), (int)QVariant::String); |
1109 | QCOMPARE(object->property("test9").userType(), (int)QVariant::String); |
1110 | QCOMPARE(object->property("test10").userType(), (int)QVariant::Bool); |
1111 | QCOMPARE(object->property("test11").userType(), (int)QVariant::Bool); |
1112 | QCOMPARE(object->property("test12").userType(), (int)QVariant::Color); |
1113 | QCOMPARE(object->property("test13").userType(), (int)QVariant::RectF); |
1114 | QCOMPARE(object->property("test14").userType(), (int)QVariant::PointF); |
1115 | QCOMPARE(object->property("test15").userType(), (int)QVariant::SizeF); |
1116 | QCOMPARE(object->property("test16").userType(), (int)QVariant::Vector3D); |
1117 | QVERIFY(isJSNumberType(object->property("test1Bound").userType())); |
1118 | QVERIFY(isJSNumberType(object->property("test20Bound").userType())); |
1119 | |
1120 | QCOMPARE(object->property("test1"), QVariant(5)); |
1121 | QCOMPARE(object->property("test2"), QVariant((double)1.7)); |
1122 | QCOMPARE(object->property("test3"), QVariant(QString(QLatin1String( "Hello world!")))); |
1123 | QCOMPARE(object->property("test4"), QVariant(QString(QLatin1String( "#FF008800")))); |
1124 | QCOMPARE(object->property("test5"), QVariant(QString(QLatin1String( "10,10,10x10")))); |
1125 | QCOMPARE(object->property("test6"), QVariant(QString(QLatin1String( "10,10")))); |
1126 | QCOMPARE(object->property("test7"), QVariant(QString(QLatin1String( "10x10")))); |
1127 | QCOMPARE(object->property("test8"), QVariant(QString(QLatin1String( "100,100,100")))); |
1128 | QCOMPARE(object->property("test9"), QVariant(QString(QLatin1String( "#FF008800")))); |
1129 | QCOMPARE(object->property("test10"), QVariant(bool(true))); |
1130 | QCOMPARE(object->property("test11"), QVariant(bool(false))); |
1131 | QCOMPARE(object->property("test12"), QVariant(QColor::fromRgbF(0.2, 0.3, 0.4, 0.5))); |
1132 | QCOMPARE(object->property("test13"), QVariant(QRectF(10, 10, 10, 10))); |
1133 | QCOMPARE(object->property("test14"), QVariant(QPointF(10, 10))); |
1134 | QCOMPARE(object->property("test15"), QVariant(QSizeF(10, 10))); |
1135 | QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100))); |
1136 | QCOMPARE(object->property("test1Bound"), QVariant(9)); |
1137 | QCOMPARE(object->property("test20Bound"), QVariant(27)); |
1138 | } |
1139 | |
1140 | void tst_qqmllanguage::bindJSValueToType() |
1141 | { |
1142 | QQmlComponent component(&engine, testFileUrl(fileName: "assignLiteralToJSValue.qml")); |
1143 | |
1144 | VERIFY_ERRORS(0); |
1145 | QScopedPointer<QObject> root(component.create()); |
1146 | QVERIFY(root != nullptr); |
1147 | |
1148 | { |
1149 | MyTypeObject *object = root->findChild<MyTypeObject *>(aName: "typedProperties"); |
1150 | |
1151 | QCOMPARE(object->intProperty(), 5); |
1152 | QCOMPARE(object->doubleProperty(), double(1.7)); |
1153 | QCOMPARE(object->stringProperty(), QString(QLatin1String("Hello world!"))); |
1154 | QCOMPARE(object->boolProperty(), true); |
1155 | QCOMPARE(object->colorProperty(), QColor::fromRgbF(0.2, 0.3, 0.4, 0.5)); |
1156 | QCOMPARE(object->rectFProperty(), QRectF(10, 10, 10, 10)); |
1157 | QCOMPARE(object->pointFProperty(), QPointF(10, 10)); |
1158 | QCOMPARE(object->sizeFProperty(), QSizeF(10, 10)); |
1159 | QCOMPARE(object->vectorProperty(), QVector3D(100, 100, 100)); |
1160 | } { |
1161 | MyTypeObject *object = root->findChild<MyTypeObject *>(aName: "stringProperties"); |
1162 | |
1163 | QCOMPARE(object->intProperty(), 1); |
1164 | QCOMPARE(object->doubleProperty(), double(1.7)); |
1165 | QCOMPARE(object->stringProperty(), QString(QLatin1String("Hello world!"))); |
1166 | QCOMPARE(object->boolProperty(), true); |
1167 | QCOMPARE(object->colorProperty(), QColor::fromRgb(0x00, 0x88, 0x00, 0xFF)); |
1168 | QCOMPARE(object->rectFProperty(), QRectF(10, 10, 10, 10)); |
1169 | QCOMPARE(object->pointFProperty(), QPointF(10, 10)); |
1170 | QCOMPARE(object->sizeFProperty(), QSizeF(10, 10)); |
1171 | QCOMPARE(object->vectorProperty(), QVector3D(100, 100, 100)); |
1172 | } |
1173 | } |
1174 | |
1175 | void tst_qqmllanguage::bindTypeToJSValue() |
1176 | { |
1177 | QQmlComponent component(&engine, testFileUrl(fileName: "bindTypeToJSValue.qml")); |
1178 | |
1179 | VERIFY_ERRORS(0); |
1180 | QScopedPointer<QObject> root(component.create()); |
1181 | QVERIFY(root != nullptr); |
1182 | |
1183 | { |
1184 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "flagProperty"); |
1185 | QVERIFY(object); |
1186 | QJSValue value = object->qjsvalue(); |
1187 | QVERIFY(value.isNumber()); |
1188 | QCOMPARE(value.toNumber(), qreal(MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3)); |
1189 | } { |
1190 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "enumProperty"); |
1191 | QJSValue value = object->qjsvalue(); |
1192 | QVERIFY(value.isNumber()); |
1193 | QCOMPARE(value.toNumber(), qreal(MyTypeObject::EnumVal2)); |
1194 | } { |
1195 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "stringProperty"); |
1196 | QJSValue value = object->qjsvalue(); |
1197 | QVERIFY(value.isString()); |
1198 | QCOMPARE(value.toString(), QString(QLatin1String("Hello World!"))); |
1199 | } { |
1200 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "uintProperty"); |
1201 | QJSValue value = object->qjsvalue(); |
1202 | QVERIFY(value.isNumber()); |
1203 | QCOMPARE(value.toNumber(), qreal(10)); |
1204 | } { |
1205 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "intProperty"); |
1206 | QJSValue value = object->qjsvalue(); |
1207 | QVERIFY(value.isNumber()); |
1208 | QCOMPARE(value.toNumber(), qreal(-19)); |
1209 | } { |
1210 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "realProperty"); |
1211 | QJSValue value = object->qjsvalue(); |
1212 | QVERIFY(value.isNumber()); |
1213 | QCOMPARE(value.toNumber(), qreal(23.2)); |
1214 | } { |
1215 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "doubleProperty"); |
1216 | QJSValue value = object->qjsvalue(); |
1217 | QVERIFY(value.isNumber()); |
1218 | QCOMPARE(value.toNumber(), qreal(-19.7)); |
1219 | } { |
1220 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "floatProperty"); |
1221 | QJSValue value = object->qjsvalue(); |
1222 | QVERIFY(value.isNumber()); |
1223 | QCOMPARE(value.toNumber(), qreal(8.5)); |
1224 | } { |
1225 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "colorProperty"); |
1226 | QJSValue value = object->qjsvalue(); |
1227 | QVERIFY(value.isObject()); |
1228 | QCOMPARE(value.property(QLatin1String("r")).toNumber(), qreal(1.0)); |
1229 | QCOMPARE(value.property(QLatin1String("g")).toNumber(), qreal(0.0)); |
1230 | QCOMPARE(value.property(QLatin1String("b")).toNumber(), qreal(0.0)); |
1231 | } { |
1232 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "dateProperty"); |
1233 | QJSValue value = object->qjsvalue(); |
1234 | QCOMPARE(value.toDateTime().isValid(), true); |
1235 | } { |
1236 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "timeProperty"); |
1237 | QJSValue value = object->qjsvalue(); |
1238 | QCOMPARE(value.toDateTime().isValid(), true); |
1239 | } { |
1240 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "dateTimeProperty"); |
1241 | QJSValue value = object->qjsvalue(); |
1242 | QCOMPARE(value.toDateTime().isValid(), true); |
1243 | } { |
1244 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "pointProperty"); |
1245 | QJSValue value = object->qjsvalue(); |
1246 | QVERIFY(value.isObject()); |
1247 | QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(99)); |
1248 | QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(13)); |
1249 | } { |
1250 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "pointFProperty"); |
1251 | QJSValue value = object->qjsvalue(); |
1252 | QVERIFY(value.isObject()); |
1253 | QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(-10.1)); |
1254 | QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(12.3)); |
1255 | } { |
1256 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "rectProperty"); |
1257 | QJSValue value = object->qjsvalue(); |
1258 | QVERIFY(value.isObject()); |
1259 | QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(9)); |
1260 | QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(7)); |
1261 | QCOMPARE(value.property(QLatin1String("width")).toNumber(), qreal(100)); |
1262 | QCOMPARE(value.property(QLatin1String("height")).toNumber(), qreal(200)); |
1263 | } { |
1264 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "rectFProperty"); |
1265 | QJSValue value = object->qjsvalue(); |
1266 | QVERIFY(value.isObject()); |
1267 | QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(1000.1)); |
1268 | QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(-10.9)); |
1269 | QCOMPARE(value.property(QLatin1String("width")).toNumber(), qreal(400)); |
1270 | QCOMPARE(value.property(QLatin1String("height")).toNumber(), qreal(90.99)); |
1271 | } { |
1272 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "boolProperty"); |
1273 | QJSValue value = object->qjsvalue(); |
1274 | QVERIFY(value.isBool()); |
1275 | QCOMPARE(value.toBool(), true); |
1276 | } { |
1277 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "variantProperty"); |
1278 | QJSValue value = object->qjsvalue(); |
1279 | QVERIFY(value.isString()); |
1280 | QCOMPARE(value.toString(), QString(QLatin1String("Hello World!"))); |
1281 | } { |
1282 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "vectorProperty"); |
1283 | QJSValue value = object->qjsvalue(); |
1284 | QVERIFY(value.isObject()); |
1285 | QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(10.0f)); |
1286 | QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(1.0f)); |
1287 | QCOMPARE(value.property(QLatin1String("z")).toNumber(), qreal(2.2f)); |
1288 | } { |
1289 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "vector4Property"); |
1290 | QJSValue value = object->qjsvalue(); |
1291 | QVERIFY(value.isObject()); |
1292 | QCOMPARE(value.property(QLatin1String("x")).toNumber(), qreal(10.0f)); |
1293 | QCOMPARE(value.property(QLatin1String("y")).toNumber(), qreal(1.0f)); |
1294 | QCOMPARE(value.property(QLatin1String("z")).toNumber(), qreal(2.2f)); |
1295 | QCOMPARE(value.property(QLatin1String("w")).toNumber(), qreal(2.3f)); |
1296 | } { |
1297 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "urlProperty"); |
1298 | QJSValue value = object->qjsvalue(); |
1299 | const QUrl encoded = QUrl::fromEncoded(url: "main.qml?with%3cencoded%3edata", mode: QUrl::TolerantMode); |
1300 | QCOMPARE(value.toString(), component.url().resolved(encoded).toString()); |
1301 | } { |
1302 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "objectProperty"); |
1303 | QJSValue value = object->qjsvalue(); |
1304 | QVERIFY(value.isQObject()); |
1305 | QVERIFY(qobject_cast<MyTypeObject *>(value.toQObject())); |
1306 | } { |
1307 | MyQmlObject *object = root->findChild<MyQmlObject *>(aName: "varProperty"); |
1308 | QJSValue value = object->qjsvalue(); |
1309 | QVERIFY(value.isString()); |
1310 | QCOMPARE(value.toString(), QString(QLatin1String("Hello World!"))); |
1311 | } |
1312 | } |
1313 | |
1314 | // Tests that custom parser types can be instantiated |
1315 | void tst_qqmllanguage::customParserTypes() |
1316 | { |
1317 | QQmlComponent component(&engine, testFileUrl(fileName: "customParserTypes.qml")); |
1318 | VERIFY_ERRORS(0); |
1319 | QScopedPointer<QObject> object(component.create()); |
1320 | QVERIFY(object != nullptr); |
1321 | QCOMPARE(object->property("count"), QVariant(2)); |
1322 | } |
1323 | |
1324 | // Tests that custom pursor types can be instantiated in ICs |
1325 | void tst_qqmllanguage::customParserTypeInInlineComponent() |
1326 | { |
1327 | QQmlComponent component(&engine, testFileUrl(fileName: "customParserTypeInIC.qml")); |
1328 | VERIFY_ERRORS(0); |
1329 | QScopedPointer<QObject> object(component.create()); |
1330 | QVERIFY(object != nullptr); |
1331 | QCOMPARE(object->property("count"), 2); |
1332 | } |
1333 | |
1334 | // Tests that the root item can be a custom component |
1335 | void tst_qqmllanguage::rootAsQmlComponent() |
1336 | { |
1337 | QQmlComponent component(&engine, testFileUrl(fileName: "rootAsQmlComponent.qml")); |
1338 | VERIFY_ERRORS(0); |
1339 | QScopedPointer<MyContainer> object(qobject_cast<MyContainer *>(object: component.create())); |
1340 | QVERIFY(object != nullptr); |
1341 | QCOMPARE(object->property("x"), QVariant(11)); |
1342 | QCOMPARE(object->getChildren()->count(), 2); |
1343 | } |
1344 | |
1345 | void tst_qqmllanguage::rootItemIsComponent() |
1346 | { |
1347 | QQmlComponent component(&engine, testFileUrl(fileName: "rootItemIsComponent.qml")); |
1348 | VERIFY_ERRORS(0); |
1349 | QScopedPointer<QObject> root(component.create()); |
1350 | QVERIFY(qobject_cast<QQmlComponent*>(root.data())); |
1351 | QScopedPointer<QObject> other(qobject_cast<QQmlComponent*>(object: root.data())->create()); |
1352 | QVERIFY(!other.isNull()); |
1353 | QQmlContext *context = qmlContext(other.data()); |
1354 | QVERIFY(context); |
1355 | QCOMPARE(context->nameForObject(other.data()), QStringLiteral("blah")); |
1356 | } |
1357 | |
1358 | // Tests that components can be specified inline |
1359 | void tst_qqmllanguage::inlineQmlComponents() |
1360 | { |
1361 | QQmlComponent component(&engine, testFileUrl(fileName: "inlineQmlComponents.qml")); |
1362 | VERIFY_ERRORS(0); |
1363 | QScopedPointer<MyContainer> object(qobject_cast<MyContainer *>(object: component.create())); |
1364 | QVERIFY(object != nullptr); |
1365 | QCOMPARE(object->getChildren()->count(), 1); |
1366 | QQmlComponent *comp = qobject_cast<QQmlComponent *>(object: object->getChildren()->at(i: 0)); |
1367 | QVERIFY(comp != nullptr); |
1368 | QScopedPointer<MyQmlObject> compObject(qobject_cast<MyQmlObject *>(object: comp->create())); |
1369 | QVERIFY(compObject != nullptr); |
1370 | QCOMPARE(compObject->value(), 11); |
1371 | } |
1372 | |
1373 | // Tests that types that have an id property have it set |
1374 | void tst_qqmllanguage::idProperty() |
1375 | { |
1376 | { |
1377 | QQmlComponent component(&engine, testFileUrl(fileName: "idProperty.qml")); |
1378 | VERIFY_ERRORS(0); |
1379 | QScopedPointer<MyContainer> object(qobject_cast<MyContainer *>(object: component.create())); |
1380 | QVERIFY(object != nullptr); |
1381 | QCOMPARE(object->getChildren()->count(), 2); |
1382 | MyTypeObject *child = |
1383 | qobject_cast<MyTypeObject *>(object: object->getChildren()->at(i: 0)); |
1384 | QVERIFY(child != nullptr); |
1385 | QCOMPARE(child->id(), QString("myObjectId")); |
1386 | QCOMPARE(object->property("object"), QVariant::fromValue((QObject *)child)); |
1387 | |
1388 | child = |
1389 | qobject_cast<MyTypeObject *>(object: object->getChildren()->at(i: 1)); |
1390 | QVERIFY(child != nullptr); |
1391 | QCOMPARE(child->id(), QString("name.with.dots")); |
1392 | } |
1393 | { |
1394 | QQmlComponent component(&engine, testFileUrl(fileName: "idPropertyMismatch.qml")); |
1395 | VERIFY_ERRORS(0); |
1396 | QScopedPointer<QObject> root(component.create()); |
1397 | QVERIFY(!root.isNull()); |
1398 | QQmlContext *ctx = qmlContext(root.data()); |
1399 | QVERIFY(ctx); |
1400 | QCOMPARE(ctx->nameForObject(root.data()), QString("root")); |
1401 | } |
1402 | } |
1403 | |
1404 | // Tests automatic connection to notify signals if "onBlahChanged" syntax is used |
1405 | // even if the notify signal for "blah" is not called "blahChanged" |
1406 | void tst_qqmllanguage::autoNotifyConnection() |
1407 | { |
1408 | QQmlComponent component(&engine, testFileUrl(fileName: "autoNotifyConnection.qml")); |
1409 | VERIFY_ERRORS(0); |
1410 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create())); |
1411 | QVERIFY(object != nullptr); |
1412 | QMetaProperty prop = object->metaObject()->property(index: object->metaObject()->indexOfProperty(name: "receivedNotify")); |
1413 | QVERIFY(prop.isValid()); |
1414 | |
1415 | QCOMPARE(prop.read(object.data()), QVariant::fromValue(false)); |
1416 | object->setPropertyWithNotify(1); |
1417 | QCOMPARE(prop.read(object.data()), QVariant::fromValue(true)); |
1418 | } |
1419 | |
1420 | // Tests that signals can be assigned to |
1421 | void tst_qqmllanguage::assignSignal() |
1422 | { |
1423 | QQmlComponent component(&engine, testFileUrl(fileName: "assignSignal.qml")); |
1424 | VERIFY_ERRORS(0); |
1425 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create())); |
1426 | QVERIFY(object != nullptr); |
1427 | QTest::ignoreMessage(type: QtWarningMsg, message: "MyQmlObject::basicSlot"); |
1428 | emit object->basicSignal(); |
1429 | QTest::ignoreMessage(type: QtWarningMsg, message: "MyQmlObject::basicSlotWithArgs(9)"); |
1430 | emit object->basicParameterizedSignal(parameter: 9); |
1431 | } |
1432 | |
1433 | void tst_qqmllanguage::assignSignalFunctionExpression() |
1434 | { |
1435 | QQmlComponent component(&engine, testFileUrl(fileName: "assignSignalFunctionExpression.qml")); |
1436 | VERIFY_ERRORS(0); |
1437 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create())); |
1438 | QVERIFY(object != nullptr); |
1439 | QTest::ignoreMessage(type: QtWarningMsg, message: "MyQmlObject::basicSlot"); |
1440 | emit object->basicSignal(); |
1441 | QTest::ignoreMessage(type: QtWarningMsg, message: "MyQmlObject::basicSlotWithArgs(9)"); |
1442 | emit object->basicParameterizedSignal(parameter: 9); |
1443 | } |
1444 | |
1445 | void tst_qqmllanguage::overrideSignal_data() |
1446 | { |
1447 | QTest::addColumn<QString>(name: "file"); |
1448 | QTest::addColumn<QString>(name: "errorFile"); |
1449 | |
1450 | QTest::newRow(dataTag: "override signal with signal") << "overrideSignal.1.qml"<< "overrideSignal.1.errors.txt"; |
1451 | QTest::newRow(dataTag: "override signal with method") << "overrideSignal.2.qml"<< "overrideSignal.2.errors.txt"; |
1452 | QTest::newRow(dataTag: "override signal with property") << "overrideSignal.3.qml"<< ""; |
1453 | QTest::newRow(dataTag: "override signal of alias property with signal") << "overrideSignal.4.qml"<< "overrideSignal.4.errors.txt"; |
1454 | QTest::newRow(dataTag: "override signal of superclass with signal") << "overrideSignal.5.qml"<< "overrideSignal.5.errors.txt"; |
1455 | QTest::newRow(dataTag: "override builtin signal with signal") << "overrideSignal.6.qml"<< "overrideSignal.6.errors.txt"; |
1456 | } |
1457 | |
1458 | void tst_qqmllanguage::overrideSignal() |
1459 | { |
1460 | QFETCH(QString, file); |
1461 | QFETCH(QString, errorFile); |
1462 | |
1463 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
1464 | if (errorFile.isEmpty()) { |
1465 | VERIFY_ERRORS(0); |
1466 | QScopedPointer<QObject> object(component.create()); |
1467 | QVERIFY(object != nullptr); |
1468 | QVERIFY(object->property("success").toBool()); |
1469 | } else { |
1470 | VERIFY_ERRORS(errorFile.toLatin1().constData()); |
1471 | } |
1472 | } |
1473 | |
1474 | // Tests the creation and assignment of dynamic properties |
1475 | void tst_qqmllanguage::dynamicProperties() |
1476 | { |
1477 | QQmlComponent component(&engine, testFileUrl(fileName: "dynamicProperties.qml")); |
1478 | VERIFY_ERRORS(0); |
1479 | QScopedPointer<QObject> object(component.create()); |
1480 | QCOMPARE(object->property("intProperty"), QVariant(10)); |
1481 | QCOMPARE(object->property("boolProperty"), QVariant(false)); |
1482 | QCOMPARE(object->property("doubleProperty"), QVariant(-10.1)); |
1483 | QCOMPARE(object->property("realProperty"), QVariant((qreal)-19.9)); |
1484 | QCOMPARE(object->property("stringProperty"), QVariant( "Hello World!")); |
1485 | QCOMPARE(object->property("urlProperty"), QVariant(testFileUrl( "main.qml"))); |
1486 | QCOMPARE(object->property("colorProperty"), QVariant(QColor( "red"))); |
1487 | QCOMPARE(object->property("dateProperty"), QVariant(QDate(1945, 9, 2))); |
1488 | QCOMPARE(object->property("varProperty"), QVariant( "Hello World!")); |
1489 | } |
1490 | |
1491 | // Test that nested types can use dynamic properties |
1492 | void tst_qqmllanguage::dynamicPropertiesNested() |
1493 | { |
1494 | QQmlComponent component(&engine, testFileUrl(fileName: "dynamicPropertiesNested.qml")); |
1495 | VERIFY_ERRORS(0); |
1496 | QScopedPointer<QObject> object(component.create()); |
1497 | QVERIFY(object != nullptr); |
1498 | |
1499 | QCOMPARE(object->property("super_a").toInt(), 11); // Overridden |
1500 | QCOMPARE(object->property("super_c").toInt(), 14); // Inherited |
1501 | QCOMPARE(object->property("a").toInt(), 13); // New |
1502 | QCOMPARE(object->property("b").toInt(), 12); // New |
1503 | } |
1504 | |
1505 | // Tests the creation and assignment to dynamic list properties |
1506 | void tst_qqmllanguage::listProperties() |
1507 | { |
1508 | QQmlComponent component(&engine, testFileUrl(fileName: "listProperties.qml")); |
1509 | VERIFY_ERRORS(0); |
1510 | QScopedPointer<QObject> object(component.create()); |
1511 | QVERIFY(object != nullptr); |
1512 | |
1513 | QCOMPARE(object->property("test").toInt(), 2); |
1514 | } |
1515 | |
1516 | // Tests that initializing list properties of a base class does not crash |
1517 | // (QTBUG-82171) |
1518 | void tst_qqmllanguage::listPropertiesInheritanceNoCrash() |
1519 | { |
1520 | QQmlEngine engine; |
1521 | QQmlComponent component(&engine, testFileUrl(fileName: "listPropertiesChild.qml")); |
1522 | QScopedPointer<QObject> object(component.create()); // should not crash |
1523 | QVERIFY(object != nullptr); |
1524 | } |
1525 | |
1526 | void tst_qqmllanguage::badListItemType() |
1527 | { |
1528 | QQmlComponent component(&engine, testFileUrl(fileName: "badListItemType.qml")); |
1529 | QVERIFY(component.isError()); |
1530 | VERIFY_ERRORS("badListItemType.errors.txt"); |
1531 | } |
1532 | |
1533 | // Tests the creation and assignment of dynamic object properties |
1534 | // ### Not complete |
1535 | void tst_qqmllanguage::dynamicObjectProperties() |
1536 | { |
1537 | { |
1538 | QQmlComponent component(&engine, testFileUrl(fileName: "dynamicObjectProperties.qml")); |
1539 | VERIFY_ERRORS(0); |
1540 | QScopedPointer<QObject> object(component.create()); |
1541 | QVERIFY(object != nullptr); |
1542 | |
1543 | QCOMPARE(object->property("objectProperty"), QVariant::fromValue((QObject*)nullptr)); |
1544 | QVERIFY(object->property("objectProperty2") != QVariant::fromValue((QObject*)nullptr)); |
1545 | } |
1546 | { |
1547 | QQmlComponent component(&engine, testFileUrl(fileName: "dynamicObjectProperties.2.qml")); |
1548 | VERIFY_ERRORS(0); |
1549 | QScopedPointer<QObject> object(component.create()); |
1550 | QVERIFY(object != nullptr); |
1551 | |
1552 | QVERIFY(object->property("objectProperty") != QVariant::fromValue((QObject*)nullptr)); |
1553 | } |
1554 | } |
1555 | |
1556 | // Tests the declaration of dynamic signals and slots |
1557 | void tst_qqmllanguage::dynamicSignalsAndSlots() |
1558 | { |
1559 | QTest::ignoreMessage(type: QtDebugMsg, message: "1921"); |
1560 | |
1561 | QQmlComponent component(&engine, testFileUrl(fileName: "dynamicSignalsAndSlots.qml")); |
1562 | VERIFY_ERRORS(0); |
1563 | QScopedPointer<QObject> object(component.create()); |
1564 | QVERIFY(object != nullptr); |
1565 | QVERIFY(object->metaObject()->indexOfMethod("signal1()") != -1); |
1566 | QVERIFY(object->metaObject()->indexOfMethod("signal2()") != -1); |
1567 | QVERIFY(object->metaObject()->indexOfMethod("slot1()") != -1); |
1568 | QVERIFY(object->metaObject()->indexOfMethod("slot2()") != -1); |
1569 | |
1570 | QCOMPARE(object->property("test").toInt(), 0); |
1571 | QMetaObject::invokeMethod(obj: object.data(), member: "slot3", type: Qt::DirectConnection, Q_ARG(QVariant, QVariant(10))); |
1572 | QCOMPARE(object->property("test").toInt(), 10); |
1573 | } |
1574 | |
1575 | void tst_qqmllanguage::simpleBindings() |
1576 | { |
1577 | QQmlComponent component(&engine, testFileUrl(fileName: "simpleBindings.qml")); |
1578 | VERIFY_ERRORS(0); |
1579 | QScopedPointer<QObject> object(component.create()); |
1580 | QVERIFY(object != nullptr); |
1581 | QCOMPARE(object->property("value1"), QVariant(10)); |
1582 | QCOMPARE(object->property("value2"), QVariant(10)); |
1583 | QCOMPARE(object->property("value3"), QVariant(21)); |
1584 | QCOMPARE(object->property("value4"), QVariant(10)); |
1585 | QCOMPARE(object->property("objectProperty"), QVariant::fromValue(object.data())); |
1586 | } |
1587 | |
1588 | class EvaluationCounter : public QObject |
1589 | { |
1590 | Q_OBJECT |
1591 | public: |
1592 | int counter = 0; |
1593 | Q_INVOKABLE void increaseEvaluationCounter() { ++counter; } |
1594 | }; |
1595 | |
1596 | void tst_qqmllanguage::noDoubleEvaluationForFlushedBindings_data() |
1597 | { |
1598 | QTest::addColumn<QString>(name: "fileName"); |
1599 | QTest::newRow(dataTag: "order1") << QString( "noDoubleEvaluationForFlushedBindings.qml"); |
1600 | QTest::newRow(dataTag: "order2") << QString( "noDoubleEvaluationForFlushedBindings.2.qml"); |
1601 | } |
1602 | |
1603 | void tst_qqmllanguage::noDoubleEvaluationForFlushedBindings() |
1604 | { |
1605 | QFETCH(QString, fileName); |
1606 | QQmlEngine engine; |
1607 | |
1608 | EvaluationCounter stats; |
1609 | engine.rootContext()->setContextProperty("stats", &stats); |
1610 | |
1611 | QQmlComponent component(&engine, testFileUrl(fileName)); |
1612 | VERIFY_ERRORS(0); |
1613 | QScopedPointer<QObject> obj(component.create()); |
1614 | QVERIFY(!obj.isNull()); |
1615 | |
1616 | QCOMPARE(stats.counter, 1); |
1617 | } |
1618 | |
1619 | void tst_qqmllanguage::autoComponentCreation() |
1620 | { |
1621 | { |
1622 | QQmlComponent component(&engine, testFileUrl(fileName: "autoComponentCreation.qml")); |
1623 | VERIFY_ERRORS(0); |
1624 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
1625 | QVERIFY(object != nullptr); |
1626 | QVERIFY(object->componentProperty() != nullptr); |
1627 | QScopedPointer<MyTypeObject> child(qobject_cast<MyTypeObject *>(object: object->componentProperty()->create())); |
1628 | QVERIFY(child != nullptr); |
1629 | QCOMPARE(child->realProperty(), qreal(9)); |
1630 | } |
1631 | { |
1632 | QQmlComponent component(&engine, testFileUrl(fileName: "autoComponentCreation.2.qml")); |
1633 | VERIFY_ERRORS(0); |
1634 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
1635 | QVERIFY(object != nullptr); |
1636 | QVERIFY(object->componentProperty() != nullptr); |
1637 | QScopedPointer<MyTypeObject> child(qobject_cast<MyTypeObject *>(object: object->componentProperty()->create())); |
1638 | QVERIFY(child != nullptr); |
1639 | QCOMPARE(child->realProperty(), qreal(9)); |
1640 | } |
1641 | } |
1642 | |
1643 | void tst_qqmllanguage::autoComponentCreationInGroupProperty() |
1644 | { |
1645 | QQmlComponent component(&engine, testFileUrl(fileName: "autoComponentCreationInGroupProperties.qml")); |
1646 | VERIFY_ERRORS(0); |
1647 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
1648 | QVERIFY(object != nullptr); |
1649 | QVERIFY(object->componentProperty() != nullptr); |
1650 | QScopedPointer<MyTypeObject> child(qobject_cast<MyTypeObject *>(object: object->componentProperty()->create())); |
1651 | QVERIFY(child != nullptr); |
1652 | QCOMPARE(child->realProperty(), qreal(9)); |
1653 | } |
1654 | |
1655 | void tst_qqmllanguage::propertyValueSource() |
1656 | { |
1657 | { |
1658 | QQmlComponent component(&engine, testFileUrl(fileName: "propertyValueSource.qml")); |
1659 | VERIFY_ERRORS(0); |
1660 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
1661 | QVERIFY(object != nullptr); |
1662 | |
1663 | QList<QObject *> valueSources; |
1664 | QObjectList allChildren = object->findChildren<QObject*>(); |
1665 | foreach (QObject *child, allChildren) { |
1666 | if (qobject_cast<QQmlPropertyValueSource *>(object: child)) |
1667 | valueSources.append(t: child); |
1668 | } |
1669 | |
1670 | QCOMPARE(valueSources.count(), 1); |
1671 | MyPropertyValueSource *valueSource = |
1672 | qobject_cast<MyPropertyValueSource *>(object: valueSources.at(i: 0)); |
1673 | QVERIFY(valueSource != nullptr); |
1674 | QCOMPARE(valueSource->prop.object(), qobject_cast<QObject*>(object.data())); |
1675 | QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); |
1676 | } |
1677 | |
1678 | { |
1679 | QQmlComponent component(&engine, testFileUrl(fileName: "propertyValueSource.2.qml")); |
1680 | VERIFY_ERRORS(0); |
1681 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
1682 | QVERIFY(object != nullptr); |
1683 | |
1684 | QList<QObject *> valueSources; |
1685 | QObjectList allChildren = object->findChildren<QObject*>(); |
1686 | foreach (QObject *child, allChildren) { |
1687 | if (qobject_cast<QQmlPropertyValueSource *>(object: child)) |
1688 | valueSources.append(t: child); |
1689 | } |
1690 | |
1691 | QCOMPARE(valueSources.count(), 1); |
1692 | MyPropertyValueSource *valueSource = |
1693 | qobject_cast<MyPropertyValueSource *>(object: valueSources.at(i: 0)); |
1694 | QVERIFY(valueSource != nullptr); |
1695 | QCOMPARE(valueSource->prop.object(), qobject_cast<QObject*>(object.data())); |
1696 | QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); |
1697 | } |
1698 | } |
1699 | |
1700 | void tst_qqmllanguage::requiredProperty() |
1701 | { |
1702 | QQmlEngine engine; |
1703 | { |
1704 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperties.1.qml")); |
1705 | VERIFY_ERRORS(0); |
1706 | QScopedPointer<QObject> object(component.create()); |
1707 | QVERIFY(object); |
1708 | } |
1709 | { |
1710 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperties.2.qml")); |
1711 | QVERIFY(!component.errors().empty()); |
1712 | } |
1713 | { |
1714 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperties.4.qml")); |
1715 | QScopedPointer<QObject> object(component.create()); |
1716 | QVERIFY(!component.errors().empty()); |
1717 | QVERIFY(component.errorString().contains("Required property objectName was not initialized")); |
1718 | } |
1719 | { |
1720 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperties.3.qml")); |
1721 | QScopedPointer<QObject> object(component.create()); |
1722 | QVERIFY(!component.errors().empty()); |
1723 | QVERIFY(component.errorString().contains("Required property i was not initialized")); |
1724 | } |
1725 | { |
1726 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperties.5.qml")); |
1727 | QScopedPointer<QObject> object(component.create()); |
1728 | QVERIFY(!component.errors().empty()); |
1729 | QVERIFY(component.errorString().contains("Required property i was not initialized")); |
1730 | } |
1731 | { |
1732 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperties.6.qml")); |
1733 | VERIFY_ERRORS(0); |
1734 | QScopedPointer<QObject> object(component.create()); |
1735 | QVERIFY(object); |
1736 | } |
1737 | { |
1738 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperties.7.qml")); |
1739 | QScopedPointer<QObject> object(component.create()); |
1740 | QVERIFY(!component.errors().empty()); |
1741 | QVERIFY(component.errorString().contains("Property blub was marked as required but does not exist")); |
1742 | } |
1743 | } |
1744 | |
1745 | class MyClassWithRequiredProperty : public QObject |
1746 | { |
1747 | public: |
1748 | Q_OBJECT |
1749 | Q_PROPERTY(int test MEMBER m_test REQUIRED NOTIFY testChanged) |
1750 | Q_SIGNAL void testChanged(); |
1751 | private: |
1752 | int m_test; |
1753 | }; |
1754 | |
1755 | class ChildClassWithoutOwnRequired : public MyClassWithRequiredProperty |
1756 | { |
1757 | public: |
1758 | Q_OBJECT |
1759 | Q_PROPERTY(int test2 MEMBER m_test2 NOTIFY test2Changed) |
1760 | Q_SIGNAL void test2Changed(); |
1761 | private: |
1762 | int m_test2; |
1763 | }; |
1764 | |
1765 | class ChildClassWithOwnRequired : public MyClassWithRequiredProperty |
1766 | { |
1767 | public: |
1768 | Q_OBJECT |
1769 | Q_PROPERTY(int test2 MEMBER m_test2 REQUIRED NOTIFY test2Changed) |
1770 | Q_SIGNAL void test2Changed(); |
1771 | private: |
1772 | int m_test2; |
1773 | }; |
1774 | |
1775 | void tst_qqmllanguage::requiredPropertyFromCpp_data() |
1776 | { |
1777 | qmlRegisterType<MyClassWithRequiredProperty>(uri: "example.org", versionMajor: 1, versionMinor: 0, qmlName: "MyClass"); |
1778 | qmlRegisterType<ChildClassWithoutOwnRequired>(uri: "example.org", versionMajor: 1, versionMinor: 0, qmlName: "Child"); |
1779 | qmlRegisterType<ChildClassWithOwnRequired>(uri: "example.org", versionMajor: 1, versionMinor: 0, qmlName: "Child2"); |
1780 | |
1781 | |
1782 | QTest::addColumn<QUrl>(name: "setFile"); |
1783 | QTest::addColumn<QUrl>(name: "notSetFile"); |
1784 | QTest::addColumn<QString>(name: "errorMessage"); |
1785 | QTest::addColumn<int>(name: "expectedValue"); |
1786 | |
1787 | QTest::addRow(format: "direct") << testFileUrl(fileName: "cppRequiredProperty.qml") << testFileUrl(fileName: "cppRequiredPropertyNotSet.qml") << QString( ":4 Required property test was not initialized\n") << 42; |
1788 | QTest::addRow(format: "in parent") << testFileUrl(fileName: "cppRequiredPropertyInParent.qml") << testFileUrl(fileName: "cppRequiredPropertyInParentNotSet.qml") << QString( ":4 Required property test was not initialized\n") << 42; |
1789 | QTest::addRow(format: "in child and parent") << testFileUrl(fileName: "cppRequiredPropertyInChildAndParent.qml") << testFileUrl(fileName: "cppRequiredPropertyInChildAndParentNotSet.qml") << QString( ":4 Required property test2 was not initialized\n") << 18; |
1790 | } |
1791 | |
1792 | void tst_qqmllanguage::requiredPropertyFromCpp() |
1793 | { |
1794 | QQmlEngine engine; |
1795 | QFETCH(QUrl, setFile); |
1796 | QFETCH(QUrl, notSetFile); |
1797 | QFETCH(QString, errorMessage); |
1798 | QFETCH(int, expectedValue); |
1799 | { |
1800 | QQmlComponent comp(&engine, notSetFile); |
1801 | QScopedPointer<QObject> o { comp.create() }; |
1802 | QVERIFY(o.isNull()); |
1803 | QVERIFY(comp.isError()); |
1804 | QCOMPARE(comp.errorString(), notSetFile.toString() + errorMessage); |
1805 | } |
1806 | { |
1807 | QQmlComponent comp(&engine, setFile); |
1808 | QScopedPointer<QObject> o { comp.create() }; |
1809 | QVERIFY(!o.isNull()); |
1810 | QCOMPARE(o->property("test").toInt(), expectedValue); |
1811 | } |
1812 | } |
1813 | |
1814 | void tst_qqmllanguage::attachedProperties() |
1815 | { |
1816 | QQmlComponent component(&engine, testFileUrl(fileName: "attachedProperties.qml")); |
1817 | VERIFY_ERRORS(0); |
1818 | QScopedPointer<QObject> object(component.create()); |
1819 | QVERIFY(object != nullptr); |
1820 | QObject *attached = qmlAttachedPropertiesObject<MyQmlObject>(obj: object.data()); |
1821 | QVERIFY(attached != nullptr); |
1822 | QCOMPARE(attached->property("value"), QVariant(10)); |
1823 | QCOMPARE(attached->property("value2"), QVariant(13)); |
1824 | } |
1825 | |
1826 | // Tests non-static object properties |
1827 | void tst_qqmllanguage::dynamicObjects() |
1828 | { |
1829 | QQmlComponent component(&engine, testFileUrl(fileName: "dynamicObject.1.qml")); |
1830 | VERIFY_ERRORS(0); |
1831 | QScopedPointer<QObject> object(component.create()); |
1832 | QVERIFY(object != nullptr); |
1833 | } |
1834 | |
1835 | // Tests the registration of custom variant string converters |
1836 | void tst_qqmllanguage::customVariantTypes() |
1837 | { |
1838 | QQmlComponent component(&engine, testFileUrl(fileName: "customVariantTypes.qml")); |
1839 | VERIFY_ERRORS(0); |
1840 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create())); |
1841 | QVERIFY(object != nullptr); |
1842 | QCOMPARE(object->customType().a, 10); |
1843 | } |
1844 | |
1845 | void tst_qqmllanguage::valueTypes() |
1846 | { |
1847 | QQmlComponent component(&engine, testFileUrl(fileName: "valueTypes.qml")); |
1848 | VERIFY_ERRORS(0); |
1849 | |
1850 | QString message = component.url().toString() + ":2:1: QML MyTypeObject: Binding loop detected for property \"rectProperty.width\""; |
1851 | QTest::ignoreMessage(type: QtWarningMsg, qPrintable(message)); |
1852 | QTest::ignoreMessage(type: QtWarningMsg, qPrintable(message)); |
1853 | |
1854 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
1855 | QVERIFY(object != nullptr); |
1856 | |
1857 | |
1858 | QCOMPARE(object->rectProperty(), QRect(10, 11, 12, 13)); |
1859 | QCOMPARE(object->rectProperty2(), QRect(10, 11, 12, 13)); |
1860 | QCOMPARE(object->intProperty(), 10); |
1861 | object->doAction(); |
1862 | QCOMPARE(object->rectProperty(), QRect(12, 11, 14, 13)); |
1863 | QCOMPARE(object->rectProperty2(), QRect(12, 11, 14, 13)); |
1864 | QCOMPARE(object->intProperty(), 12); |
1865 | |
1866 | // ### |
1867 | #if 0 |
1868 | QQmlProperty p(object, "rectProperty.x"); |
1869 | QCOMPARE(p.read(), QVariant(12)); |
1870 | p.write(13); |
1871 | QCOMPARE(p.read(), QVariant(13)); |
1872 | |
1873 | quint32 r = QQmlPropertyPrivate::saveValueType(p.coreIndex(), p.valueTypeCoreIndex()); |
1874 | QQmlProperty p2; |
1875 | QQmlPropertyPrivate::restore(p2, r, object); |
1876 | QCOMPARE(p2.read(), QVariant(13)); |
1877 | #endif |
1878 | } |
1879 | |
1880 | void tst_qqmllanguage::cppnamespace() |
1881 | { |
1882 | QScopedPointer<QObject> object; |
1883 | |
1884 | auto create = [&](const char *file) { |
1885 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
1886 | VERIFY_ERRORS(0); |
1887 | object.reset(other: component.create()); |
1888 | QVERIFY(object != nullptr); |
1889 | }; |
1890 | |
1891 | auto createAndCheck = [&](const char *file) { |
1892 | create(file); |
1893 | return !QTest::currentTestFailed(); |
1894 | }; |
1895 | |
1896 | QVERIFY(createAndCheck("cppnamespace.qml")); |
1897 | QCOMPARE(object->property("intProperty").toInt(), |
1898 | (int)MyNamespace::MyOtherNSEnum::OtherKey2); |
1899 | |
1900 | QVERIFY(createAndCheck("cppstaticnamespace.qml")); |
1901 | QCOMPARE(object->property("intProperty").toInt(), |
1902 | (int)MyStaticNamespace::MyOtherNSEnum::OtherKey2); |
1903 | |
1904 | QVERIFY(createAndCheck("cppnamespace.2.qml")); |
1905 | QVERIFY(createAndCheck("cppstaticnamespace.2.qml")); |
1906 | } |
1907 | |
1908 | void tst_qqmllanguage::aliasProperties() |
1909 | { |
1910 | // Simple "int" alias |
1911 | { |
1912 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.1.qml")); |
1913 | VERIFY_ERRORS(0); |
1914 | QScopedPointer<QObject> object(component.create()); |
1915 | QVERIFY(object != nullptr); |
1916 | |
1917 | // Read through alias |
1918 | QCOMPARE(object->property("valueAlias").toInt(), 10); |
1919 | object->setProperty(name: "value", value: QVariant(13)); |
1920 | QCOMPARE(object->property("valueAlias").toInt(), 13); |
1921 | |
1922 | // Write through alias |
1923 | object->setProperty(name: "valueAlias", value: QVariant(19)); |
1924 | QCOMPARE(object->property("valueAlias").toInt(), 19); |
1925 | QCOMPARE(object->property("value").toInt(), 19); |
1926 | } |
1927 | |
1928 | // Complex object alias |
1929 | { |
1930 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.2.qml")); |
1931 | VERIFY_ERRORS(0); |
1932 | QScopedPointer<QObject> object(component.create()); |
1933 | QVERIFY(object != nullptr); |
1934 | |
1935 | // Read through alias |
1936 | MyQmlObject *v = |
1937 | qvariant_cast<MyQmlObject *>(v: object->property(name: "aliasObject")); |
1938 | QVERIFY(v != nullptr); |
1939 | QCOMPARE(v->value(), 10); |
1940 | |
1941 | // Write through alias |
1942 | MyQmlObject *v2 = new MyQmlObject(); |
1943 | v2->setParent(object.data()); |
1944 | object->setProperty(name: "aliasObject", value: QVariant::fromValue(value: v2)); |
1945 | MyQmlObject *v3 = |
1946 | qvariant_cast<MyQmlObject *>(v: object->property(name: "aliasObject")); |
1947 | QVERIFY(v3 != nullptr); |
1948 | QCOMPARE(v3, v2); |
1949 | } |
1950 | |
1951 | // Nested aliases |
1952 | { |
1953 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.3.qml")); |
1954 | VERIFY_ERRORS(0); |
1955 | QScopedPointer<QObject> object(component.create()); |
1956 | QVERIFY(object != nullptr); |
1957 | |
1958 | QCOMPARE(object->property("value").toInt(), 1892); |
1959 | QCOMPARE(object->property("value2").toInt(), 1892); |
1960 | |
1961 | object->setProperty(name: "value", value: QVariant(1313)); |
1962 | QCOMPARE(object->property("value").toInt(), 1313); |
1963 | QCOMPARE(object->property("value2").toInt(), 1313); |
1964 | |
1965 | object->setProperty(name: "value2", value: QVariant(8080)); |
1966 | QCOMPARE(object->property("value").toInt(), 8080); |
1967 | QCOMPARE(object->property("value2").toInt(), 8080); |
1968 | } |
1969 | |
1970 | // Enum aliases |
1971 | { |
1972 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.4.qml")); |
1973 | VERIFY_ERRORS(0); |
1974 | QScopedPointer<QObject> object(component.create()); |
1975 | QVERIFY(object != nullptr); |
1976 | |
1977 | QCOMPARE(object->property("enumAlias").toInt(), 1); |
1978 | } |
1979 | |
1980 | // Id aliases |
1981 | { |
1982 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.5.qml")); |
1983 | VERIFY_ERRORS(0); |
1984 | QScopedPointer<QObject> object(component.create()); |
1985 | QVERIFY(object != nullptr); |
1986 | |
1987 | QVariant v = object->property(name: "otherAlias"); |
1988 | QCOMPARE(v.userType(), qMetaTypeId<MyQmlObject*>()); |
1989 | MyQmlObject *o = qvariant_cast<MyQmlObject*>(v); |
1990 | QCOMPARE(o->value(), 10); |
1991 | |
1992 | delete o; |
1993 | |
1994 | v = object->property(name: "otherAlias"); |
1995 | QCOMPARE(v.userType(), qMetaTypeId<MyQmlObject*>()); |
1996 | o = qvariant_cast<MyQmlObject*>(v); |
1997 | QVERIFY(!o); |
1998 | } |
1999 | |
2000 | // Nested aliases - this used to cause a crash |
2001 | { |
2002 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.6.qml")); |
2003 | VERIFY_ERRORS(0); |
2004 | QScopedPointer<QObject> object(component.create()); |
2005 | QVERIFY(object != nullptr); |
2006 | |
2007 | QCOMPARE(object->property("a").toInt(), 1923); |
2008 | } |
2009 | |
2010 | // Ptr Alias Cleanup - check that aliases to ptr types return 0 |
2011 | // if the object aliased to is removed |
2012 | { |
2013 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.7.qml")); |
2014 | VERIFY_ERRORS(0); |
2015 | |
2016 | QScopedPointer<QObject> object(component.create()); |
2017 | QVERIFY(object != nullptr); |
2018 | |
2019 | QObject *object1 = qvariant_cast<QObject *>(v: object->property(name: "object")); |
2020 | QVERIFY(object1 != nullptr); |
2021 | QObject *object2 = qvariant_cast<QObject *>(v: object1->property(name: "object")); |
2022 | QVERIFY(object2 != nullptr); |
2023 | |
2024 | QObject *alias = qvariant_cast<QObject *>(v: object->property(name: "aliasedObject")); |
2025 | QCOMPARE(alias, object2); |
2026 | |
2027 | delete object1; |
2028 | |
2029 | QObject *alias2 = object.data(); // "Random" start value |
2030 | int status = -1; |
2031 | void *a[] = { &alias2, nullptr, &status }; |
2032 | QMetaObject::metacall(object.data(), QMetaObject::ReadProperty, |
2033 | object->metaObject()->indexOfProperty(name: "aliasedObject"), a); |
2034 | QVERIFY(!alias2); |
2035 | } |
2036 | |
2037 | // Simple composite type |
2038 | { |
2039 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.8.qml")); |
2040 | VERIFY_ERRORS(0); |
2041 | QScopedPointer<QObject> object(component.create()); |
2042 | QVERIFY(object != nullptr); |
2043 | |
2044 | QCOMPARE(object->property("value").toInt(), 10); |
2045 | } |
2046 | |
2047 | // Complex composite type |
2048 | { |
2049 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.9.qml")); |
2050 | VERIFY_ERRORS(0); |
2051 | QScopedPointer<QObject> object(component.create()); |
2052 | QVERIFY(object != nullptr); |
2053 | |
2054 | QCOMPARE(object->property("value").toInt(), 10); |
2055 | } |
2056 | |
2057 | // Valuetype alias |
2058 | // Simple "int" alias |
2059 | { |
2060 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.10.qml")); |
2061 | VERIFY_ERRORS(0); |
2062 | QScopedPointer<QObject> object(component.create()); |
2063 | QVERIFY(object != nullptr); |
2064 | |
2065 | // Read through alias |
2066 | QCOMPARE(object->property("valueAlias").toRect(), QRect(10, 11, 9, 8)); |
2067 | object->setProperty(name: "rectProperty", value: QVariant(QRect(33, 12, 99, 100))); |
2068 | QCOMPARE(object->property("valueAlias").toRect(), QRect(33, 12, 99, 100)); |
2069 | |
2070 | // Write through alias |
2071 | object->setProperty(name: "valueAlias", value: QVariant(QRect(3, 3, 4, 9))); |
2072 | QCOMPARE(object->property("valueAlias").toRect(), QRect(3, 3, 4, 9)); |
2073 | QCOMPARE(object->property("rectProperty").toRect(), QRect(3, 3, 4, 9)); |
2074 | } |
2075 | |
2076 | // Valuetype sub-alias |
2077 | { |
2078 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.11.qml")); |
2079 | VERIFY_ERRORS(0); |
2080 | QScopedPointer<QObject> object(component.create()); |
2081 | QVERIFY(object != nullptr); |
2082 | |
2083 | // Read through alias |
2084 | QCOMPARE(object->property("aliasProperty").toInt(), 19); |
2085 | object->setProperty(name: "rectProperty", value: QVariant(QRect(33, 8, 102, 111))); |
2086 | QCOMPARE(object->property("aliasProperty").toInt(), 33); |
2087 | |
2088 | // Write through alias |
2089 | object->setProperty(name: "aliasProperty", value: QVariant(4)); |
2090 | QCOMPARE(object->property("aliasProperty").toInt(), 4); |
2091 | QCOMPARE(object->property("rectProperty").toRect(), QRect(4, 8, 102, 111)); |
2092 | } |
2093 | |
2094 | // Nested aliases with a qml file |
2095 | { |
2096 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.12.qml")); |
2097 | VERIFY_ERRORS(0); |
2098 | QScopedPointer<QObject> object(component.create()); |
2099 | QVERIFY(!object.isNull()); |
2100 | |
2101 | QPointer<QObject> subObject = qvariant_cast<QObject*>(v: object->property(name: "referencingSubObject")); |
2102 | QVERIFY(!subObject.isNull()); |
2103 | |
2104 | QVERIFY(subObject->property("success").toBool()); |
2105 | } |
2106 | |
2107 | // Nested aliases with a qml file with reverse ordering |
2108 | { |
2109 | // This is known to fail at the moment. |
2110 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.13.qml")); |
2111 | VERIFY_ERRORS(0); |
2112 | QScopedPointer<QObject> object(component.create()); |
2113 | QVERIFY(!object.isNull()); |
2114 | |
2115 | QPointer<QObject> subObject = qvariant_cast<QObject*>(v: object->property(name: "referencingSubObject")); |
2116 | QVERIFY(!subObject.isNull()); |
2117 | |
2118 | QVERIFY(subObject->property("success").toBool()); |
2119 | } |
2120 | |
2121 | // "Nested" aliases within an object that require iterative resolution |
2122 | { |
2123 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.14.qml")); |
2124 | VERIFY_ERRORS(0); |
2125 | |
2126 | QScopedPointer<QObject> object(component.create()); |
2127 | QVERIFY(!object.isNull()); |
2128 | |
2129 | QPointer<QObject> subObject = qvariant_cast<QObject*>(v: object->property(name: "referencingSubObject")); |
2130 | QVERIFY(!subObject.isNull()); |
2131 | |
2132 | QVERIFY(subObject->property("success").toBool()); |
2133 | } |
2134 | |
2135 | // Property bindings on group properties that are actually aliases (QTBUG-51043) |
2136 | { |
2137 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.15.qml")); |
2138 | VERIFY_ERRORS(0); |
2139 | |
2140 | QScopedPointer<QObject> object(component.create()); |
2141 | QVERIFY(!object.isNull()); |
2142 | |
2143 | QPointer<QObject> subItem = qvariant_cast<QObject*>(v: object->property(name: "symbol")); |
2144 | QVERIFY(!subItem.isNull()); |
2145 | |
2146 | QCOMPARE(subItem->property("y").toInt(), 1); |
2147 | } |
2148 | |
2149 | // Alias to sub-object with binding (QTBUG-57041) |
2150 | { |
2151 | // This is shold *not* crash. |
2152 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.16.qml")); |
2153 | VERIFY_ERRORS(0); |
2154 | |
2155 | QScopedPointer<QObject> object(component.create()); |
2156 | QVERIFY(!object.isNull()); |
2157 | } |
2158 | |
2159 | // Alias to grouped property |
2160 | { |
2161 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.17.qml")); |
2162 | VERIFY_ERRORS(0); |
2163 | |
2164 | QScopedPointer<QObject> object(component.create()); |
2165 | QVERIFY(!object.isNull()); |
2166 | QVERIFY(object->property("success").toBool()); |
2167 | } |
2168 | |
2169 | // Alias to grouped property updates |
2170 | { |
2171 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.17.qml")); |
2172 | VERIFY_ERRORS(0); |
2173 | |
2174 | QScopedPointer<QObject> object(component.create()); |
2175 | QVERIFY(!object.isNull()); |
2176 | QObject *aliasUser = object->findChild<QObject*>(aName: QLatin1String("aliasUser")); |
2177 | QVERIFY(aliasUser); |
2178 | QQmlProperty checkValueProp(object.get(), "checkValue"); |
2179 | QVERIFY(checkValueProp.isValid()); |
2180 | checkValueProp.write(777); |
2181 | QCOMPARE(object->property("checkValue").toInt(), 777); |
2182 | QCOMPARE(aliasUser->property("topMargin").toInt(), 777); |
2183 | } |
2184 | |
2185 | // Write to alias to grouped property |
2186 | { |
2187 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.17.qml")); |
2188 | VERIFY_ERRORS(0); |
2189 | |
2190 | QScopedPointer<QObject> object(component.create()); |
2191 | QVERIFY(!object.isNull()); |
2192 | QObject *aliasUser = object->findChild<QObject*>(aName: QLatin1String("aliasUser")); |
2193 | QVERIFY(aliasUser); |
2194 | QQmlProperty topMarginProp {aliasUser, "topMargin"}; |
2195 | QVERIFY(topMarginProp.isValid()); |
2196 | topMarginProp.write(777); |
2197 | QObject *myItem = object->findChild<QObject*>(aName: QLatin1String("myItem")); |
2198 | QVERIFY(myItem); |
2199 | auto anchors = myItem->property(name: "anchors").value<QObject*>(); |
2200 | QVERIFY(anchors); |
2201 | QCOMPARE(anchors->property("topMargin").toInt(), 777); |
2202 | } |
2203 | |
2204 | // Binding to alias to grouped property gets updated |
2205 | { |
2206 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.17.qml")); |
2207 | VERIFY_ERRORS(0); |
2208 | |
2209 | QScopedPointer<QObject> object(component.create()); |
2210 | QVERIFY(!object.isNull()); |
2211 | QObject *aliasUser = object->findChild<QObject*>(aName: QLatin1String("aliasUser")); |
2212 | QVERIFY(aliasUser); |
2213 | QQmlProperty topMarginProp {aliasUser, "topMargin"}; |
2214 | QVERIFY(topMarginProp.isValid()); |
2215 | topMarginProp.write(20); |
2216 | QObject *myText = object->findChild<QObject*>(aName: QLatin1String("myText")); |
2217 | QVERIFY(myText); |
2218 | auto text = myText->property(name: "text").toString(); |
2219 | QCOMPARE(text, "alias:\n20"); |
2220 | } |
2221 | |
2222 | { |
2223 | QQmlComponent component(&engine, testFileUrl(fileName: "alias.18.qml")); |
2224 | VERIFY_ERRORS("alias.18.errors.txt"); |
2225 | } |
2226 | } |
2227 | |
2228 | // QTBUG-13374 Test that alias properties and signals can coexist |
2229 | void tst_qqmllanguage::aliasPropertiesAndSignals() |
2230 | { |
2231 | QQmlComponent component(&engine, testFileUrl(fileName: "aliasPropertiesAndSignals.qml")); |
2232 | VERIFY_ERRORS(0); |
2233 | QScopedPointer<QObject> o(component.create()); |
2234 | QVERIFY(o); |
2235 | QCOMPARE(o->property("test").toBool(), true); |
2236 | } |
2237 | |
2238 | void tst_qqmllanguage::qtbug_89822() |
2239 | { |
2240 | QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_89822.qml")); |
2241 | VERIFY_ERRORS("qtbug_89822.errors.txt"); |
2242 | } |
2243 | |
2244 | // Test that the root element in a composite type can be a Component |
2245 | void tst_qqmllanguage::componentCompositeType() |
2246 | { |
2247 | QQmlComponent component(&engine, testFileUrl(fileName: "componentCompositeType.qml")); |
2248 | VERIFY_ERRORS(0); |
2249 | QScopedPointer<QObject> object(component.create()); |
2250 | QVERIFY(object != nullptr); |
2251 | } |
2252 | |
2253 | class TestType : public QObject { |
2254 | Q_OBJECT |
2255 | public: |
2256 | TestType(QObject *p=nullptr) : QObject(p) {} |
2257 | }; |
2258 | |
2259 | class TestType2 : public QObject { |
2260 | Q_OBJECT |
2261 | public: |
2262 | TestType2(QObject *p=nullptr) : QObject(p) {} |
2263 | }; |
2264 | |
2265 | void tst_qqmllanguage::i18n_data() |
2266 | { |
2267 | QTest::addColumn<QString>(name: "file"); |
2268 | QTest::addColumn<QString>(name: "stringProperty"); |
2269 | QTest::newRow(dataTag: "i18nStrings") << "i18nStrings.qml"<< QString::fromUtf8(str: "Test \303\241\303\242\303\243\303\244\303\245 (5 accented 'a' letters)"); |
2270 | QTest::newRow(dataTag: "i18nDeclaredPropertyNames") << "i18nDeclaredPropertyNames.qml"<< QString::fromUtf8(str: "Test \303\241\303\242\303\243\303\244\303\245: 10"); |
2271 | QTest::newRow(dataTag: "i18nDeclaredPropertyUse") << "i18nDeclaredPropertyUse.qml"<< QString::fromUtf8(str: "Test \303\241\303\242\303\243\303\244\303\245: 15"); |
2272 | QTest::newRow(dataTag: "i18nScript") << "i18nScript.qml"<< QString::fromUtf8(str: "Test \303\241\303\242\303\243\303\244\303\245: 20"); |
2273 | QTest::newRow(dataTag: "i18nType") << "i18nType.qml"<< QString::fromUtf8(str: "Test \303\241\303\242\303\243\303\244\303\245: 30"); |
2274 | QTest::newRow(dataTag: "i18nNameSpace") << "i18nNameSpace.qml"<< QString::fromUtf8(str: "Test \303\241\303\242\303\243\303\244\303\245: 40"); |
2275 | } |
2276 | |
2277 | void tst_qqmllanguage::i18n() |
2278 | { |
2279 | QFETCH(QString, file); |
2280 | QFETCH(QString, stringProperty); |
2281 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
2282 | VERIFY_ERRORS(0); |
2283 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(object: component.create())); |
2284 | QVERIFY(object != nullptr); |
2285 | QCOMPARE(object->stringProperty(), stringProperty); |
2286 | } |
2287 | |
2288 | // Check that the Component::onCompleted attached property works |
2289 | void tst_qqmllanguage::onCompleted() |
2290 | { |
2291 | QQmlComponent component(&engine, testFileUrl(fileName: "onCompleted.qml")); |
2292 | VERIFY_ERRORS(0); |
2293 | QTest::ignoreMessage(type: QtDebugMsg, message: "Completed 6 10"); |
2294 | QTest::ignoreMessage(type: QtDebugMsg, message: "Completed 6 10"); |
2295 | QTest::ignoreMessage(type: QtDebugMsg, message: "Completed 10 11"); |
2296 | QScopedPointer<QObject> object(component.create()); |
2297 | QVERIFY(object != nullptr); |
2298 | } |
2299 | |
2300 | // Check that the Component::onDestruction attached property works |
2301 | void tst_qqmllanguage::onDestruction() |
2302 | { |
2303 | QQmlComponent component(&engine, testFileUrl(fileName: "onDestruction.qml")); |
2304 | VERIFY_ERRORS(0); |
2305 | QScopedPointer<QObject> object(component.create()); |
2306 | QVERIFY(object != nullptr); |
2307 | QTest::ignoreMessage(type: QtDebugMsg, message: "Destruction 6 10"); |
2308 | QTest::ignoreMessage(type: QtDebugMsg, message: "Destruction 6 10"); |
2309 | QTest::ignoreMessage(type: QtDebugMsg, message: "Destruction 10 11"); |
2310 | } |
2311 | |
2312 | // Check that assignments to QQmlScriptString properties work |
2313 | void tst_qqmllanguage::scriptString() |
2314 | { |
2315 | { |
2316 | QQmlComponent component(&engine, testFileUrl(fileName: "scriptString.qml")); |
2317 | VERIFY_ERRORS(0); |
2318 | |
2319 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2320 | QVERIFY(object != nullptr); |
2321 | QVERIFY(!object->scriptProperty().isEmpty()); |
2322 | QCOMPARE(object->scriptProperty().stringLiteral(), QString()); |
2323 | bool ok; |
2324 | QCOMPARE(object->scriptProperty().numberLiteral(&ok), qreal(0.)); |
2325 | QCOMPARE(ok, false); |
2326 | |
2327 | const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(script: object->scriptProperty()); |
2328 | QVERIFY(scriptPrivate != nullptr); |
2329 | QCOMPARE(scriptPrivate->script, QString("foo + bar")); |
2330 | QCOMPARE(scriptPrivate->scope, qobject_cast<QObject*>(object.data())); |
2331 | QCOMPARE(scriptPrivate->context, qmlContext(object.data())); |
2332 | |
2333 | QVERIFY(object->grouped() != nullptr); |
2334 | const QQmlScriptStringPrivate *groupedPrivate = QQmlScriptStringPrivate::get(script: object->grouped()->script()); |
2335 | QCOMPARE(groupedPrivate->script, QString("console.log(1921)")); |
2336 | QCOMPARE(groupedPrivate->scope, qobject_cast<QObject*>(object.data())); |
2337 | QCOMPARE(groupedPrivate->context, qmlContext(object.data())); |
2338 | } |
2339 | |
2340 | { |
2341 | QQmlComponent component(&engine, testFileUrl(fileName: "scriptString2.qml")); |
2342 | VERIFY_ERRORS(0); |
2343 | |
2344 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2345 | QVERIFY(object != nullptr); |
2346 | QCOMPARE(object->scriptProperty().stringLiteral(), QString("hello\\n\\\"world\\\"")); |
2347 | } |
2348 | |
2349 | { |
2350 | QQmlComponent component(&engine, testFileUrl(fileName: "scriptString3.qml")); |
2351 | VERIFY_ERRORS(0); |
2352 | |
2353 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2354 | QVERIFY(object != nullptr); |
2355 | bool ok; |
2356 | QCOMPARE(object->scriptProperty().numberLiteral(&ok), qreal(12.345)); |
2357 | QCOMPARE(ok, true); |
2358 | |
2359 | } |
2360 | |
2361 | { |
2362 | QQmlComponent component(&engine, testFileUrl(fileName: "scriptString4.qml")); |
2363 | VERIFY_ERRORS(0); |
2364 | |
2365 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2366 | QVERIFY(object != nullptr); |
2367 | bool ok; |
2368 | QCOMPARE(object->scriptProperty().booleanLiteral(&ok), true); |
2369 | QCOMPARE(ok, true); |
2370 | } |
2371 | |
2372 | { |
2373 | QQmlComponent component(&engine, testFileUrl(fileName: "scriptString5.qml")); |
2374 | VERIFY_ERRORS(0); |
2375 | |
2376 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2377 | QVERIFY(object != nullptr); |
2378 | QCOMPARE(object->scriptProperty().isNullLiteral(), true); |
2379 | } |
2380 | |
2381 | { |
2382 | QQmlComponent component(&engine, testFileUrl(fileName: "scriptString6.qml")); |
2383 | VERIFY_ERRORS(0); |
2384 | |
2385 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2386 | QVERIFY(object != nullptr); |
2387 | QCOMPARE(object->scriptProperty().isUndefinedLiteral(), true); |
2388 | } |
2389 | { |
2390 | QQmlComponent component(&engine, testFileUrl(fileName: "scriptString7.qml")); |
2391 | VERIFY_ERRORS(0); |
2392 | |
2393 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2394 | QVERIFY(object != nullptr); |
2395 | QQmlScriptString ss = object->scriptProperty(); |
2396 | |
2397 | { |
2398 | QQmlExpression expr(ss, /*context*/nullptr, object.data()); |
2399 | QCOMPARE(expr.evaluate().toInt(), int(100)); |
2400 | } |
2401 | |
2402 | { |
2403 | SimpleObjectWithCustomParser testScope; |
2404 | QVERIFY(testScope.metaObject()->indexOfProperty("intProperty") != object->metaObject()->indexOfProperty( "intProperty")); |
2405 | |
2406 | testScope.setIntProperty(42); |
2407 | QQmlExpression expr(ss, /*context*/nullptr, &testScope); |
2408 | QCOMPARE(expr.evaluate().toInt(), int(42)); |
2409 | } |
2410 | } |
2411 | } |
2412 | |
2413 | // Check that assignments to QQmlScriptString properties works also from within Javascript |
2414 | void tst_qqmllanguage::scriptStringJs() |
2415 | { |
2416 | QQmlComponent component(&engine, testFileUrl(fileName: "scriptStringJs.qml")); |
2417 | VERIFY_ERRORS(0); |
2418 | |
2419 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2420 | QVERIFY(object != nullptr); |
2421 | QQmlContext *context = QQmlEngine::contextForObject(object.data()); |
2422 | QVERIFY(context != nullptr); |
2423 | bool ok; |
2424 | |
2425 | QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\" hello \\\" world \"")); |
2426 | QVERIFY(!object->scriptProperty().isEmpty()); |
2427 | QVERIFY(!object->scriptProperty().isUndefinedLiteral()); |
2428 | QVERIFY(!object->scriptProperty().isNullLiteral()); |
2429 | QCOMPARE(object->scriptProperty().stringLiteral(), QString(" hello \\\" world ")); |
2430 | QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); |
2431 | QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); |
2432 | |
2433 | QJSValue inst = engine.newQObject(object: object.data()); |
2434 | QJSValue func = engine.evaluate(program: "(function(value) { this.scriptProperty = value })"); |
2435 | |
2436 | func.callWithInstance(instance: inst, args: QJSValueList() << "test a \"string "); |
2437 | QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\"test a \\\"string \"")); |
2438 | QVERIFY(!object->scriptProperty().isEmpty()); |
2439 | QVERIFY(!object->scriptProperty().isUndefinedLiteral()); |
2440 | QVERIFY(!object->scriptProperty().isNullLiteral()); |
2441 | QCOMPARE(object->scriptProperty().stringLiteral(), QString("test a \\\"string ")); |
2442 | QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); |
2443 | QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); |
2444 | |
2445 | func.callWithInstance(instance: inst, args: QJSValueList() << QJSValue::UndefinedValue); |
2446 | QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("undefined")); |
2447 | QVERIFY(!object->scriptProperty().isEmpty()); |
2448 | QVERIFY(object->scriptProperty().isUndefinedLiteral()); |
2449 | QVERIFY(!object->scriptProperty().isNullLiteral()); |
2450 | QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); |
2451 | QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); |
2452 | QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); |
2453 | |
2454 | func.callWithInstance(instance: inst, args: QJSValueList() << true); |
2455 | QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("true")); |
2456 | QVERIFY(!object->scriptProperty().isEmpty()); |
2457 | QVERIFY(!object->scriptProperty().isUndefinedLiteral()); |
2458 | QVERIFY(!object->scriptProperty().isNullLiteral()); |
2459 | QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); |
2460 | QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); |
2461 | QVERIFY(object->scriptProperty().booleanLiteral(&ok) && ok); |
2462 | |
2463 | func.callWithInstance(instance: inst, args: QJSValueList() << false); |
2464 | QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("false")); |
2465 | QVERIFY(!object->scriptProperty().isEmpty()); |
2466 | QVERIFY(!object->scriptProperty().isUndefinedLiteral()); |
2467 | QVERIFY(!object->scriptProperty().isNullLiteral()); |
2468 | QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); |
2469 | QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); |
2470 | QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && ok); |
2471 | |
2472 | func.callWithInstance(instance: inst, args: QJSValueList() << QJSValue::NullValue); |
2473 | QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("null")); |
2474 | QVERIFY(!object->scriptProperty().isEmpty()); |
2475 | QVERIFY(!object->scriptProperty().isUndefinedLiteral()); |
2476 | QVERIFY(object->scriptProperty().isNullLiteral()); |
2477 | QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); |
2478 | QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); |
2479 | QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); |
2480 | |
2481 | func.callWithInstance(instance: inst, args: QJSValueList() << 12.34); |
2482 | QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("12.34")); |
2483 | QVERIFY(!object->scriptProperty().isEmpty()); |
2484 | QVERIFY(!object->scriptProperty().isUndefinedLiteral()); |
2485 | QVERIFY(!object->scriptProperty().isNullLiteral()); |
2486 | QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); |
2487 | QVERIFY(object->scriptProperty().numberLiteral(&ok) == 12.34 && ok); |
2488 | QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); |
2489 | } |
2490 | |
2491 | struct FreeUnitData |
2492 | { |
2493 | static void cleanup(const QV4::CompiledData::Unit *readOnlyQmlUnit) |
2494 | { |
2495 | if (readOnlyQmlUnit && !(readOnlyQmlUnit->flags & QV4::CompiledData::Unit::StaticData)) |
2496 | free(ptr: const_cast<QV4::CompiledData::Unit *>(readOnlyQmlUnit)); |
2497 | } |
2498 | }; |
2499 | |
2500 | void tst_qqmllanguage::scriptStringWithoutSourceCode() |
2501 | { |
2502 | QUrl url = testFileUrl(fileName: "scriptString7.qml"); |
2503 | QScopedPointer<const QV4::CompiledData::Unit, FreeUnitData> readOnlyQmlUnit; |
2504 | { |
2505 | QQmlEnginePrivate *eng = QQmlEnginePrivate::get(e: &engine); |
2506 | QQmlRefPointer<QQmlTypeData> td = eng->typeLoader.getType(unNormalizedUrl: url); |
2507 | Q_ASSERT(td); |
2508 | |
2509 | QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit(); |
2510 | readOnlyQmlUnit.reset(other: compilationUnit->unitData()); |
2511 | Q_ASSERT(readOnlyQmlUnit); |
2512 | QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(size: readOnlyQmlUnit->unitSize)); |
2513 | memcpy(dest: qmlUnit, src: readOnlyQmlUnit.data(), n: readOnlyQmlUnit->unitSize); |
2514 | |
2515 | qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; |
2516 | compilationUnit->setUnitData(unitData: qmlUnit); |
2517 | |
2518 | const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/index: 0); |
2519 | QCOMPARE(compilationUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); |
2520 | quint32 i; |
2521 | for (i = 0; i < rootObject->nBindings; ++i) { |
2522 | const QV4::CompiledData::Binding *binding = rootObject->bindingTable() + i; |
2523 | if (compilationUnit->stringAt(index: binding->propertyNameIndex) != QString("scriptProperty")) |
2524 | continue; |
2525 | QCOMPARE(compilationUnit->bindingValueAsScriptString(binding), QString("intProperty")); |
2526 | const_cast<QV4::CompiledData::Binding*>(binding)->stringIndex = 0; // empty string index |
2527 | QVERIFY(compilationUnit->bindingValueAsScriptString(binding).isEmpty()); |
2528 | break; |
2529 | } |
2530 | QVERIFY(i < rootObject->nBindings); |
2531 | } |
2532 | QQmlComponent component(&engine, url); |
2533 | VERIFY_ERRORS(0); |
2534 | |
2535 | QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create())); |
2536 | QVERIFY(object != nullptr); |
2537 | QQmlScriptString ss = object->scriptProperty(); |
2538 | QVERIFY(!ss.isEmpty()); |
2539 | QCOMPARE(ss.stringLiteral(), QString()); |
2540 | bool ok; |
2541 | QCOMPARE(ss.numberLiteral(&ok), qreal(0.)); |
2542 | QCOMPARE(ok, false); |
2543 | |
2544 | const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(script: ss); |
2545 | QVERIFY(scriptPrivate != nullptr); |
2546 | QVERIFY(scriptPrivate->script.isEmpty()); |
2547 | QCOMPARE(scriptPrivate->scope, qobject_cast<QObject*>(object.data())); |
2548 | QCOMPARE(scriptPrivate->context, qmlContext(object.data())); |
2549 | |
2550 | { |
2551 | QQmlExpression expr(ss, /*context*/nullptr, object.data()); |
2552 | QCOMPARE(expr.evaluate().toInt(), int(100)); |
2553 | } |
2554 | } |
2555 | |
2556 | // Test the QQmlScriptString comparison operators. The script strings are considered |
2557 | // equal if there evaluation would produce the same result. |
2558 | void tst_qqmllanguage::scriptStringComparison() |
2559 | { |
2560 | QQmlComponent component1(&engine, testFileUrl(fileName: "scriptString.qml")); |
2561 | QVERIFY(!component1.isError() && component1.errors().isEmpty()); |
2562 | QScopedPointer<MyTypeObject> object1(qobject_cast<MyTypeObject*>(object: component1.create())); |
2563 | QVERIFY(object1 != nullptr); |
2564 | |
2565 | QQmlComponent component2(&engine, testFileUrl(fileName: "scriptString2.qml")); |
2566 | QVERIFY(!component2.isError() && component2.errors().isEmpty()); |
2567 | QScopedPointer<MyTypeObject> object2(qobject_cast<MyTypeObject*>(object: component2.create())); |
2568 | QVERIFY(object2 != nullptr); |
2569 | |
2570 | QQmlComponent component3(&engine, testFileUrl(fileName: "scriptString3.qml")); |
2571 | QVERIFY(!component3.isError() && component3.errors().isEmpty()); |
2572 | QScopedPointer<MyTypeObject> object3(qobject_cast<MyTypeObject*>(object: component3.create())); |
2573 | QVERIFY(object3 != nullptr); |
2574 | |
2575 | //QJSValue inst1 = engine.newQObject(object1); |
2576 | QJSValue inst2 = engine.newQObject(object: object2.data()); |
2577 | QJSValue inst3 = engine.newQObject(object: object3.data()); |
2578 | QJSValue func = engine.evaluate(program: "(function(value) { this.scriptProperty = value })"); |
2579 | |
2580 | const QString s = "hello\\n\\\"world\\\""; |
2581 | const qreal n = 12.345; |
2582 | bool ok; |
2583 | |
2584 | QCOMPARE(object2->scriptProperty().stringLiteral(), s); |
2585 | QVERIFY(object3->scriptProperty().numberLiteral(&ok) == n && ok); |
2586 | QCOMPARE(object1->scriptProperty(), object1->scriptProperty()); |
2587 | QCOMPARE(object2->scriptProperty(), object2->scriptProperty()); |
2588 | QCOMPARE(object3->scriptProperty(), object3->scriptProperty()); |
2589 | QVERIFY(object2->scriptProperty() != object3->scriptProperty()); |
2590 | QVERIFY(object1->scriptProperty() != object2->scriptProperty()); |
2591 | QVERIFY(object1->scriptProperty() != object3->scriptProperty()); |
2592 | |
2593 | func.callWithInstance(instance: inst2, args: QJSValueList() << n); |
2594 | QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); |
2595 | |
2596 | func.callWithInstance(instance: inst2, args: QJSValueList() << s); |
2597 | QVERIFY(object2->scriptProperty() != object3->scriptProperty()); |
2598 | func.callWithInstance(instance: inst3, args: QJSValueList() << s); |
2599 | QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); |
2600 | |
2601 | func.callWithInstance(instance: inst2, args: QJSValueList() << QJSValue::UndefinedValue); |
2602 | QVERIFY(object2->scriptProperty() != object3->scriptProperty()); |
2603 | func.callWithInstance(instance: inst3, args: QJSValueList() << QJSValue::UndefinedValue); |
2604 | QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); |
2605 | |
2606 | func.callWithInstance(instance: inst2, args: QJSValueList() << QJSValue::NullValue); |
2607 | QVERIFY(object2->scriptProperty() != object3->scriptProperty()); |
2608 | func.callWithInstance(instance: inst3, args: QJSValueList() << QJSValue::NullValue); |
2609 | QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); |
2610 | |
2611 | func.callWithInstance(instance: inst2, args: QJSValueList() << false); |
2612 | QVERIFY(object2->scriptProperty() != object3->scriptProperty()); |
2613 | func.callWithInstance(instance: inst3, args: QJSValueList() << false); |
2614 | QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); |
2615 | |
2616 | func.callWithInstance(instance: inst2, args: QJSValueList() << true); |
2617 | QVERIFY(object2->scriptProperty() != object3->scriptProperty()); |
2618 | func.callWithInstance(instance: inst3, args: QJSValueList() << true); |
2619 | QCOMPARE(object2->scriptProperty(), object3->scriptProperty()); |
2620 | |
2621 | QVERIFY(object1->scriptProperty() != object2->scriptProperty()); |
2622 | object2->setScriptProperty(object1->scriptProperty()); |
2623 | QCOMPARE(object1->scriptProperty(), object2->scriptProperty()); |
2624 | |
2625 | QVERIFY(object1->scriptProperty() != object3->scriptProperty()); |
2626 | func.callWithInstance(instance: inst3, args: QJSValueList() << engine.toScriptValue(value: object1->scriptProperty())); |
2627 | QCOMPARE(object1->scriptProperty(), object3->scriptProperty()); |
2628 | |
2629 | // While this are two instances of the same object they are still considered different |
2630 | // because the (none literal) script string may access variables which have different |
2631 | // values in both instances and hence evaluated to different results. |
2632 | QScopedPointer<MyTypeObject> object1_2(qobject_cast<MyTypeObject*>(object: component1.create())); |
2633 | QVERIFY(object1_2 != nullptr); |
2634 | QVERIFY(object1->scriptProperty() != object1_2->scriptProperty()); |
2635 | } |
2636 | |
2637 | // Check that default property assignments are correctly spliced into explicit |
2638 | // property assignments |
2639 | void tst_qqmllanguage::defaultPropertyListOrder() |
2640 | { |
2641 | QQmlComponent component(&engine, testFileUrl(fileName: "defaultPropertyListOrder.qml")); |
2642 | VERIFY_ERRORS(0); |
2643 | |
2644 | QScopedPointer<MyContainer> container(qobject_cast<MyContainer *>(object: component.create())); |
2645 | QVERIFY(container != nullptr); |
2646 | |
2647 | QCOMPARE(container->getChildren()->count(), 6); |
2648 | QCOMPARE(container->getChildren()->at(0)->property("index"), QVariant(0)); |
2649 | QCOMPARE(container->getChildren()->at(1)->property("index"), QVariant(1)); |
2650 | QCOMPARE(container->getChildren()->at(2)->property("index"), QVariant(2)); |
2651 | QCOMPARE(container->getChildren()->at(3)->property("index"), QVariant(3)); |
2652 | QCOMPARE(container->getChildren()->at(4)->property("index"), QVariant(4)); |
2653 | QCOMPARE(container->getChildren()->at(5)->property("index"), QVariant(5)); |
2654 | } |
2655 | |
2656 | void tst_qqmllanguage::declaredPropertyValues() |
2657 | { |
2658 | QQmlComponent component(&engine, testFileUrl(fileName: "declaredPropertyValues.qml")); |
2659 | VERIFY_ERRORS(0); |
2660 | } |
2661 | |
2662 | void tst_qqmllanguage::dontDoubleCallClassBegin() |
2663 | { |
2664 | QQmlComponent component(&engine, testFileUrl(fileName: "dontDoubleCallClassBegin.qml")); |
2665 | QScopedPointer<QObject> o(component.create()); |
2666 | QVERIFY(o); |
2667 | |
2668 | MyParserStatus *o2 = qobject_cast<MyParserStatus *>(object: qvariant_cast<QObject *>(v: o->property(name: "object"))); |
2669 | QVERIFY(o2); |
2670 | QCOMPARE(o2->classBeginCount(), 1); |
2671 | QCOMPARE(o2->componentCompleteCount(), 1); |
2672 | } |
2673 | |
2674 | void tst_qqmllanguage::reservedWords_data() |
2675 | { |
2676 | QTest::addColumn<QByteArray>(name: "word"); |
2677 | |
2678 | QTest::newRow(dataTag: "abstract") << QByteArray( "abstract"); |
2679 | QTest::newRow(dataTag: "as") << QByteArray( "as"); |
2680 | QTest::newRow(dataTag: "boolean") << QByteArray( "boolean"); |
2681 | QTest::newRow(dataTag: "break") << QByteArray( "break"); |
2682 | QTest::newRow(dataTag: "byte") << QByteArray( "byte"); |
2683 | QTest::newRow(dataTag: "case") << QByteArray( "case"); |
2684 | QTest::newRow(dataTag: "catch") << QByteArray( "catch"); |
2685 | QTest::newRow(dataTag: "char") << QByteArray( "char"); |
2686 | QTest::newRow(dataTag: "class") << QByteArray( "class"); |
2687 | QTest::newRow(dataTag: "continue") << QByteArray( "continue"); |
2688 | QTest::newRow(dataTag: "const") << QByteArray( "const"); |
2689 | QTest::newRow(dataTag: "debugger") << QByteArray( "debugger"); |
2690 | QTest::newRow(dataTag: "default") << QByteArray( "default"); |
2691 | QTest::newRow(dataTag: "delete") << QByteArray( "delete"); |
2692 | QTest::newRow(dataTag: "do") << QByteArray( "do"); |
2693 | QTest::newRow(dataTag: "double") << QByteArray( "double"); |
2694 | QTest::newRow(dataTag: "else") << QByteArray( "else"); |
2695 | QTest::newRow(dataTag: "enum") << QByteArray( "enum"); |
2696 | QTest::newRow(dataTag: "export") << QByteArray( "export"); |
2697 | QTest::newRow(dataTag: "extends") << QByteArray( "extends"); |
2698 | QTest::newRow(dataTag: "false") << QByteArray( "false"); |
2699 | QTest::newRow(dataTag: "final") << QByteArray( "final"); |
2700 | QTest::newRow(dataTag: "finally") << QByteArray( "finally"); |
2701 | QTest::newRow(dataTag: "float") << QByteArray( "float"); |
2702 | QTest::newRow(dataTag: "for") << QByteArray( "for"); |
2703 | QTest::newRow(dataTag: "function") << QByteArray( "function"); |
2704 | QTest::newRow(dataTag: "goto") << QByteArray( "goto"); |
2705 | QTest::newRow(dataTag: "if") << QByteArray( "if"); |
2706 | QTest::newRow(dataTag: "implements") << QByteArray( "implements"); |
2707 | QTest::newRow(dataTag: "import") << QByteArray( "import"); |
2708 | QTest::newRow(dataTag: "pragma") << QByteArray( "pragma"); |
2709 | QTest::newRow(dataTag: "in") << QByteArray( "in"); |
2710 | QTest::newRow(dataTag: "instanceof") << QByteArray( "instanceof"); |
2711 | QTest::newRow(dataTag: "int") << QByteArray( "int"); |
2712 | QTest::newRow(dataTag: "interface") << QByteArray( "interface"); |
2713 | QTest::newRow(dataTag: "long") << QByteArray( "long"); |
2714 | QTest::newRow(dataTag: "native") << QByteArray( "native"); |
2715 | QTest::newRow(dataTag: "new") << QByteArray( "new"); |
2716 | QTest::newRow(dataTag: "null") << QByteArray( "null"); |
2717 | QTest::newRow(dataTag: "package") << QByteArray( "package"); |
2718 | QTest::newRow(dataTag: "private") << QByteArray( "private"); |
2719 | QTest::newRow(dataTag: "protected") << QByteArray( "protected"); |
2720 | QTest::newRow(dataTag: "public") << QByteArray( "public"); |
2721 | QTest::newRow(dataTag: "return") << QByteArray( "return"); |
2722 | QTest::newRow(dataTag: "short") << QByteArray( "short"); |
2723 | QTest::newRow(dataTag: "static") << QByteArray( "static"); |
2724 | QTest::newRow(dataTag: "super") << QByteArray( "super"); |
2725 | QTest::newRow(dataTag: "switch") << QByteArray( "switch"); |
2726 | QTest::newRow(dataTag: "synchronized") << QByteArray( "synchronized"); |
2727 | QTest::newRow(dataTag: "this") << QByteArray( "this"); |
2728 | QTest::newRow(dataTag: "throw") << QByteArray( "throw"); |
2729 | QTest::newRow(dataTag: "throws") << QByteArray( "throws"); |
2730 | QTest::newRow(dataTag: "transient") << QByteArray( "transient"); |
2731 | QTest::newRow(dataTag: "true") << QByteArray( "true"); |
2732 | QTest::newRow(dataTag: "try") << QByteArray( "try"); |
2733 | QTest::newRow(dataTag: "typeof") << QByteArray( "typeof"); |
2734 | QTest::newRow(dataTag: "var") << QByteArray( "var"); |
2735 | QTest::newRow(dataTag: "void") << QByteArray( "void"); |
2736 | QTest::newRow(dataTag: "volatile") << QByteArray( "volatile"); |
2737 | QTest::newRow(dataTag: "while") << QByteArray( "while"); |
2738 | QTest::newRow(dataTag: "with") << QByteArray( "with"); |
2739 | } |
2740 | |
2741 | void tst_qqmllanguage::reservedWords() |
2742 | { |
2743 | QFETCH(QByteArray, word); |
2744 | QQmlComponent component(&engine); |
2745 | component.setData("import QtQuick 2.0\nQtObject { property string "+ word + " }", baseUrl: QUrl()); |
2746 | QCOMPARE(component.errorString(), QLatin1String(":2 Expected token `identifier'\n")); |
2747 | } |
2748 | |
2749 | // Check that first child of qml is of given type. Empty type insists on error. |
2750 | void tst_qqmllanguage::testType(const QString& qml, const QString& type, const QString& expectederror, bool partialMatch) |
2751 | { |
2752 | if (engine.importPathList() == defaultImportPathList) |
2753 | engine.addImportPath(dir: testFile(fileName: "lib")); |
2754 | |
2755 | QQmlComponent component(&engine); |
2756 | component.setData(qml.toUtf8(), baseUrl: testFileUrl(fileName: "empty.qml")); // just a file for relative local imports |
2757 | |
2758 | QTRY_VERIFY(!component.isLoading()); |
2759 | |
2760 | if (type.isEmpty()) { |
2761 | QVERIFY(component.isError()); |
2762 | QString actualerror; |
2763 | foreach (const QQmlError e, component.errors()) { |
2764 | if (!actualerror.isEmpty()) |
2765 | actualerror.append(s: "; "); |
2766 | actualerror.append(s: e.description()); |
2767 | } |
2768 | QCOMPARE(actualerror.left(partialMatch ? expectederror.length(): -1),expectederror); |
2769 | } else { |
2770 | VERIFY_ERRORS(0); |
2771 | QScopedPointer<QObject> object(component.create()); |
2772 | QVERIFY(object != nullptr); |
2773 | const QMetaObject *meta = object->metaObject(); |
2774 | for (; meta; meta = meta->superClass()) { |
2775 | const QString className(meta->className()); |
2776 | if (!className.contains(s: "_QMLTYPE_") && !className.contains(s: "_QML_")) { |
2777 | QCOMPARE(className, type); |
2778 | break; |
2779 | } |
2780 | } |
2781 | QVERIFY(meta != nullptr); |
2782 | } |
2783 | |
2784 | engine.setImportPathList(defaultImportPathList); |
2785 | } |
2786 | |
2787 | // QTBUG-17276 |
2788 | void tst_qqmllanguage::inlineAssignmentsOverrideBindings() |
2789 | { |
2790 | QQmlComponent component(&engine, testFileUrl(fileName: "inlineAssignmentsOverrideBindings.qml")); |
2791 | |
2792 | QScopedPointer<QObject> o(component.create()); |
2793 | QVERIFY(o != nullptr); |
2794 | QCOMPARE(o->property("test").toInt(), 11); |
2795 | } |
2796 | |
2797 | // QTBUG-19354 |
2798 | void tst_qqmllanguage::nestedComponentRoots() |
2799 | { |
2800 | QQmlComponent component(&engine, testFileUrl(fileName: "nestedComponentRoots.qml")); |
2801 | } |
2802 | |
2803 | // Import tests (QT-558) |
2804 | void tst_qqmllanguage::importsBuiltin_data() |
2805 | { |
2806 | // QT-610 |
2807 | |
2808 | QTest::addColumn<QString>(name: "qml"); |
2809 | QTest::addColumn<QString>(name: "type"); |
2810 | QTest::addColumn<QString>(name: "error"); |
2811 | |
2812 | // import built-ins |
2813 | QTest::newRow(dataTag: "missing import") |
2814 | << "Test {}" |
2815 | << "" |
2816 | << "Test is not a type"; |
2817 | QTest::newRow(dataTag: "not in version 0.0") |
2818 | << "import org.qtproject.Test 0.0\n" |
2819 | "Test {}" |
2820 | << "" |
2821 | << "Test is not a type"; |
2822 | QTest::newRow(dataTag: "version not installed") |
2823 | << "import org.qtproject.Test 99.0\n" |
2824 | "Test {}" |
2825 | << "" |
2826 | << "module \"org.qtproject.Test\" version 99.0 is not installed"; |
2827 | QTest::newRow(dataTag: "in version 0.0") |
2828 | << "import org.qtproject.Test 0.0\n" |
2829 | "TestTP {}" |
2830 | << "TestType" |
2831 | << ""; |
2832 | QTest::newRow(dataTag: "qualified in version 0.0") |
2833 | << "import org.qtproject.Test 0.0 as T\n" |
2834 | "T.TestTP {}" |
2835 | << "TestType" |
2836 | << ""; |
2837 | QTest::newRow(dataTag: "in version 1.0") |
2838 | << "import org.qtproject.Test 1.0\n" |
2839 | "Test {}" |
2840 | << "TestType" |
2841 | << ""; |
2842 | QTest::newRow(dataTag: "qualified wrong") |
2843 | << "import org.qtproject.Test 1.0 as T\n"// QT-610 |
2844 | "Test {}" |
2845 | << "" |
2846 | << "Test is not a type"; |
2847 | QTest::newRow(dataTag: "qualified right") |
2848 | << "import org.qtproject.Test 1.0 as T\n" |
2849 | "T.Test {}" |
2850 | << "TestType" |
2851 | << ""; |
2852 | QTest::newRow(dataTag: "qualified right but not in version 0.0") |
2853 | << "import org.qtproject.Test 0.0 as T\n" |
2854 | "T.Test {}" |
2855 | << "" |
2856 | << "T.Test is not a type"; |
2857 | QTest::newRow(dataTag: "in version 1.1") |
2858 | << "import org.qtproject.Test 1.1\n" |
2859 | "Test {}" |
2860 | << "TestType" |
2861 | << ""; |
2862 | QTest::newRow(dataTag: "in version 1.3") |
2863 | << "import org.qtproject.Test 1.3\n" |
2864 | "Test {}" |
2865 | << "TestType" |
2866 | << ""; |
2867 | QTest::newRow(dataTag: "in version 1.5") |
2868 | << "import org.qtproject.Test 1.5\n" |
2869 | "Test {}" |
2870 | << "TestType" |
2871 | << ""; |
2872 | QTest::newRow(dataTag: "changed in version 1.8") |
2873 | << "import org.qtproject.Test 1.8\n" |
2874 | "Test {}" |
2875 | << "TestType2" |
2876 | << ""; |
2877 | QTest::newRow(dataTag: "in version 1.12") |
2878 | << "import org.qtproject.Test 1.12\n" |
2879 | "Test {}" |
2880 | << "TestType2" |
2881 | << ""; |
2882 | QTest::newRow(dataTag: "old in version 1.9") |
2883 | << "import org.qtproject.Test 1.9\n" |
2884 | "OldTest {}" |
2885 | << "TestType" |
2886 | << ""; |
2887 | QTest::newRow(dataTag: "old in version 1.11") |
2888 | << "import org.qtproject.Test 1.11\n" |
2889 | "OldTest {}" |
2890 | << "TestType" |
2891 | << ""; |
2892 | QTest::newRow(dataTag: "multiversion 1") |
2893 | << "import org.qtproject.Test 1.11\n" |
2894 | "import org.qtproject.Test 1.12\n" |
2895 | "Test {}" |
2896 | << (!qmlCheckTypes()?"TestType2": "") |
2897 | << (!qmlCheckTypes()?"": "Test is ambiguous. Found in org/qtproject/Test/ in version 1.12 and 1.11"); |
2898 | QTest::newRow(dataTag: "multiversion 2") |
2899 | << "import org.qtproject.Test 1.11\n" |
2900 | "import org.qtproject.Test 1.12\n" |
2901 | "OldTest {}" |
2902 | << (!qmlCheckTypes()?"TestType": "") |
2903 | << (!qmlCheckTypes()?"": "OldTest is ambiguous. Found in org/qtproject/Test/ in version 1.12 and 1.11"); |
2904 | QTest::newRow(dataTag: "qualified multiversion 3") |
2905 | << "import org.qtproject.Test 1.0 as T0\n" |
2906 | "import org.qtproject.Test 1.8 as T8\n" |
2907 | "T0.Test {}" |
2908 | << "TestType" |
2909 | << ""; |
2910 | QTest::newRow(dataTag: "qualified multiversion 4") |
2911 | << "import org.qtproject.Test 1.0 as T0\n" |
2912 | "import org.qtproject.Test 1.8 as T8\n" |
2913 | "T8.Test {}" |
2914 | << "TestType2" |
2915 | << ""; |
2916 | } |
2917 | |
2918 | void tst_qqmllanguage::importsBuiltin() |
2919 | { |
2920 | QFETCH(QString, qml); |
2921 | QFETCH(QString, type); |
2922 | QFETCH(QString, error); |
2923 | testType(qml,type,expectederror: error); |
2924 | } |
2925 | |
2926 | void tst_qqmllanguage::importsLocal_data() |
2927 | { |
2928 | QTest::addColumn<QString>(name: "qml"); |
2929 | QTest::addColumn<QString>(name: "type"); |
2930 | QTest::addColumn<QString>(name: "error"); |
2931 | |
2932 | // import locals |
2933 | QTest::newRow(dataTag: "local import") |
2934 | << "import \"subdir\"\n"// QT-613 |
2935 | "Test {}" |
2936 | << "QQuickRectangle" |
2937 | << ""; |
2938 | QTest::newRow(dataTag: "local import second") |
2939 | << "import QtQuick 2.0\nimport \"subdir\"\n" |
2940 | "Test {}" |
2941 | << "QQuickRectangle" |
2942 | << ""; |
2943 | QTest::newRow(dataTag: "local import subsubdir") |
2944 | << "import QtQuick 2.0\nimport \"subdir/subsubdir\"\n" |
2945 | "SubTest {}" |
2946 | << "QQuickRectangle" |
2947 | << ""; |
2948 | QTest::newRow(dataTag: "local import QTBUG-7721 A") |
2949 | << "subdir.Test {}"// no longer allowed (QTBUG-7721) |
2950 | << "" |
2951 | << "subdir.Test - subdir is neither a type nor a namespace"; |
2952 | QTest::newRow(dataTag: "local import QTBUG-7721 B") |
2953 | << "import \"subdir\" as X\n" |
2954 | "X.subsubdir.SubTest {}"// no longer allowed (QTBUG-7721) |
2955 | << "" |
2956 | << "X.subsubdir.SubTest - subsubdir is not a type"; |
2957 | QTest::newRow(dataTag: "local import as") |
2958 | << "import \"subdir\" as T\n" |
2959 | "T.Test {}" |
2960 | << "QQuickRectangle" |
2961 | << ""; |
2962 | QTest::newRow(dataTag: "wrong local import as") |
2963 | << "import \"subdir\" as T\n" |
2964 | "Test {}" |
2965 | << "" |
2966 | << "Test is not a type"; |
2967 | QTest::newRow(dataTag: "library precedence over local import") |
2968 | << "import \"subdir\"\n" |
2969 | "import org.qtproject.Test 1.0\n" |
2970 | "Test {}" |
2971 | << (!qmlCheckTypes()?"TestType": "") |
2972 | << (!qmlCheckTypes()?"": "Test is ambiguous. Found in org/qtproject/Test/ and in subdir/"); |
2973 | |
2974 | if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) { |
2975 | // file URL doesn't work with qrc scheme |
2976 | QTest::newRow(dataTag: "file URL survives percent-encoding") |
2977 | << "import \""+ QUrl::fromLocalFile(localfile: QDir::currentPath() + "/{subdir}").toString() + "\"\n" |
2978 | "Test {}" |
2979 | << "QQuickRectangle" |
2980 | << ""; |
2981 | } |
2982 | } |
2983 | |
2984 | void tst_qqmllanguage::importsLocal() |
2985 | { |
2986 | QFETCH(QString, qml); |
2987 | QFETCH(QString, type); |
2988 | QFETCH(QString, error); |
2989 | testType(qml,type,expectederror: error); |
2990 | } |
2991 | |
2992 | void tst_qqmllanguage::basicRemote_data() |
2993 | { |
2994 | QTest::addColumn<QUrl>(name: "url"); |
2995 | QTest::addColumn<QString>(name: "type"); |
2996 | QTest::addColumn<QString>(name: "error"); |
2997 | |
2998 | QString serverdir = "/qtest/qml/qqmllanguage/"; |
2999 | |
3000 | QTest::newRow(dataTag: "no need for qmldir") << QUrl(serverdir+ "Test.qml") << ""<< ""; |
3001 | QTest::newRow(dataTag: "absent qmldir") << QUrl(serverdir+ "/noqmldir/Test.qml") << ""<< ""; |
3002 | QTest::newRow(dataTag: "need qmldir") << QUrl(serverdir+ "TestNamed.qml") << ""<< ""; |
3003 | } |
3004 | |
3005 | void tst_qqmllanguage::basicRemote() |
3006 | { |
3007 | QFETCH(QUrl, url); |
3008 | QFETCH(QString, type); |
3009 | QFETCH(QString, error); |
3010 | |
3011 | ThreadedTestHTTPServer server(dataDirectory()); |
3012 | |
3013 | url = server.baseUrl().resolved(relative: url); |
3014 | |
3015 | QQmlComponent component(&engine, url); |
3016 | |
3017 | QTRY_VERIFY(!component.isLoading()); |
3018 | |
3019 | if (error.isEmpty()) { |
3020 | if (component.isError()) |
3021 | qDebug() << component.errors(); |
3022 | QVERIFY(!component.isError()); |
3023 | } else { |
3024 | QVERIFY(component.isError()); |
3025 | } |
3026 | } |
3027 | |
3028 | void tst_qqmllanguage::importsRemote_data() |
3029 | { |
3030 | QTest::addColumn<QString>(name: "qml"); |
3031 | QTest::addColumn<QString>(name: "type"); |
3032 | QTest::addColumn<QString>(name: "error"); |
3033 | |
3034 | QString serverdir = "{{ServerBaseUrl}}/qtest/qml/qqmllanguage"; |
3035 | |
3036 | QTest::newRow(dataTag: "remote import") << "import \""+serverdir+ "\"\nTest {}"<< "QQuickRectangle" |
3037 | << ""; |
3038 | QTest::newRow(dataTag: "remote import with subdir") << "import \""+serverdir+ "\"\nTestSubDir {}"<< "QQuickText" |
3039 | << ""; |
3040 | QTest::newRow(dataTag: "remote import with local") << "import \""+serverdir+ "\"\nTestLocal {}"<< "QQuickImage" |
3041 | << ""; |
3042 | QTest::newRow(dataTag: "remote import with qualifier") << "import \""+serverdir+ "\" as NS\nNS.NamedLocal {}"<< "QQuickImage" |
3043 | << ""; |
3044 | QTest::newRow(dataTag: "wrong remote import with undeclared local") << "import \""+serverdir+ "\"\nWrongTestLocal {}"<< "" |
3045 | << "WrongTestLocal is not a type"; |
3046 | QTest::newRow(dataTag: "wrong remote import of internal local") << "import \""+serverdir+ "\"\nLocalInternal {}"<< "" |
3047 | << "LocalInternal is not a type"; |
3048 | QTest::newRow(dataTag: "wrong remote import of undeclared local") << "import \""+serverdir+ "\"\nUndeclaredLocal {}"<< "" |
3049 | << "UndeclaredLocal is not a type"; |
3050 | } |
3051 | |
3052 | void tst_qqmllanguage::importsRemote() |
3053 | { |
3054 | QFETCH(QString, qml); |
3055 | QFETCH(QString, type); |
3056 | QFETCH(QString, error); |
3057 | |
3058 | ThreadedTestHTTPServer server(dataDirectory()); |
3059 | |
3060 | qml.replace(QStringLiteral("{{ServerBaseUrl}}"), after: server.baseUrl().toString()); |
3061 | |
3062 | testType(qml,type,expectederror: error); |
3063 | } |
3064 | |
3065 | void tst_qqmllanguage::importsInstalled_data() |
3066 | { |
3067 | // QT-610 |
3068 | |
3069 | QTest::addColumn<QString>(name: "qml"); |
3070 | QTest::addColumn<QString>(name: "type"); |
3071 | QTest::addColumn<QString>(name: "error"); |
3072 | |
3073 | // import installed |
3074 | QTest::newRow(dataTag: "installed import 0") |
3075 | << "import org.qtproject.installedtest0 0.0\n" |
3076 | "InstalledTestTP {}" |
3077 | << "QQuickRectangle" |
3078 | << ""; |
3079 | QTest::newRow(dataTag: "installed import 0 as TP") |
3080 | << "import org.qtproject.installedtest0 0.0 as TP\n" |
3081 | "TP.InstalledTestTP {}" |
3082 | << "QQuickRectangle" |
3083 | << ""; |
3084 | QTest::newRow(dataTag: "installed import 1") |
3085 | << "import org.qtproject.installedtest 1.0\n" |
3086 | "InstalledTest {}" |
3087 | << "QQuickRectangle" |
3088 | << ""; |
3089 | QTest::newRow(dataTag: "installed import 2") |
3090 | << "import org.qtproject.installedtest 1.3\n" |
3091 | "InstalledTest {}" |
3092 | << "QQuickRectangle" |
3093 | << ""; |
3094 | QTest::newRow(dataTag: "installed import 3") |
3095 | << "import org.qtproject.installedtest 1.4\n" |
3096 | "InstalledTest {}" |
3097 | << "QQuickText" |
3098 | << ""; |
3099 | QTest::newRow(dataTag: "installed import minor version not available") // QTBUG-11936 |
3100 | << "import org.qtproject.installedtest 0.1\n" |
3101 | "InstalledTest {}" |
3102 | << "" |
3103 | << "module \"org.qtproject.installedtest\" version 0.1 is not installed"; |
3104 | QTest::newRow(dataTag: "installed import minor version not available") // QTBUG-9627 |
3105 | << "import org.qtproject.installedtest 1.10\n" |
3106 | "InstalledTest {}" |
3107 | << "" |
3108 | << "module \"org.qtproject.installedtest\" version 1.10 is not installed"; |
3109 | QTest::newRow(dataTag: "installed import major version not available") // QTBUG-9627 |
3110 | << "import org.qtproject.installedtest 9.0\n" |
3111 | "InstalledTest {}" |
3112 | << "" |
3113 | << "module \"org.qtproject.installedtest\" version 9.0 is not installed"; |
3114 | QTest::newRow(dataTag: "installed import visibility") // QT-614 |
3115 | << "import org.qtproject.installedtest 1.4\n" |
3116 | "PrivateType {}" |
3117 | << "" |
3118 | << "PrivateType is not a type"; |
3119 | QTest::newRow(dataTag: "installed import version QML clash") |
3120 | << "import org.qtproject.installedtest1 1.0\n" |
3121 | "Test {}" |
3122 | << "" |
3123 | << "\"Test\" version 1.0 is defined more than once in module \"org.qtproject.installedtest1\""; |
3124 | QTest::newRow(dataTag: "installed import version JS clash") |
3125 | << "import org.qtproject.installedtest2 1.0\n" |
3126 | "Test {}" |
3127 | << "" |
3128 | << "\"Test\" version 1.0 is defined more than once in module \"org.qtproject.installedtest2\""; |
3129 | } |
3130 | |
3131 | void tst_qqmllanguage::importsInstalled() |
3132 | { |
3133 | QFETCH(QString, qml); |
3134 | QFETCH(QString, type); |
3135 | QFETCH(QString, error); |
3136 | testType(qml,type,expectederror: error); |
3137 | } |
3138 | |
3139 | void tst_qqmllanguage::importsInstalledRemote_data() |
3140 | { |
3141 | // Repeat the tests for local installed data |
3142 | importsInstalled_data(); |
3143 | } |
3144 | |
3145 | void tst_qqmllanguage::importsInstalledRemote() |
3146 | { |
3147 | QFETCH(QString, qml); |
3148 | QFETCH(QString, type); |
3149 | QFETCH(QString, error); |
3150 | |
3151 | ThreadedTestHTTPServer server(dataDirectory()); |
3152 | |
3153 | QString serverdir = server.urlString(documentPath: "/lib/"); |
3154 | engine.setImportPathList(QStringList(defaultImportPathList) << serverdir); |
3155 | |
3156 | testType(qml,type,expectederror: error); |
3157 | |
3158 | engine.setImportPathList(defaultImportPathList); |
3159 | } |
3160 | |
3161 | void tst_qqmllanguage::importsPath_data() |
3162 | { |
3163 | QTest::addColumn<QStringList>(name: "importPath"); |
3164 | QTest::addColumn<QString>(name: "qml"); |
3165 | QTest::addColumn<QString>(name: "value"); |
3166 | |
3167 | QTest::newRow(dataTag: "local takes priority normal") |
3168 | << (QStringList() << testFile(fileName: "lib") << "{{ServerBaseUrl}}/lib2/") |
3169 | << "import testModule 1.0\n" |
3170 | "Test {}" |
3171 | << "foo"; |
3172 | |
3173 | QTest::newRow(dataTag: "local takes priority reversed") |
3174 | << (QStringList() << "{{ServerBaseUrl}}/lib/"<< testFile(fileName: "lib2")) |
3175 | << "import testModule 1.0\n" |
3176 | "Test {}" |
3177 | << "bar"; |
3178 | |
3179 | QTest::newRow(dataTag: "earlier takes priority 1") |
3180 | << (QStringList() << "{{ServerBaseUrl}}/lib/"<< "{{ServerBaseUrl}}/lib2/") |
3181 | << "import testModule 1.0\n" |
3182 | "Test {}" |
3183 | << "foo"; |
3184 | |
3185 | QTest::newRow(dataTag: "earlier takes priority 2") |
3186 | << (QStringList() << "{{ServerBaseUrl}}/lib2/"<< "{{ServerBaseUrl}}/lib/") |
3187 | << "import testModule 1.0\n" |
3188 | "Test {}" |
3189 | << "bar"; |
3190 | |
3191 | QTest::newRow(dataTag: "major version takes priority over unversioned") |
3192 | << (QStringList() << "{{ServerBaseUrl}}/lib/"<< "{{ServerBaseUrl}}/lib3/") |
3193 | << "import testModule 1.0\n" |
3194 | "Test {}" |
3195 | << "baz"; |
3196 | |
3197 | QTest::newRow(dataTag: "major version takes priority over minor") |
3198 | << (QStringList() << "{{ServerBaseUrl}}/lib4/"<< "{{ServerBaseUrl}}/lib3/") |
3199 | << "import testModule 1.0\n" |
3200 | "Test {}" |
3201 | << "baz"; |
3202 | |
3203 | QTest::newRow(dataTag: "minor version takes priority over unversioned") |
3204 | << (QStringList() << "{{ServerBaseUrl}}/lib/"<< "{{ServerBaseUrl}}/lib4/") |
3205 | << "import testModule 1.0\n" |
3206 | "Test {}" |
3207 | << "qux"; |
3208 | } |
3209 | |
3210 | void tst_qqmllanguage::importsPath() |
3211 | { |
3212 | QFETCH(QStringList, importPath); |
3213 | QFETCH(QString, qml); |
3214 | QFETCH(QString, value); |
3215 | |
3216 | ThreadedTestHTTPServer server(dataDirectory()); |
3217 | |
3218 | for (int i = 0; i < importPath.count(); ++i) |
3219 | importPath[i].replace(QStringLiteral("{{ServerBaseUrl}}"), after: server.baseUrl().toString()); |
3220 | |
3221 | engine.setImportPathList(QStringList(defaultImportPathList) << importPath); |
3222 | |
3223 | QQmlComponent component(&engine); |
3224 | component.setData(qml.toUtf8(), baseUrl: testFileUrl(fileName: "empty.qml")); |
3225 | |
3226 | QTRY_VERIFY(component.isReady()); |
3227 | VERIFY_ERRORS(0); |
3228 | |
3229 | QScopedPointer<QObject> object(component.create()); |
3230 | QVERIFY(object != nullptr); |
3231 | QCOMPARE(object->property("test").toString(), value); |
3232 | |
3233 | engine.setImportPathList(defaultImportPathList); |
3234 | } |
3235 | |
3236 | void tst_qqmllanguage::importsOrder_data() |
3237 | { |
3238 | QTest::addColumn<QString>(name: "qml"); |
3239 | QTest::addColumn<QString>(name: "type"); |
3240 | QTest::addColumn<QString>(name: "error"); |
3241 | QTest::addColumn<bool>(name: "partialMatch"); |
3242 | |
3243 | QTest::newRow(dataTag: "double import") << |
3244 | "import org.qtproject.installedtest 1.4\n" |
3245 | "import org.qtproject.installedtest 1.4\n" |
3246 | "InstalledTest {}" |
3247 | << (!qmlCheckTypes()?"QQuickText": "") |
3248 | << (!qmlCheckTypes()?"": "InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.4 and 1.4") |
3249 | << false; |
3250 | QTest::newRow(dataTag: "installed import overrides 1") << |
3251 | "import org.qtproject.installedtest 1.0\n" |
3252 | "import org.qtproject.installedtest 1.4\n" |
3253 | "InstalledTest {}" |
3254 | << (!qmlCheckTypes()?"QQuickText": "") |
3255 | << (!qmlCheckTypes()?"": "InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.4 and 1.0") |
3256 | << false; |
3257 | QTest::newRow(dataTag: "installed import overrides 2") << |
3258 | "import org.qtproject.installedtest 1.4\n" |
3259 | "import org.qtproject.installedtest 1.0\n" |
3260 | "InstalledTest {}" |
3261 | << (!qmlCheckTypes()?"QQuickRectangle": "") |
3262 | << (!qmlCheckTypes()?"": "InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.0 and 1.4") |
3263 | << false; |
3264 | QTest::newRow(dataTag: "installed import re-overrides 1") << |
3265 | "import org.qtproject.installedtest 1.4\n" |
3266 | "import org.qtproject.installedtest 1.0\n" |
3267 | "import org.qtproject.installedtest 1.4\n" |
3268 | "InstalledTest {}" |
3269 | << (!qmlCheckTypes()?"QQuickText": "") |
3270 | << (!qmlCheckTypes()?"": "InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.4 and 1.0") |
3271 | << false; |
3272 | QTest::newRow(dataTag: "installed import re-overrides 2") << |
3273 | "import org.qtproject.installedtest 1.4\n" |
3274 | "import org.qtproject.installedtest 1.0\n" |
3275 | "import org.qtproject.installedtest 1.4\n" |
3276 | "import org.qtproject.installedtest 1.0\n" |
3277 | "InstalledTest {}" |
3278 | << (!qmlCheckTypes()?"QQuickRectangle": "") |
3279 | << (!qmlCheckTypes()?"": "InstalledTest is ambiguous. Found in lib/org/qtproject/installedtest/ in version 1.0 and 1.4") |
3280 | << false; |
3281 | QTest::newRow(dataTag: "installed import versus builtin 1") << |
3282 | "import org.qtproject.installedtest 1.5\n" |
3283 | "import QtQuick 2.0\n" |
3284 | "Rectangle {}" |
3285 | << (!qmlCheckTypes()?"QQuickRectangle": "") |
3286 | << (!qmlCheckTypes()?"": "Rectangle is ambiguous. Found in file://") |
3287 | << true; |
3288 | QTest::newRow(dataTag: "installed import versus builtin 2") << |
3289 | "import QtQuick 2.0\n" |
3290 | "import org.qtproject.installedtest 1.5\n" |
3291 | "Rectangle {}" |
3292 | << (!qmlCheckTypes()?"QQuickText": "") |
3293 | << (!qmlCheckTypes()?"": "Rectangle is ambiguous. Found in lib/org/qtproject/installedtest/ and in file://") |
3294 | << true; |
3295 | QTest::newRow(dataTag: "namespaces cannot be overridden by types 1") << |
3296 | "import QtQuick 2.0 as Rectangle\n" |
3297 | "import org.qtproject.installedtest 1.5\n" |
3298 | "Rectangle {}" |
3299 | << "" |
3300 | << "Namespace Rectangle cannot be used as a type" |
3301 | << false; |
3302 | QTest::newRow(dataTag: "namespaces cannot be overridden by types 2") << |
3303 | "import QtQuick 2.0 as Rectangle\n" |
3304 | "import org.qtproject.installedtest 1.5\n" |
3305 | "Rectangle.Image {}" |
3306 | << "QQuickImage" |
3307 | << "" |
3308 | << false; |
3309 | QTest::newRow(dataTag: "local last 1") << |
3310 | "LocalLast {}" |
3311 | << "QQuickText" |
3312 | << "" |
3313 | << false; |
3314 | QTest::newRow(dataTag: "local last 2") << |
3315 | "import org.qtproject.installedtest 1.0\n" |
3316 | "LocalLast {}" |
3317 | << (!qmlCheckTypes()?"QQuickRectangle": "")// i.e. from org.qtproject.installedtest, not data/LocalLast.qml |
3318 | << (!qmlCheckTypes()?"": "LocalLast is ambiguous. Found in lib/org/qtproject/installedtest/ and in ") |
3319 | << false; |
3320 | QTest::newRow(dataTag: "local last 3") << //Forces it to load the local qmldir to resolve types, but they shouldn't override anything |
3321 | "import org.qtproject.installedtest 1.0\n" |
3322 | "LocalLast {LocalLast2{}}" |
3323 | << (!qmlCheckTypes()?"QQuickRectangle": "")// i.e. from org.qtproject.installedtest, not data/LocalLast.qml |
3324 | << (!qmlCheckTypes()?"": "LocalLast is ambiguous. Found in lib/org/qtproject/installedtest/ and in ") |
3325 | << false; |
3326 | } |
3327 | |
3328 | void tst_qqmllanguage::importsOrder() |
3329 | { |
3330 | QFETCH(QString, qml); |
3331 | QFETCH(QString, type); |
3332 | QFETCH(QString, error); |
3333 | QFETCH(bool, partialMatch); |
3334 | testType(qml,type,expectederror: error,partialMatch); |
3335 | } |
3336 | |
3337 | void tst_qqmllanguage::importIncorrectCase() |
3338 | { |
3339 | if (engine.importPathList() == defaultImportPathList) |
3340 | engine.addImportPath(dir: testFile(fileName: "lib")); |
3341 | |
3342 | // Load "importIncorrectCase.qml" using wrong case |
3343 | QQmlComponent component(&engine, testFileUrl(fileName: "ImportIncorrectCase.qml")); |
3344 | |
3345 | QList<QQmlError> errors = component.errors(); |
3346 | QCOMPARE(errors.count(), 1); |
3347 | |
3348 | const QString expectedError = isCaseSensitiveFileSystem(path: dataDirectory()) ? |
3349 | QStringLiteral("No such file or directory") : |
3350 | QStringLiteral("File name case mismatch"); |
3351 | QCOMPARE(errors.at(0).description(), expectedError); |
3352 | |
3353 | engine.setImportPathList(defaultImportPathList); |
3354 | } |
3355 | |
3356 | void tst_qqmllanguage::importJs_data() |
3357 | { |
3358 | QTest::addColumn<QString>(name: "file"); |
3359 | QTest::addColumn<QString>(name: "errorFile"); |
3360 | QTest::addColumn<bool>(name: "performTest"); |
3361 | |
3362 | QTest::newRow(dataTag: "defaultVersion") |
3363 | << "importJs.1.qml" |
3364 | << "importJs.1.errors.txt" |
3365 | << true; |
3366 | |
3367 | QTest::newRow(dataTag: "specifiedVersion") |
3368 | << "importJs.2.qml" |
3369 | << "importJs.2.errors.txt" |
3370 | << true; |
3371 | |
3372 | QTest::newRow(dataTag: "excludeExcessiveVersion") |
3373 | << "importJs.3.qml" |
3374 | << "importJs.3.errors.txt" |
3375 | << false; |
3376 | |
3377 | QTest::newRow(dataTag: "includeAppropriateVersion") |
3378 | << "importJs.4.qml" |
3379 | << "importJs.4.errors.txt" |
3380 | << true; |
3381 | |
3382 | QTest::newRow(dataTag: "noDefaultVersion") |
3383 | << "importJs.5.qml" |
3384 | << "importJs.5.errors.txt" |
3385 | << false; |
3386 | |
3387 | QTest::newRow(dataTag: "repeatImportFails") |
3388 | << "importJs.6.qml" |
3389 | << "importJs.6.errors.txt" |
3390 | << false; |
3391 | |
3392 | QTest::newRow(dataTag: "multipleVersionImportFails") |
3393 | << "importJs.7.qml" |
3394 | << "importJs.7.errors.txt" |
3395 | << false; |
3396 | |
3397 | QTest::newRow(dataTag: "namespacedImport") |
3398 | << "importJs.8.qml" |
3399 | << "importJs.8.errors.txt" |
3400 | << true; |
3401 | |
3402 | QTest::newRow(dataTag: "namespacedVersionedImport") |
3403 | << "importJs.9.qml" |
3404 | << "importJs.9.errors.txt" |
3405 | << true; |
3406 | |
3407 | QTest::newRow(dataTag: "namespacedRepeatImport") |
3408 | << "importJs.10.qml" |
3409 | << "importJs.10.errors.txt" |
3410 | << true; |
3411 | |
3412 | QTest::newRow(dataTag: "emptyScript") |
3413 | << "importJs.11.qml" |
3414 | << "importJs.11.errors.txt" |
3415 | << true; |
3416 | } |
3417 | |
3418 | void tst_qqmllanguage::importJs() |
3419 | { |
3420 | QFETCH(QString, file); |
3421 | QFETCH(QString, errorFile); |
3422 | QFETCH(bool, performTest); |
3423 | |
3424 | engine.setImportPathList(QStringList(defaultImportPathList) << testFile(fileName: "lib")); |
3425 | |
3426 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
3427 | |
3428 | { |
3429 | DETERMINE_ERRORS(errorFile,expected,actual); |
3430 | QCOMPARE(expected.size(), actual.size()); |
3431 | for (int i = 0; i < expected.size(); ++i) |
3432 | { |
3433 | const int compareLen = qMin(a: expected.at(i).length(), b: actual.at(i).length()); |
3434 | QCOMPARE(expected.at(i).left(compareLen), actual.at(i).left(compareLen)); |
3435 | } |
3436 | } |
3437 | |
3438 | if (performTest) { |
3439 | QScopedPointer<QObject> object(component.create()); |
3440 | QVERIFY(object != nullptr); |
3441 | QCOMPARE(object->property("test").toBool(),true); |
3442 | } |
3443 | |
3444 | engine.setImportPathList(defaultImportPathList); |
3445 | } |
3446 | |
3447 | void tst_qqmllanguage::importJsModule_data() |
3448 | { |
3449 | QTest::addColumn<QString>(name: "file"); |
3450 | |
3451 | QTest::newRow(dataTag: "plainImport") |
3452 | << "importJsModule.1.qml"; |
3453 | |
3454 | QTest::newRow(dataTag: "ImportQmlStyle") |
3455 | << "importJsModule.2.qml"; |
3456 | |
3457 | QTest::newRow(dataTag: "plainImportWithCycle") |
3458 | << "importJsModule.3.qml"; |
3459 | } |
3460 | |
3461 | void tst_qqmllanguage::importJsModule() |
3462 | { |
3463 | QFETCH(QString, file); |
3464 | |
3465 | engine.setImportPathList(QStringList(defaultImportPathList) << testFile(fileName: "lib")); |
3466 | auto importPathGuard = qScopeGuard(f: [this]{ |
3467 | engine.setImportPathList(defaultImportPathList); |
3468 | }); |
3469 | |
3470 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
3471 | QVERIFY2(!component.isError(), qPrintable(component.errorString())); |
3472 | QScopedPointer<QObject> object(component.create()); |
3473 | QVERIFY(object != nullptr); |
3474 | QCOMPARE(object->property("test").toBool(),true); |
3475 | } |
3476 | |
3477 | void tst_qqmllanguage::explicitSelfImport() |
3478 | { |
3479 | engine.setImportPathList(QStringList(defaultImportPathList) << testFile(fileName: "lib")); |
3480 | |
3481 | QQmlComponent component(&engine, testFileUrl(fileName: "mixedModuleWithSelfImport.qml")); |
3482 | QVERIFY(component.errors().count() == 0); |
3483 | |
3484 | engine.setImportPathList(defaultImportPathList); |
3485 | } |
3486 | |
3487 | void tst_qqmllanguage::importInternalType() |
3488 | { |
3489 | QQmlEngine engine; |
3490 | engine.addImportPath(dir: dataDirectory()); |
3491 | |
3492 | { |
3493 | QQmlComponent component(&engine); |
3494 | component.setData("import modulewithinternaltypes 1.0\nPublicType{}", baseUrl: QUrl()); |
3495 | VERIFY_ERRORS(0); |
3496 | QScopedPointer<QObject> obj(component.create()); |
3497 | QVERIFY(!obj.isNull()); |
3498 | QVERIFY(obj->property("myInternalType").value<QObject*>() != 0); |
3499 | } |
3500 | { |
3501 | QQmlComponent component(&engine); |
3502 | component.setData("import modulewithinternaltypes 1.0\nPublicTypeWithExplicitImport{}", baseUrl: QUrl()); |
3503 | VERIFY_ERRORS(0); |
3504 | QScopedPointer<QObject> obj(component.create()); |
3505 | QVERIFY(!obj.isNull()); |
3506 | QVERIFY(obj->property("myInternalType").value<QObject*>() != 0); |
3507 | } |
3508 | } |
3509 | |
3510 | void tst_qqmllanguage::qmlAttachedPropertiesObjectMethod() |
3511 | { |
3512 | QObject object; |
3513 | |
3514 | QCOMPARE(qmlAttachedPropertiesObject<MyQmlObject>(&object, false), (QObject *)nullptr); |
3515 | QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(&object, true)); |
3516 | |
3517 | { |
3518 | QQmlComponent component(&engine, testFileUrl(fileName: "qmlAttachedPropertiesObjectMethod.1.qml")); |
3519 | VERIFY_ERRORS(0); |
3520 | QScopedPointer<QObject> object(component.create()); |
3521 | QVERIFY(object != nullptr); |
3522 | |
3523 | QCOMPARE(qmlAttachedPropertiesObject<MyQmlObject>(object.data(), false), (QObject *)nullptr); |
3524 | QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object.data(), true) != nullptr); |
3525 | } |
3526 | |
3527 | { |
3528 | QQmlComponent component(&engine, testFileUrl(fileName: "qmlAttachedPropertiesObjectMethod.2.qml")); |
3529 | VERIFY_ERRORS(0); |
3530 | QScopedPointer<QObject> object(component.create()); |
3531 | QVERIFY(object != nullptr); |
3532 | |
3533 | QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object.data(), false) != nullptr); |
3534 | QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object.data(), true) != nullptr); |
3535 | } |
3536 | } |
3537 | |
3538 | void tst_qqmllanguage::crash1() |
3539 | { |
3540 | QQmlComponent component(&engine); |
3541 | component.setData("import QtQuick 2.0\nComponent {}", baseUrl: QUrl()); |
3542 | } |
3543 | |
3544 | void tst_qqmllanguage::crash2() |
3545 | { |
3546 | QQmlComponent component(&engine, testFileUrl(fileName: "crash2.qml")); |
3547 | } |
3548 | |
3549 | // QTBUG-8676 |
3550 | void tst_qqmllanguage::customOnProperty() |
3551 | { |
3552 | QQmlComponent component(&engine, testFileUrl(fileName: "customOnProperty.qml")); |
3553 | |
3554 | VERIFY_ERRORS(0); |
3555 | QScopedPointer<QObject> object(component.create()); |
3556 | QVERIFY(object != nullptr); |
3557 | |
3558 | QCOMPARE(object->property("on").toInt(), 10); |
3559 | } |
3560 | |
3561 | // QTBUG-12601 |
3562 | void tst_qqmllanguage::variantNotify() |
3563 | { |
3564 | QQmlComponent component(&engine, testFileUrl(fileName: "variantNotify.qml")); |
3565 | |
3566 | VERIFY_ERRORS(0); |
3567 | QScopedPointer<QObject> object(component.create()); |
3568 | QVERIFY(object != nullptr); |
3569 | |
3570 | QCOMPARE(object->property("notifyCount").toInt(), 1); |
3571 | } |
3572 | |
3573 | void tst_qqmllanguage::revisions() |
3574 | { |
3575 | { |
3576 | QQmlComponent component(&engine, testFileUrl(fileName: "revisions11.qml")); |
3577 | |
3578 | VERIFY_ERRORS(0); |
3579 | QScopedPointer<MyRevisionedClass> object(qobject_cast<MyRevisionedClass*>(object: component.create())); |
3580 | QVERIFY(object != nullptr); |
3581 | |
3582 | QCOMPARE(object->prop2(), 10.0); |
3583 | } |
3584 | { |
3585 | QQmlEngine myEngine; |
3586 | QQmlComponent component(&myEngine, testFileUrl(fileName: "revisionssub11.qml")); |
3587 | |
3588 | VERIFY_ERRORS(0); |
3589 | QScopedPointer<MyRevisionedSubclass> object(qobject_cast<MyRevisionedSubclass*>(object: component.create())); |
3590 | QVERIFY(object != nullptr); |
3591 | |
3592 | QCOMPARE(object->prop1(), 10.0); |
3593 | QCOMPARE(object->prop2(), 10.0); |
3594 | QCOMPARE(object->prop3(), 10.0); |
3595 | QCOMPARE(object->prop4(), 10.0); |
3596 | } |
3597 | { |
3598 | QQmlComponent component(&engine, testFileUrl(fileName: "versionedbase.qml")); |
3599 | VERIFY_ERRORS(0); |
3600 | QScopedPointer<MySubclass> object(qobject_cast<MySubclass*>(object: component.create())); |
3601 | QVERIFY(object != nullptr); |
3602 | |
3603 | QCOMPARE(object->prop1(), 10.0); |
3604 | QCOMPARE(object->prop2(), 10.0); |
3605 | } |
3606 | } |
3607 | |
3608 | void tst_qqmllanguage::revisionOverloads() |
3609 | { |
3610 | { |
3611 | QQmlComponent component(&engine, testFileUrl(fileName: "allowedRevisionOverloads.qml")); |
3612 | VERIFY_ERRORS(0); |
3613 | } |
3614 | { |
3615 | QQmlComponent component(&engine, testFileUrl(fileName: "disallowedRevisionOverloads.qml")); |
3616 | QEXPECT_FAIL("", "QTBUG-13849", Abort); |
3617 | QVERIFY(0); |
3618 | VERIFY_ERRORS("disallowedRevisionOverloads.errors.txt"); |
3619 | } |
3620 | } |
3621 | |
3622 | void tst_qqmllanguage::subclassedUncreateableRevision_data() |
3623 | { |
3624 | QTest::addColumn<QString>(name: "version"); |
3625 | QTest::addColumn<QString>(name: "prop"); |
3626 | QTest::addColumn<bool>(name: "shouldWork"); |
3627 | |
3628 | QTest::newRow(dataTag: "prop1 exists in 1.0") << "1.0"<< "prop1"<< true; |
3629 | QTest::newRow(dataTag: "prop2 does not exist in 1.0") << "1.0"<< "prop2"<< false; |
3630 | QTest::newRow(dataTag: "prop3 does not exist in 1.0") << "1.0"<< "prop3"<< false; |
3631 | |
3632 | QTest::newRow(dataTag: "prop1 exists in 1.1") << "1.1"<< "prop1"<< true; |
3633 | QTest::newRow(dataTag: "prop2 works because it's re-declared in Derived") << "1.1"<< "prop2"<< true; |
3634 | QTest::newRow(dataTag: "prop3 only works if the Base REVISION 1 is picked up") << "1.1"<< "prop3"<< true; |
3635 | |
3636 | } |
3637 | |
3638 | void tst_qqmllanguage::subclassedUncreateableRevision() |
3639 | { |
3640 | QFETCH(QString, version); |
3641 | QFETCH(QString, prop); |
3642 | QFETCH(bool, shouldWork); |
3643 | |
3644 | { |
3645 | QQmlEngine engine; |
3646 | QString qml = QString("import QtQuick 2.0\nimport Test %1\nMyUncreateableBaseClass {}").arg(a: version); |
3647 | QQmlComponent c(&engine); |
3648 | QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready"); |
3649 | c.setData(qml.toUtf8(), baseUrl: QUrl::fromLocalFile(localfile: QDir::currentPath())); |
3650 | QScopedPointer<QObject> obj(c.create()); |
3651 | QCOMPARE(obj.data(), static_cast<QObject*>(nullptr)); |
3652 | QCOMPARE(c.errors().count(), 1); |
3653 | QCOMPARE(c.errors().first().description(), QString("Cannot create MyUncreateableBaseClass")); |
3654 | } |
3655 | |
3656 | QQmlEngine engine; |
3657 | QString qml = QString("import QtQuick 2.0\nimport Test %1\nMyCreateableDerivedClass {\n%3: true\n}").arg(a: version).arg(a: prop); |
3658 | QQmlComponent c(&engine); |
3659 | if (!shouldWork) |
3660 | QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready"); |
3661 | c.setData(qml.toUtf8(), baseUrl: QUrl::fromLocalFile(localfile: QDir::currentPath())); |
3662 | QScopedPointer<QObject> obj(c.create()); |
3663 | if (!shouldWork) { |
3664 | QCOMPARE(obj.data(), static_cast<QObject*>(nullptr)); |
3665 | return; |
3666 | } |
3667 | |
3668 | QVERIFY(obj); |
3669 | MyUncreateableBaseClass *base = qobject_cast<MyUncreateableBaseClass*>(object: obj.data()); |
3670 | QVERIFY(base); |
3671 | QCOMPARE(base->property(prop.toLatin1()).toBool(), true); |
3672 | } |
3673 | |
3674 | void tst_qqmllanguage::subclassedExtendedUncreateableRevision_data() |
3675 | { |
3676 | QTest::addColumn<QString>(name: "version"); |
3677 | QTest::addColumn<QString>(name: "prop"); |
3678 | QTest::addColumn<bool>(name: "shouldWork"); |
3679 | |
3680 | QTest::newRow(dataTag: "prop1 exists in 1.0") << "1.0"<< "prop1"<< true; |
3681 | QTest::newRow(dataTag: "prop2 does not exist in 1.0") << "1.0"<< "prop2"<< false; |
3682 | QTest::newRow(dataTag: "prop3 does not exist in 1.0") << "1.0"<< "prop3"<< false; |
3683 | QTest::newRow(dataTag: "prop4 exists in 1.0") << "1.0"<< "prop4"<< true; |
3684 | QTest::newRow(dataTag: "prop5 exists in 1.0") << "1.0"<< "prop5"<< true; |
3685 | |
3686 | QTest::newRow(dataTag: "prop1 exists in 1.1") << "1.1"<< "prop1"<< true; |
3687 | QTest::newRow(dataTag: "prop2 exists in 1.1") << "1.1"<< "prop2"<< true; |
3688 | QTest::newRow(dataTag: "prop3 exists in 1.1") << "1.1"<< "prop3"<< true; |
3689 | QTest::newRow(dataTag: "prop4 exists in 1.1") << "1.1"<< "prop4"<< true; |
3690 | QTest::newRow(dataTag: "prop5 exists in 1.1") << "1.1"<< "prop5"<< true; |
3691 | } |
3692 | |
3693 | void tst_qqmllanguage::subclassedExtendedUncreateableRevision() |
3694 | { |
3695 | QFETCH(QString, version); |
3696 | QFETCH(QString, prop); |
3697 | QFETCH(bool, shouldWork); |
3698 | |
3699 | { |
3700 | QQmlEngine engine; |
3701 | QString qml = QString("import QtQuick 2.0\nimport Test %1\nMyExtendedUncreateableBaseClass {}").arg(a: version); |
3702 | QQmlComponent c(&engine); |
3703 | QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready"); |
3704 | c.setData(qml.toUtf8(), baseUrl: QUrl::fromLocalFile(localfile: QDir::currentPath())); |
3705 | QScopedPointer<QObject> obj(c.create()); |
3706 | QCOMPARE(obj.data(), static_cast<QObject*>(nullptr)); |
3707 | QCOMPARE(c.errors().count(), 1); |
3708 | QCOMPARE(c.errors().first().description(), QString("Cannot create MyExtendedUncreateableBaseClass")); |
3709 | } |
3710 | |
3711 | QQmlEngine engine; |
3712 | QString qml = QString("import QtQuick 2.0\nimport Test %1\nMyExtendedCreateableDerivedClass {\n%3: true\n}").arg(a: version).arg(a: prop); |
3713 | QQmlComponent c(&engine); |
3714 | if (!shouldWork) |
3715 | QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready"); |
3716 | c.setData(qml.toUtf8(), baseUrl: QUrl::fromLocalFile(localfile: QDir::currentPath())); |
3717 | QScopedPointer<QObject> obj(c.create()); |
3718 | if (!shouldWork) { |
3719 | QCOMPARE(obj.data(), static_cast<QObject*>(nullptr)); |
3720 | return; |
3721 | } |
3722 | |
3723 | QVERIFY(obj); |
3724 | MyExtendedUncreateableBaseClass *base = qobject_cast<MyExtendedUncreateableBaseClass*>(object: obj.data()); |
3725 | QVERIFY(base); |
3726 | QCOMPARE(base->property(prop.toLatin1()).toBool(), true); |
3727 | } |
3728 | |
3729 | void tst_qqmllanguage::uncreatableTypesAsProperties() |
3730 | { |
3731 | QQmlEngine engine; |
3732 | QQmlComponent component(&engine, testFileUrl(fileName: "uncreatableTypeAsProperty.qml")); |
3733 | QScopedPointer<QObject> object(component.create()); |
3734 | QVERIFY(!object.isNull()); |
3735 | } |
3736 | |
3737 | void tst_qqmllanguage::initTestCase() |
3738 | { |
3739 | QQmlDataTest::initTestCase(); |
3740 | if (dataDirectoryUrl().scheme() == QLatin1String("qrc")) |
3741 | engine.addImportPath(dir: dataDirectory()); |
3742 | else |
3743 | QVERIFY2(QDir::setCurrent(dataDirectory()), qPrintable("Could not chdir to "+ dataDirectory())); |
3744 | |
3745 | |
3746 | defaultImportPathList = engine.importPathList(); |
3747 | |
3748 | QQmlMetaType::registerCustomStringConverter(qMetaTypeId<MyCustomVariantType>(), myCustomVariantTypeConverter); |
3749 | |
3750 | registerTypes(); |
3751 | // Registered here because it uses testFileUrl |
3752 | qmlRegisterType(url: testFileUrl(fileName: "CompositeType.qml"), uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "RegisteredCompositeType"); |
3753 | qmlRegisterType(url: testFileUrl(fileName: "CompositeType.DoesNotExist.qml"), uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "RegisteredCompositeType2"); |
3754 | qmlRegisterType(url: testFileUrl(fileName: "invalidRoot.1.qml"), uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "RegisteredCompositeType3"); |
3755 | qmlRegisterType(url: testFileUrl(fileName: "CompositeTypeWithEnum.qml"), uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "RegisteredCompositeTypeWithEnum"); |
3756 | qmlRegisterType(url: testFileUrl(fileName: "CompositeTypeWithAttachedProperty.qml"), uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "RegisteredCompositeTypeWithAttachedProperty"); |
3757 | |
3758 | // Registering the TestType class in other modules should have no adverse effects |
3759 | qmlRegisterType<TestType>(uri: "org.qtproject.TestPre", versionMajor: 1, versionMinor: 0, qmlName: "Test"); |
3760 | |
3761 | qmlRegisterType<TestType>(uri: "org.qtproject.Test", versionMajor: 0, versionMinor: 0, qmlName: "TestTP"); |
3762 | qmlRegisterType<TestType>(uri: "org.qtproject.Test", versionMajor: 1, versionMinor: 0, qmlName: "Test"); |
3763 | qmlRegisterType<TestType>(uri: "org.qtproject.Test", versionMajor: 1, versionMinor: 5, qmlName: "Test"); |
3764 | qmlRegisterType<TestType2>(uri: "org.qtproject.Test", versionMajor: 1, versionMinor: 8, qmlName: "Test"); |
3765 | qmlRegisterType<TestType>(uri: "org.qtproject.Test", versionMajor: 1, versionMinor: 9, qmlName: "OldTest"); |
3766 | qmlRegisterType<TestType2>(uri: "org.qtproject.Test", versionMajor: 1, versionMinor: 12, qmlName: "Test"); |
3767 | |
3768 | // Registering the TestType class in other modules should have no adverse effects |
3769 | qmlRegisterType<TestType>(uri: "org.qtproject.TestPost", versionMajor: 1, versionMinor: 0, qmlName: "Test"); |
3770 | |
3771 | // Create locale-specific file |
3772 | // For POSIX, this will just be data/I18nType.qml, since POSIX is 7-bit |
3773 | // For iso8859-1 locale, this will just be data/I18nType?????.qml where ????? is 5 8-bit characters |
3774 | // For utf-8 locale, this will be data/I18nType??????????.qml where ?????????? is 5 8-bit characters, UTF-8 encoded |
3775 | if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) { |
3776 | QFile in(testFileUrl(fileName: QLatin1String("I18nType30.qml")).toLocalFile()); |
3777 | QVERIFY2(in.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(in.fileName(), in.errorString()))); |
3778 | QFile out(testFileUrl(fileName: QString::fromUtf8(str: "I18nType\303\201\303\242\303\243\303\244\303\245.qml")).toLocalFile()); |
3779 | QVERIFY2(out.open(QIODevice::WriteOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(out.fileName(), out.errorString()))); |
3780 | out.write(data: in.readAll()); |
3781 | } |
3782 | |
3783 | // Register a Composite Singleton. |
3784 | qmlRegisterSingletonType(url: testFileUrl(fileName: "singleton/RegisteredCompositeSingletonType.qml"), uri: "org.qtproject.Test", versionMajor: 1, versionMinor: 0, qmlName: "RegisteredSingleton"); |
3785 | } |
3786 | |
3787 | void tst_qqmllanguage::aliasPropertyChangeSignals() |
3788 | { |
3789 | { |
3790 | QQmlComponent component(&engine, testFileUrl(fileName: "aliasPropertyChangeSignals.qml")); |
3791 | |
3792 | VERIFY_ERRORS(0); |
3793 | QScopedPointer<QObject> o(component.create()); |
3794 | QVERIFY(o != nullptr); |
3795 | |
3796 | QCOMPARE(o->property("test").toBool(), true); |
3797 | } |
3798 | |
3799 | // QTCREATORBUG-2769 |
3800 | { |
3801 | QQmlComponent component(&engine, testFileUrl(fileName: "aliasPropertyChangeSignals.2.qml")); |
3802 | |
3803 | VERIFY_ERRORS(0); |
3804 | QScopedPointer<QObject> o(component.create()); |
3805 | QVERIFY(o != nullptr); |
3806 | |
3807 | QCOMPARE(o->property("test").toBool(), true); |
3808 | } |
3809 | } |
3810 | |
3811 | // Tests property initializers |
3812 | void tst_qqmllanguage::propertyInit() |
3813 | { |
3814 | { |
3815 | QQmlComponent component(&engine, testFileUrl(fileName: "propertyInit.1.qml")); |
3816 | |
3817 | VERIFY_ERRORS(0); |
3818 | QScopedPointer<QObject> o(component.create()); |
3819 | QVERIFY(o != nullptr); |
3820 | |
3821 | QCOMPARE(o->property("test").toInt(), 1); |
3822 | } |
3823 | |
3824 | { |
3825 | QQmlComponent component(&engine, testFileUrl(fileName: "propertyInit.2.qml")); |
3826 | |
3827 | VERIFY_ERRORS(0); |
3828 | QScopedPointer<QObject> o(component.create()); |
3829 | QVERIFY(o != nullptr); |
3830 | |
3831 | QCOMPARE(o->property("test").toInt(), 123); |
3832 | } |
3833 | } |
3834 | |
3835 | // Test that registration order doesn't break type availability |
3836 | // QTBUG-16878 |
3837 | void tst_qqmllanguage::registrationOrder() |
3838 | { |
3839 | QQmlComponent component(&engine, testFileUrl(fileName: "registrationOrder.qml")); |
3840 | |
3841 | QScopedPointer<QObject> o(component.create()); |
3842 | QVERIFY(o != nullptr); |
3843 | QCOMPARE(o->metaObject(), &MyVersion2Class::staticMetaObject); |
3844 | } |
3845 | |
3846 | void tst_qqmllanguage::readonly() |
3847 | { |
3848 | QQmlComponent component(&engine, testFileUrl(fileName: "readonly.qml")); |
3849 | |
3850 | QScopedPointer<QObject> o(component.create()); |
3851 | QVERIFY(o != nullptr); |
3852 | |
3853 | QCOMPARE(o->property("test1").toInt(), 10); |
3854 | QCOMPARE(o->property("test2").toInt(), 18); |
3855 | QCOMPARE(o->property("test3").toInt(), 13); |
3856 | |
3857 | o->setProperty(name: "testData", value: 13); |
3858 | |
3859 | QCOMPARE(o->property("test1").toInt(), 10); |
3860 | QCOMPARE(o->property("test2").toInt(), 22); |
3861 | QCOMPARE(o->property("test3").toInt(), 13); |
3862 | |
3863 | o->setProperty(name: "testData2", value: 2); |
3864 | |
3865 | QCOMPARE(o->property("test1").toInt(), 10); |
3866 | QCOMPARE(o->property("test2").toInt(), 22); |
3867 | QCOMPARE(o->property("test3").toInt(), 2); |
3868 | |
3869 | o->setProperty(name: "test1", value: 11); |
3870 | o->setProperty(name: "test2", value: 11); |
3871 | o->setProperty(name: "test3", value: 11); |
3872 | |
3873 | QCOMPARE(o->property("test1").toInt(), 10); |
3874 | QCOMPARE(o->property("test2").toInt(), 22); |
3875 | QCOMPARE(o->property("test3").toInt(), 2); |
3876 | } |
3877 | |
3878 | void tst_qqmllanguage::readonlyObjectProperties() |
3879 | { |
3880 | QQmlComponent component(&engine, testFileUrl(fileName: "readonlyObjectProperty.qml")); |
3881 | |
3882 | QScopedPointer<QObject> o(component.create()); |
3883 | QVERIFY(!o.isNull()); |
3884 | |
3885 | QQmlProperty prop(o.data(), QStringLiteral("subObject"), &engine); |
3886 | QVERIFY(!prop.isWritable()); |
3887 | QVERIFY(!prop.write(QVariant::fromValue(o.data()))); |
3888 | |
3889 | QObject *subObject = qvariant_cast<QObject*>(v: prop.read()); |
3890 | QVERIFY(subObject); |
3891 | QCOMPARE(subObject->property("readWrite").toInt(), int(42)); |
3892 | subObject->setProperty(name: "readWrite", value: QVariant::fromValue(value: int(100))); |
3893 | QCOMPARE(subObject->property("readWrite").toInt(), int(100)); |
3894 | } |
3895 | |
3896 | void tst_qqmllanguage::receivers() |
3897 | { |
3898 | QQmlComponent component(&engine, testFileUrl(fileName: "receivers.qml")); |
3899 | |
3900 | QScopedPointer<MyReceiversTestObject> o(qobject_cast<MyReceiversTestObject*>(object: component.create())); |
3901 | QVERIFY(o != nullptr); |
3902 | QCOMPARE(o->mySignalCount(), 1); |
3903 | QCOMPARE(o->propChangedCount(), 2); |
3904 | QCOMPARE(o->myUnconnectedSignalCount(), 0); |
3905 | |
3906 | QVERIFY(o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::mySignal))); |
3907 | QVERIFY(o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::propChanged))); |
3908 | QVERIFY(!o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::myUnconnectedSignal))); |
3909 | } |
3910 | |
3911 | void tst_qqmllanguage::registeredCompositeType() |
3912 | { |
3913 | QQmlComponent component(&engine, testFileUrl(fileName: "registeredCompositeType.qml")); |
3914 | |
3915 | VERIFY_ERRORS(0); |
3916 | QScopedPointer<QObject> o(component.create()); |
3917 | QVERIFY(o != nullptr); |
3918 | } |
3919 | |
3920 | // QTBUG-43582 |
3921 | void tst_qqmllanguage::registeredCompositeTypeWithEnum() |
3922 | { |
3923 | QQmlComponent component(&engine, testFileUrl(fileName: "registeredCompositeTypeWithEnum.qml")); |
3924 | |
3925 | VERIFY_ERRORS(0); |
3926 | QScopedPointer<QObject> o(component.create()); |
3927 | QVERIFY(o != nullptr); |
3928 | |
3929 | QCOMPARE(o->property("enumValue0").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue0)); |
3930 | QCOMPARE(o->property("enumValue42").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue42)); |
3931 | QCOMPARE(o->property("enumValue15").toInt(), static_cast<int>(MyCompositeBaseType::ScopedCompositeEnum::EnumValue15)); |
3932 | } |
3933 | |
3934 | // QTBUG-43581 |
3935 | void tst_qqmllanguage::registeredCompositeTypeWithAttachedProperty() |
3936 | { |
3937 | QQmlComponent component(&engine, testFileUrl(fileName: "registeredCompositeTypeWithAttachedProperty.qml")); |
3938 | |
3939 | VERIFY_ERRORS(0); |
3940 | QScopedPointer<QObject> o(component.create()); |
3941 | QVERIFY(o != nullptr); |
3942 | |
3943 | QCOMPARE(o->property("attachedProperty").toString(), QStringLiteral( "test")); |
3944 | } |
3945 | |
3946 | // QTBUG-18268 |
3947 | void tst_qqmllanguage::remoteLoadCrash() |
3948 | { |
3949 | ThreadedTestHTTPServer server(dataDirectory()); |
3950 | |
3951 | QQmlComponent component(&engine); |
3952 | component.setData("import QtQuick 2.0; Text {}", baseUrl: server.url(documentPath: "/remoteLoadCrash.qml")); |
3953 | while (component.isLoading()) |
3954 | QCoreApplication::processEvents( flags: QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, maxtime: 50); |
3955 | |
3956 | QScopedPointer<QObject> o(component.create()); |
3957 | } |
3958 | |
3959 | void tst_qqmllanguage::signalWithDefaultArg() |
3960 | { |
3961 | QQmlComponent component(&engine, testFileUrl(fileName: "signalWithDefaultArg.qml")); |
3962 | |
3963 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create())); |
3964 | QVERIFY(object != nullptr); |
3965 | |
3966 | QCOMPARE(object->property("signalCount").toInt(), 0); |
3967 | QCOMPARE(object->property("signalArg").toInt(), 0); |
3968 | |
3969 | emit object->signalWithDefaultArg(); |
3970 | QCOMPARE(object-> property("signalCount").toInt(), 1); |
3971 | QCOMPARE(object->property("signalArg").toInt(), 5); |
3972 | |
3973 | emit object->signalWithDefaultArg(parameter: 15); |
3974 | QCOMPARE(object->property("signalCount").toInt(), 2); |
3975 | QCOMPARE(object->property("signalArg").toInt(), 15); |
3976 | |
3977 | |
3978 | QMetaObject::invokeMethod(obj: object.data(), member: "emitNoArgSignal"); |
3979 | QCOMPARE(object->property("signalCount").toInt(), 3); |
3980 | QCOMPARE(object->property("signalArg").toInt(), 5); |
3981 | |
3982 | QMetaObject::invokeMethod(obj: object.data(), member: "emitArgSignal"); |
3983 | QCOMPARE(object->property("signalCount").toInt(), 4); |
3984 | QCOMPARE(object->property("signalArg").toInt(), 22); |
3985 | } |
3986 | |
3987 | void tst_qqmllanguage::signalParameterTypes() |
3988 | { |
3989 | // bound signal handlers |
3990 | { |
3991 | QQmlComponent component(&engine, testFileUrl(fileName: "signalParameterTypes.1.qml")); |
3992 | QScopedPointer<QObject> obj(component.create()); |
3993 | QVERIFY(obj != nullptr); |
3994 | QVERIFY(obj->property("success").toBool()); |
3995 | } |
3996 | |
3997 | // dynamic signal connections |
3998 | { |
3999 | QQmlComponent component(&engine, testFileUrl(fileName: "signalParameterTypes.2.qml")); |
4000 | QScopedPointer<QObject> obj(component.create()); |
4001 | QVERIFY(obj != nullptr); |
4002 | QVERIFY(obj->property("success").toBool()); |
4003 | } |
4004 | |
4005 | // dynamic signal connections |
4006 | { |
4007 | QQmlComponent component(&engine, testFileUrl(fileName: "signalParameterTypes.3.qml")); |
4008 | QScopedPointer<QObject> obj(component.create()); |
4009 | QVERIFY(obj != nullptr); |
4010 | QVERIFY(obj->property("success").toBool()); |
4011 | } |
4012 | } |
4013 | |
4014 | void tst_qqmllanguage::functionParameterTypes() |
4015 | { |
4016 | QQmlComponent component(&engine, testFileUrl(fileName: "functionParameterTypes.qml")); |
4017 | QScopedPointer<QObject> obj(component.create()); |
4018 | QVERIFY2(!obj.isNull(), qPrintable(component.errorString())); |
4019 | const QMetaObject *metaObject = obj->metaObject(); |
4020 | |
4021 | { |
4022 | QMetaMethod slot = metaObject->method(index: metaObject->indexOfSlot(slot: "returnItem()")); |
4023 | QVERIFY(slot.isValid()); |
4024 | QCOMPARE(slot.returnType(), QMetaType::type("QObject*")); |
4025 | QObject *returnedPtr = nullptr; |
4026 | slot.invoke(object: obj.data(), connectionType: Qt::DirectConnection, Q_RETURN_ARG(QObject*, returnedPtr)); |
4027 | QCOMPARE(returnedPtr, obj.data()); |
4028 | } |
4029 | |
4030 | { |
4031 | QMetaMethod slot = metaObject->method(index: metaObject->indexOfSlot(slot: "takeString(QString)")); |
4032 | QVERIFY(slot.isValid()); |
4033 | QCOMPARE(slot.parameterCount(), 1); |
4034 | QCOMPARE(slot.parameterType(0), int(QMetaType::QString)); |
4035 | } |
4036 | } |
4037 | |
4038 | // QTBUG-20639 |
4039 | void tst_qqmllanguage::globalEnums() |
4040 | { |
4041 | qRegisterMetaType<MyEnum1Class::EnumA>(); |
4042 | qRegisterMetaType<MyEnum2Class::EnumB>(); |
4043 | qRegisterMetaType<Qt::TextFormat>(); |
4044 | |
4045 | QQmlComponent component(&engine, testFileUrl(fileName: "globalEnums.qml")); |
4046 | |
4047 | QScopedPointer<QObject> o(component.create()); |
4048 | QVERIFY(o != nullptr); |
4049 | |
4050 | MyEnum1Class *enum1Class = o->findChild<MyEnum1Class *>(aName: QString::fromLatin1(str: "enum1Class")); |
4051 | QVERIFY(enum1Class != nullptr); |
4052 | QVERIFY(enum1Class->getValue() == -1); |
4053 | |
4054 | MyEnumDerivedClass *enum2Class = o->findChild<MyEnumDerivedClass *>(aName: QString::fromLatin1(str: "enumDerivedClass")); |
4055 | QVERIFY(enum2Class != nullptr); |
4056 | QVERIFY(enum2Class->getValueA() == -1); |
4057 | QVERIFY(enum2Class->getValueB() == -1); |
4058 | QVERIFY(enum2Class->getValueC() == 0); |
4059 | QVERIFY(enum2Class->getValueD() == 0); |
4060 | QVERIFY(enum2Class->getValueE() == -1); |
4061 | QVERIFY(enum2Class->getValueE2() == -1); |
4062 | |
4063 | QVERIFY(enum2Class->property("aValue") == 0); |
4064 | QVERIFY(enum2Class->property("bValue") == 0); |
4065 | QVERIFY(enum2Class->property("cValue") == 0); |
4066 | QVERIFY(enum2Class->property("dValue") == 0); |
4067 | QVERIFY(enum2Class->property("eValue") == 0); |
4068 | QVERIFY(enum2Class->property("e2Value") == 0); |
4069 | |
4070 | QSignalSpy signalA(enum2Class, SIGNAL(valueAChanged(MyEnum1Class::EnumA))); |
4071 | QSignalSpy signalB(enum2Class, SIGNAL(valueBChanged(MyEnum2Class::EnumB))); |
4072 | |
4073 | QMetaObject::invokeMethod(obj: o.data(), member: "setEnumValues"); |
4074 | |
4075 | QVERIFY(enum1Class->getValue() == MyEnum1Class::A_13); |
4076 | QVERIFY(enum2Class->getValueA() == MyEnum1Class::A_11); |
4077 | QVERIFY(enum2Class->getValueB() == MyEnum2Class::B_37); |
4078 | QVERIFY(enum2Class->getValueC() == Qt::RichText); |
4079 | QVERIFY(enum2Class->getValueD() == Qt::ElideMiddle); |
4080 | QVERIFY(enum2Class->getValueE() == MyEnum2Class::E_14); |
4081 | QVERIFY(enum2Class->getValueE2() == MyEnum2Class::E_76); |
4082 | |
4083 | QVERIFY(signalA.count() == 1); |
4084 | QVERIFY(signalB.count() == 1); |
4085 | |
4086 | QVERIFY(enum2Class->property("aValue") == MyEnum1Class::A_11); |
4087 | QVERIFY(enum2Class->property("bValue") == 37); |
4088 | QVERIFY(enum2Class->property("cValue") == 1); |
4089 | QVERIFY(enum2Class->property("dValue") == 2); |
4090 | QVERIFY(enum2Class->property("eValue") == 14); |
4091 | QVERIFY(enum2Class->property("e2Value") == 76); |
4092 | } |
4093 | |
4094 | void tst_qqmllanguage::lowercaseEnumRuntime_data() |
4095 | { |
4096 | QTest::addColumn<QString>(name: "file"); |
4097 | QTest::addColumn<QString>(name: "errorMessage"); |
4098 | |
4099 | QTest::newRow(dataTag: "enum from normal type") << "lowercaseEnumRuntime.1.qml"<< ":8: TypeError: Cannot access enum value 'lowercaseEnumVal' of 'MyTypeObject', enum values need to start with an uppercase letter."; |
4100 | QTest::newRow(dataTag: "enum from singleton type") << "lowercaseEnumRuntime.2.qml"<< ":8: TypeError: Cannot access enum value 'lowercaseEnumVal' of 'MyTypeObjectSingleton', enum values need to start with an uppercase letter."; |
4101 | } |
4102 | |
4103 | void tst_qqmllanguage::lowercaseEnumRuntime() |
4104 | { |
4105 | QFETCH(QString, file); |
4106 | QFETCH(QString, errorMessage); |
4107 | |
4108 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
4109 | VERIFY_ERRORS(0); |
4110 | QString warning = component.url().toString() + errorMessage; |
4111 | QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning)); |
4112 | delete component.create(); |
4113 | } |
4114 | |
4115 | void tst_qqmllanguage::lowercaseEnumCompileTime_data() |
4116 | { |
4117 | QTest::addColumn<QString>(name: "file"); |
4118 | QTest::addColumn<QString>(name: "errorFile"); |
4119 | |
4120 | QTest::newRow(dataTag: "assignment to int property") << "lowercaseEnumCompileTime.1.qml"<< "lowercaseEnumCompileTime.1.errors.txt"; |
4121 | QTest::newRow(dataTag: "assignment to enum property") << "lowercaseEnumCompileTime.2.qml"<< "lowercaseEnumCompileTime.2.errors.txt"; |
4122 | } |
4123 | |
4124 | void tst_qqmllanguage::lowercaseEnumCompileTime() |
4125 | { |
4126 | QFETCH(QString, file); |
4127 | QFETCH(QString, errorFile); |
4128 | |
4129 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
4130 | VERIFY_ERRORS(qPrintable(errorFile)); |
4131 | } |
4132 | |
4133 | void tst_qqmllanguage::scopedEnum() |
4134 | { |
4135 | QQmlComponent component(&engine, testFileUrl(fileName: "scopedEnum.qml")); |
4136 | |
4137 | QScopedPointer<MyTypeObject> o(qobject_cast<MyTypeObject *>(object: component.create())); |
4138 | QVERIFY(o != nullptr); |
4139 | |
4140 | QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal1); |
4141 | QCOMPARE(o->intProperty(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); |
4142 | QCOMPARE(o->property("listValue").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal3); |
4143 | QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal1); |
4144 | |
4145 | QMetaObject::invokeMethod(obj: o.data(), member: "assignNewValue"); |
4146 | QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal2); |
4147 | QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); |
4148 | } |
4149 | |
4150 | void tst_qqmllanguage::scopedEnumsWithNameClash() |
4151 | { |
4152 | auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithNameClash>(uri: "ScopedEnumsWithNameClashTest", versionMajor: 1, versionMinor: 0, qmlName: "ScopedEnum", reason: "Dummy reason"); |
4153 | auto registryGuard = qScopeGuard(f: [typeId]() { |
4154 | QQmlMetaType::unregisterType(type: typeId); |
4155 | }); |
4156 | |
4157 | QQmlEngine engine; |
4158 | QQmlComponent component(&engine, testFileUrl(fileName: "scopedEnumsWithNameClash.qml")); |
4159 | |
4160 | QTest::ignoreMessage(type: QtMsgType::QtWarningMsg, message: "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal1"); |
4161 | QTest::ignoreMessage(type: QtMsgType::QtWarningMsg, message: "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal2"); |
4162 | QTest::ignoreMessage(type: QtMsgType::QtWarningMsg, message: "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal3"); |
4163 | |
4164 | QScopedPointer<QObject> obj(component.create()); |
4165 | QVERIFY(obj != nullptr); |
4166 | QVERIFY(obj->property("success").toBool()); |
4167 | } |
4168 | |
4169 | void tst_qqmllanguage::scopedEnumsWithResolvedNameClash() |
4170 | { |
4171 | auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithResolvedNameClash>(uri: "ScopedEnumsWithResolvedNameClashTest", versionMajor: 1, versionMinor: 0, qmlName: "ScopedEnum", reason: "Dummy reason"); |
4172 | auto registryGuard = qScopeGuard(f: [typeId]() { |
4173 | QQmlMetaType::unregisterType(type: typeId); |
4174 | }); |
4175 | |
4176 | QQmlEngine engine; |
4177 | QQmlComponent component(&engine, testFileUrl(fileName: "scopedEnumsWithResolvedNameClash.qml")); |
4178 | |
4179 | QScopedPointer<QObject> obj(component.create()); |
4180 | QVERIFY(obj != nullptr); |
4181 | QVERIFY(obj->property("success").toBool()); |
4182 | } |
4183 | |
4184 | void tst_qqmllanguage::qmlEnums() |
4185 | { |
4186 | QQmlEngine engine; |
4187 | engine.setImportPathList(QStringList(defaultImportPathList) << testFile(fileName: "lib")); |
4188 | |
4189 | { |
4190 | QQmlComponent component(&engine, testFileUrl(fileName: "TypeWithEnum.qml")); |
4191 | QScopedPointer<QObject> o(component.create()); |
4192 | QVERIFY(o); |
4193 | QCOMPARE(o->property("enumValue").toInt(), 1); |
4194 | QCOMPARE(o->property("enumValue2").toInt(), 2); |
4195 | QCOMPARE(o->property("scopedEnumValue").toInt(), 1); |
4196 | |
4197 | QCOMPARE(o->property("otherEnumValue1").toInt(), 24); |
4198 | QCOMPARE(o->property("otherEnumValue2").toInt(), 25); |
4199 | QCOMPARE(o->property("otherEnumValue3").toInt(), 24); |
4200 | QCOMPARE(o->property("otherEnumValue4").toInt(), 25); |
4201 | QCOMPARE(o->property("otherEnumValue5").toInt(), 1); |
4202 | } |
4203 | |
4204 | { |
4205 | QQmlComponent component(&engine, testFileUrl(fileName: "usingTypeWithEnum.qml")); |
4206 | QScopedPointer<QObject> o(component.create()); |
4207 | QVERIFY(o); |
4208 | QCOMPARE(o->property("enumValue").toInt(), 1); |
4209 | QCOMPARE(o->property("enumValue2").toInt(), 0); |
4210 | QCOMPARE(o->property("scopedEnumValue").toInt(), 2); |
4211 | QCOMPARE(o->property("enumValueFromSingleton").toInt(), 42); |
4212 | // while this next test verifies current duplication behavior, I'm not sure it should be codified |
4213 | QCOMPARE(o->property("duplicatedEnumValueFromSingleton").toInt(), 2); |
4214 | QCOMPARE(o->property("scopedEnumValueFromSingleton1").toInt(), 43); |
4215 | QCOMPARE(o->property("scopedEnumValueFromSingleton2").toInt(), 2); |
4216 | QCOMPARE(o->property("scopedEnumValueFromSingleton3").toInt(), 2); |
4217 | } |
4218 | } |
4219 | |
4220 | void tst_qqmllanguage::literals_data() |
4221 | { |
4222 | QTest::addColumn<QString>(name: "property"); |
4223 | QTest::addColumn<QVariant>(name: "value"); |
4224 | |
4225 | QTest::newRow(dataTag: "hex") << "n1"<< QVariant(0xfe32); |
4226 | // Octal integer literals are deprecated |
4227 | // QTest::newRow("octal") << "n2" << QVariant(015); |
4228 | QTest::newRow(dataTag: "fp1") << "n3"<< QVariant(-4.2E11); |
4229 | QTest::newRow(dataTag: "fp2") << "n4"<< QVariant(.1e9); |
4230 | QTest::newRow(dataTag: "fp3") << "n5"<< QVariant(3e-12); |
4231 | QTest::newRow(dataTag: "fp4") << "n6"<< QVariant(3e+12); |
4232 | QTest::newRow(dataTag: "fp5") << "n7"<< QVariant(0.1e9); |
4233 | QTest::newRow(dataTag: "large-int1") << "n8"<< QVariant((double) 1152921504606846976); |
4234 | QTest::newRow(dataTag: "large-int2") << "n9"<< QVariant(100000000000000000000.); |
4235 | |
4236 | QTest::newRow(dataTag: "special1") << "c1"<< QVariant(QString( "\b")); |
4237 | QTest::newRow(dataTag: "special2") << "c2"<< QVariant(QString( "\f")); |
4238 | QTest::newRow(dataTag: "special3") << "c3"<< QVariant(QString( "\n")); |
4239 | QTest::newRow(dataTag: "special4") << "c4"<< QVariant(QString( "\r")); |
4240 | QTest::newRow(dataTag: "special5") << "c5"<< QVariant(QString( "\t")); |
4241 | QTest::newRow(dataTag: "special6") << "c6"<< QVariant(QString( "\v")); |
4242 | QTest::newRow(dataTag: "special7") << "c7"<< QVariant(QString( "\'")); |
4243 | QTest::newRow(dataTag: "special8") << "c8"<< QVariant(QString( "\"")); |
4244 | QTest::newRow(dataTag: "special9") << "c9"<< QVariant(QString( "\\")); |
4245 | // We don't handle octal escape sequences |
4246 | QTest::newRow(dataTag: "special10") << "c10"<< QVariant(QString(1, QChar(0xa9))); |
4247 | QTest::newRow(dataTag: "special11") << "c11"<< QVariant(QString(1, QChar(0x00A9))); |
4248 | } |
4249 | |
4250 | void tst_qqmllanguage::literals() |
4251 | { |
4252 | QFETCH(QString, property); |
4253 | QFETCH(QVariant, value); |
4254 | |
4255 | QQmlComponent component(&engine, testFile(fileName: "literals.qml")); |
4256 | |
4257 | QScopedPointer<QObject> object(component.create()); |
4258 | QVERIFY(object != nullptr); |
4259 | QCOMPARE(object->property(property.toLatin1()), value); |
4260 | } |
4261 | |
4262 | void tst_qqmllanguage::objectDeletionNotify_data() |
4263 | { |
4264 | QTest::addColumn<QString>(name: "file"); |
4265 | |
4266 | QTest::newRow(dataTag: "property QtObject") << "objectDeletionNotify.1.qml"; |
4267 | QTest::newRow(dataTag: "property variant") << "objectDeletionNotify.2.qml"; |
4268 | QTest::newRow(dataTag: "property var") << "objectDeletionNotify.3.qml"; |
4269 | QTest::newRow(dataTag: "property var guard removed") << "objectDeletionNotify.4.qml"; |
4270 | } |
4271 | |
4272 | void tst_qqmllanguage::objectDeletionNotify() |
4273 | { |
4274 | QFETCH(QString, file); |
4275 | |
4276 | QQmlComponent component(&engine, testFile(fileName: file)); |
4277 | |
4278 | QScopedPointer<QObject> object(component.create()); |
4279 | QVERIFY(object != nullptr); |
4280 | QCOMPARE(object->property("success").toBool(), true); |
4281 | |
4282 | QMetaObject::invokeMethod(obj: object.data(), member: "destroyObject"); |
4283 | |
4284 | // Process the deletion event |
4285 | QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); |
4286 | QCoreApplication::processEvents(); |
4287 | |
4288 | QCOMPARE(object->property("success").toBool(), true); |
4289 | } |
4290 | |
4291 | void tst_qqmllanguage::scopedProperties() |
4292 | { |
4293 | QQmlComponent component(&engine, testFileUrl(fileName: "scopedProperties.qml")); |
4294 | |
4295 | QScopedPointer<QObject> o(component.create()); |
4296 | QVERIFY(o != nullptr); |
4297 | QVERIFY(o->property("success").toBool()); |
4298 | } |
4299 | |
4300 | void tst_qqmllanguage::deepProperty() |
4301 | { |
4302 | QQmlComponent component(&engine, testFileUrl(fileName: "deepProperty.qml")); |
4303 | QScopedPointer<QObject> o(component.create()); |
4304 | QVERIFY(o != nullptr); |
4305 | QFont font = qvariant_cast<QFont>(v: qvariant_cast<QObject*>(v: o->property(name: "someObject"))->property(name: "font")); |
4306 | QCOMPARE(font.family(), QStringLiteral("test")); |
4307 | } |
4308 | |
4309 | // Tests that the implicit import has lowest precedence, in the case where |
4310 | // there are conflicting types and types only found in the local import. |
4311 | // Tests that just check one (or the root) type are in ::importsOrder |
4312 | void tst_qqmllanguage::implicitImportsLast() |
4313 | { |
4314 | if (qmlCheckTypes()) |
4315 | QSKIP("This test is about maintaining the same choice when type is ambiguous."); |
4316 | |
4317 | if (engine.importPathList() == defaultImportPathList) |
4318 | engine.addImportPath(dir: testFile(fileName: "lib")); |
4319 | |
4320 | QQmlComponent component(&engine, testFileUrl(fileName: "localOrderTest.qml")); |
4321 | VERIFY_ERRORS(0); |
4322 | QScopedPointer<QObject> object(component.create()); |
4323 | QVERIFY(object != nullptr); |
4324 | QVERIFY(QString(object->metaObject()->superClass()->superClass()->className()) |
4325 | .startsWith(QLatin1String("QQuickMouseArea"))); |
4326 | QObject* object2 = object->property(name: "item").value<QObject*>(); |
4327 | QVERIFY(object2 != nullptr); |
4328 | QCOMPARE(QString(object2->metaObject()->superClass()->className()), |
4329 | QLatin1String("QQuickRectangle")); |
4330 | |
4331 | engine.setImportPathList(defaultImportPathList); |
4332 | } |
4333 | |
4334 | void tst_qqmllanguage::getSingletonInstance(QQmlEngine& engine, const char* fileName, const char* propertyName, QObject** result /* out */) |
4335 | { |
4336 | QVERIFY(fileName != nullptr); |
4337 | QVERIFY(propertyName != nullptr); |
4338 | |
4339 | if (!fileName || !propertyName) |
4340 | return; |
4341 | |
4342 | QQmlComponent component(&engine, testFileUrl(fileName)); |
4343 | VERIFY_ERRORS(0); |
4344 | QScopedPointer<QObject> object(component.create()); |
4345 | QVERIFY(object != nullptr); |
4346 | |
4347 | getSingletonInstance(o: object.data(), propertyName, result); |
4348 | } |
4349 | |
4350 | void tst_qqmllanguage::getSingletonInstance(QObject* o, const char* propertyName, QObject** result /* out */) |
4351 | { |
4352 | QVERIFY(o != nullptr); |
4353 | QVERIFY(propertyName != nullptr); |
4354 | |
4355 | if (!o || !propertyName) |
4356 | return; |
4357 | |
4358 | QVariant variant = o->property(name: propertyName); |
4359 | QVERIFY(variant.isValid()); |
4360 | |
4361 | QObject *singleton = nullptr; |
4362 | if (variant.userType() == qMetaTypeId<QObject *>()) |
4363 | singleton = variant.value<QObject*>(); |
4364 | else if (variant.userType() == qMetaTypeId<QJSValue>()) |
4365 | singleton = variant.value<QJSValue>().toQObject(); |
4366 | |
4367 | QVERIFY(singleton != nullptr); |
4368 | *result = singleton; |
4369 | } |
4370 | |
4371 | void verifyCompositeSingletonPropertyValues(QObject* o, const char* n1, int v1, const char* n2, int v2) |
4372 | { |
4373 | QCOMPARE(o->property(n1).userType(), (int)QMetaType::Int); |
4374 | QCOMPARE(o->property(n1), QVariant(v1)); |
4375 | |
4376 | QCOMPARE(o->property(n2).userType(), (int)QVariant::String); |
4377 | QString numStr; |
4378 | QCOMPARE(o->property(n2), QVariant(QString(QLatin1String("Test value: ")).append(numStr.setNum(v2)))); |
4379 | } |
4380 | |
4381 | // Reads values from a composite singleton type |
4382 | void tst_qqmllanguage::compositeSingletonProperties() |
4383 | { |
4384 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest1.qml")); |
4385 | VERIFY_ERRORS(0); |
4386 | QScopedPointer<QObject> o(component.create()); |
4387 | QVERIFY(o != nullptr); |
4388 | |
4389 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 125, n2: "value2", v2: -55); |
4390 | } |
4391 | |
4392 | // Checks that the addresses of the composite singletons used in the same |
4393 | // engine are the same. |
4394 | void tst_qqmllanguage::compositeSingletonSameEngine() |
4395 | { |
4396 | QObject* s1 = nullptr; |
4397 | getSingletonInstance(engine, fileName: "singletonTest2.qml", propertyName: "singleton1", result: &s1); |
4398 | QVERIFY(s1 != nullptr); |
4399 | s1->setProperty(name: "testProp2", value: QVariant(13)); |
4400 | |
4401 | QObject* s2 = nullptr; |
4402 | getSingletonInstance(engine, fileName: "singletonTest3.qml", propertyName: "singleton2", result: &s2); |
4403 | QVERIFY(s2 != nullptr); |
4404 | QCOMPARE(s2->property("testProp2"), QVariant(13)); |
4405 | |
4406 | QCOMPARE(s1, s2); |
4407 | } |
4408 | |
4409 | // Checks that the addresses of the composite singletons used in different |
4410 | // engines are different. |
4411 | void tst_qqmllanguage::compositeSingletonDifferentEngine() |
4412 | { |
4413 | QQmlEngine e2; |
4414 | |
4415 | QObject* s1 = nullptr; |
4416 | getSingletonInstance(engine, fileName: "singletonTest2.qml", propertyName: "singleton1", result: &s1); |
4417 | QVERIFY(s1 != nullptr); |
4418 | s1->setProperty(name: "testProp2", value: QVariant(13)); |
4419 | |
4420 | QObject* s2 = nullptr; |
4421 | getSingletonInstance(engine&: e2, fileName: "singletonTest3.qml", propertyName: "singleton2", result: &s2); |
4422 | QVERIFY(s2 != nullptr); |
4423 | QCOMPARE(s2->property("testProp2"), QVariant(25)); |
4424 | |
4425 | QVERIFY(s1 != s2); |
4426 | } |
4427 | |
4428 | // pragma Singleton in a non-type qml file fails |
4429 | void tst_qqmllanguage::compositeSingletonNonTypeError() |
4430 | { |
4431 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest4.qml")); |
4432 | VERIFY_ERRORS("singletonTest4.error.txt"); |
4433 | } |
4434 | |
4435 | // Loads the singleton using a namespace qualifier |
4436 | void tst_qqmllanguage::compositeSingletonQualifiedNamespace() |
4437 | { |
4438 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest5.qml")); |
4439 | VERIFY_ERRORS(0); |
4440 | QScopedPointer<QObject> o(component.create()); |
4441 | QVERIFY(o != nullptr); |
4442 | |
4443 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 125, n2: "value2", v2: -55); |
4444 | |
4445 | // lets verify that the singleton instance we are using is the same |
4446 | // when loaded through another file (without namespace!) |
4447 | QObject *s1 = nullptr; |
4448 | getSingletonInstance(o: o.data(), propertyName: "singletonInstance", result: &s1); |
4449 | QVERIFY(s1 != nullptr); |
4450 | |
4451 | QObject* s2 = nullptr; |
4452 | getSingletonInstance(engine, fileName: "singletonTest5a.qml", propertyName: "singletonInstance", result: &s2); |
4453 | QVERIFY(s2 != nullptr); |
4454 | |
4455 | QCOMPARE(s1, s2); |
4456 | } |
4457 | |
4458 | // Loads a singleton from a module |
4459 | void tst_qqmllanguage::compositeSingletonModule() |
4460 | { |
4461 | engine.addImportPath(dir: testFile(fileName: "singleton/module")); |
4462 | |
4463 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest6.qml")); |
4464 | VERIFY_ERRORS(0); |
4465 | QScopedPointer<QObject> o(component.create()); |
4466 | QVERIFY(o != nullptr); |
4467 | |
4468 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 125, n2: "value2", v2: -55); |
4469 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value3", v1: 125, n2: "value4", v2: -55); |
4470 | |
4471 | // lets verify that the singleton instance we are using is the same |
4472 | // when loaded through another file |
4473 | QObject *s1 = nullptr; |
4474 | getSingletonInstance(o: o.data(), propertyName: "singletonInstance", result: &s1); |
4475 | QVERIFY(s1 != nullptr); |
4476 | |
4477 | QObject* s2 = nullptr; |
4478 | getSingletonInstance(engine, fileName: "singletonTest6a.qml", propertyName: "singletonInstance", result: &s2); |
4479 | QVERIFY(s2 != nullptr); |
4480 | |
4481 | QCOMPARE(s1, s2); |
4482 | } |
4483 | |
4484 | // Loads a singleton from a module with a higher version |
4485 | void tst_qqmllanguage::compositeSingletonModuleVersioned() |
4486 | { |
4487 | engine.addImportPath(dir: testFile(fileName: "singleton/module")); |
4488 | |
4489 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest7.qml")); |
4490 | VERIFY_ERRORS(0); |
4491 | QScopedPointer<QObject> o(component.create()); |
4492 | QVERIFY(o != nullptr); |
4493 | |
4494 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 225, n2: "value2", v2: 55); |
4495 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value3", v1: 225, n2: "value4", v2: 55); |
4496 | |
4497 | // lets verify that the singleton instance we are using is the same |
4498 | // when loaded through another file |
4499 | QObject *s1 = nullptr; |
4500 | getSingletonInstance(o: o.data(), propertyName: "singletonInstance", result: &s1); |
4501 | QVERIFY(s1 != nullptr); |
4502 | |
4503 | QObject* s2 = nullptr; |
4504 | getSingletonInstance(engine, fileName: "singletonTest7a.qml", propertyName: "singletonInstance", result: &s2); |
4505 | QVERIFY(s2 != nullptr); |
4506 | |
4507 | QCOMPARE(s1, s2); |
4508 | } |
4509 | |
4510 | // Loads a singleton from a module with a qualified namespace |
4511 | void tst_qqmllanguage::compositeSingletonModuleQualified() |
4512 | { |
4513 | engine.addImportPath(dir: testFile(fileName: "singleton/module")); |
4514 | |
4515 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest8.qml")); |
4516 | VERIFY_ERRORS(0); |
4517 | QScopedPointer<QObject> o(component.create()); |
4518 | QVERIFY(o != nullptr); |
4519 | |
4520 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 225, n2: "value2", v2: 55); |
4521 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value3", v1: 225, n2: "value4", v2: 55); |
4522 | |
4523 | // lets verify that the singleton instance we are using is the same |
4524 | // when loaded through another file |
4525 | QObject *s1 = nullptr; |
4526 | getSingletonInstance(o: o.data(), propertyName: "singletonInstance", result: &s1); |
4527 | QVERIFY(s1 != nullptr); |
4528 | |
4529 | QObject* s2 = nullptr; |
4530 | getSingletonInstance(engine, fileName: "singletonTest8a.qml", propertyName: "singletonInstance", result: &s2); |
4531 | QVERIFY(s2 != nullptr); |
4532 | |
4533 | QCOMPARE(s1, s2); |
4534 | } |
4535 | |
4536 | // Tries to instantiate a type with a pragma Singleton and fails |
4537 | void tst_qqmllanguage::compositeSingletonInstantiateError() |
4538 | { |
4539 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest9.qml")); |
4540 | VERIFY_ERRORS("singletonTest9.error.txt"); |
4541 | } |
4542 | |
4543 | // Having a composite singleton type as dynamic property type is allowed |
4544 | void tst_qqmllanguage::compositeSingletonDynamicPropertyError() |
4545 | { |
4546 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest10.qml")); |
4547 | VERIFY_ERRORS(0); |
4548 | } |
4549 | |
4550 | void tst_qqmllanguage::compositeSingletonDynamicSignalAndJavaScriptPragma() |
4551 | { |
4552 | { |
4553 | // Having a composite singleton type as dynamic signal parameter succeeds |
4554 | // (like C++ singleton) |
4555 | |
4556 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest11.qml")); |
4557 | VERIFY_ERRORS(0); |
4558 | QScopedPointer<QObject> o(component.create()); |
4559 | QVERIFY(o != nullptr); |
4560 | |
4561 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 99, n2: "value2", v2: -55); |
4562 | } |
4563 | { |
4564 | // Load a composite singleton type and a javascript file that has .pragma library |
4565 | // in it. This will make sure that the javascript .pragma does not get mixed with |
4566 | // the pragma Singleton changes. |
4567 | |
4568 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest16.qml")); |
4569 | VERIFY_ERRORS(0); |
4570 | QScopedPointer<QObject> o(component.create()); |
4571 | QVERIFY(o != nullptr); |
4572 | |
4573 | // The value1 that is read from the SingletonType was changed from 125 to 99 |
4574 | // above. As the type is a singleton and |
4575 | // the engine has not been destroyed, we just retrieve the old instance and |
4576 | // the value is still 99. |
4577 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 99, n2: "value2", v2: 333); |
4578 | } |
4579 | } |
4580 | |
4581 | // Use qmlRegisterType to register a qml composite type with pragma Singleton defined in it. |
4582 | // This will fail as qmlRegisterType will only instantiate CompositeTypes. |
4583 | void tst_qqmllanguage::compositeSingletonQmlRegisterTypeError() |
4584 | { |
4585 | qmlRegisterType(url: testFileUrl(fileName: "singleton/registeredComposite/CompositeType.qml"), |
4586 | uri: "CompositeSingletonTest", versionMajor: 1, versionMinor: 0, qmlName: "RegisteredCompositeType"); |
4587 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest12.qml")); |
4588 | VERIFY_ERRORS("singletonTest12.error.txt"); |
4589 | } |
4590 | |
4591 | // Qmldir defines a type as a singleton, but the qml file does not have a pragma Singleton. |
4592 | void tst_qqmllanguage::compositeSingletonQmldirNoPragmaError() |
4593 | { |
4594 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest13.qml")); |
4595 | VERIFY_ERRORS("singletonTest13.error.txt"); |
4596 | } |
4597 | |
4598 | // Invalid singleton definition in the qmldir file results in an error |
4599 | void tst_qqmllanguage::compositeSingletonQmlDirError() |
4600 | { |
4601 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest14.qml")); |
4602 | VERIFY_ERRORS("singletonTest14.error.txt"); |
4603 | } |
4604 | |
4605 | // Load a remote composite singleton type via qmldir that defines the type as a singleton |
4606 | void tst_qqmllanguage::compositeSingletonRemote() |
4607 | { |
4608 | ThreadedTestHTTPServer server(dataDirectory()); |
4609 | |
4610 | QFile f(testFile(fileName: "singletonTest15.qml")); |
4611 | QVERIFY(f.open(QIODevice::ReadOnly)); |
4612 | QByteArray contents = f.readAll(); |
4613 | f.close(); |
4614 | |
4615 | contents.replace(QByteArrayLiteral("{{ServerBaseUrl}}"), after: server.baseUrl().toString().toUtf8()); |
4616 | |
4617 | QQmlComponent component(&engine); |
4618 | component.setData(contents, baseUrl: testFileUrl(fileName: "singletonTest15.qml")); |
4619 | |
4620 | while (component.isLoading()) |
4621 | QCoreApplication::processEvents( flags: QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, maxtime: 50); |
4622 | |
4623 | VERIFY_ERRORS(0); |
4624 | QScopedPointer<QObject> o(component.create()); |
4625 | QVERIFY(o != nullptr); |
4626 | |
4627 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 525, n2: "value2", v2: 355); |
4628 | } |
4629 | |
4630 | // Reads values from a Singleton accessed through selectors. |
4631 | void tst_qqmllanguage::compositeSingletonSelectors() |
4632 | { |
4633 | QQmlEngine e2; |
4634 | QQmlFileSelector qmlSelector(&e2); |
4635 | qmlSelector.setExtraSelectors(QStringList() << "basicSelector"); |
4636 | QQmlComponent component(&e2, testFileUrl(fileName: "singletonTest1.qml")); |
4637 | VERIFY_ERRORS(0); |
4638 | QScopedPointer<QObject> o(component.create()); |
4639 | QVERIFY(o != nullptr); |
4640 | |
4641 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 625, n2: "value2", v2: 455); |
4642 | } |
4643 | |
4644 | // Reads values from a Singleton that was registered through the C++ API: |
4645 | // qmlRegisterSingletonType. |
4646 | void tst_qqmllanguage::compositeSingletonRegistered() |
4647 | { |
4648 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest17.qml")); |
4649 | VERIFY_ERRORS(0); |
4650 | QScopedPointer<QObject> o(component.create()); |
4651 | QVERIFY(o != nullptr); |
4652 | |
4653 | verifyCompositeSingletonPropertyValues(o: o.data(), n1: "value1", v1: 925, n2: "value2", v2: 755); |
4654 | } |
4655 | |
4656 | void tst_qqmllanguage::compositeSingletonCircular() |
4657 | { |
4658 | QQmlComponent component(&engine, testFileUrl(fileName: "circularSingleton.qml")); |
4659 | VERIFY_ERRORS(0); |
4660 | |
4661 | QQmlTestMessageHandler messageHandler; |
4662 | |
4663 | QScopedPointer<QObject> o(component.create()); |
4664 | QVERIFY(o != nullptr); |
4665 | |
4666 | // ensure we aren't hitting the recursion warning |
4667 | QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); |
4668 | |
4669 | QCOMPARE(o->property("value").toInt(), 2); |
4670 | } |
4671 | |
4672 | void tst_qqmllanguage::singletonsHaveContextAndEngine() |
4673 | { |
4674 | QObject *qmlSingleton = nullptr; |
4675 | getSingletonInstance(engine, fileName: "singletonTest18.qml", propertyName: "qmlSingleton", result: &qmlSingleton); |
4676 | QVERIFY(qmlContext(qmlSingleton)); |
4677 | QCOMPARE(qmlEngine(qmlSingleton), &engine); |
4678 | |
4679 | QObject *jsSingleton = nullptr; |
4680 | getSingletonInstance(engine, fileName: "singletonTest18.qml", propertyName: "jsSingleton", result: &jsSingleton); |
4681 | QVERIFY(qmlContext(jsSingleton)); |
4682 | QCOMPARE(qmlEngine(jsSingleton), &engine); |
4683 | |
4684 | QObject *cppSingleton = nullptr; |
4685 | getSingletonInstance(engine, fileName: "singletonTest18.qml", propertyName: "cppSingleton", result: &cppSingleton); |
4686 | QVERIFY(qmlContext(cppSingleton)); |
4687 | QCOMPARE(qmlEngine(cppSingleton), &engine); |
4688 | } |
4689 | |
4690 | void tst_qqmllanguage::customParserBindingScopes() |
4691 | { |
4692 | QQmlComponent component(&engine, testFileUrl(fileName: "customParserBindingScopes.qml")); |
4693 | VERIFY_ERRORS(0); |
4694 | QScopedPointer<QObject> o(component.create()); |
4695 | QVERIFY(!o.isNull()); |
4696 | QPointer<QObject> child = qvariant_cast<QObject*>(v: o->property(name: "child")); |
4697 | QVERIFY(!child.isNull()); |
4698 | QCOMPARE(child->property("testProperty").toInt(), 42); |
4699 | } |
4700 | |
4701 | void tst_qqmllanguage::customParserEvaluateEnum() |
4702 | { |
4703 | QQmlComponent component(&engine, testFileUrl(fileName: "customParserEvaluateEnum.qml")); |
4704 | VERIFY_ERRORS(0); |
4705 | QScopedPointer<QObject> o(component.create()); |
4706 | QVERIFY(!o.isNull()); |
4707 | } |
4708 | |
4709 | void tst_qqmllanguage::customParserProperties() |
4710 | { |
4711 | QQmlComponent component(&engine, testFileUrl(fileName: "customParserProperties.qml")); |
4712 | VERIFY_ERRORS(0); |
4713 | QScopedPointer<QObject> o(component.create()); |
4714 | QVERIFY(!o.isNull()); |
4715 | SimpleObjectWithCustomParser *testObject = qobject_cast<SimpleObjectWithCustomParser*>(object: o.data()); |
4716 | QVERIFY(testObject); |
4717 | QCOMPARE(testObject->customBindingsCount(), 0); |
4718 | QCOMPARE(testObject->intProperty(), 42); |
4719 | QCOMPARE(testObject->property("qmlString").toString(), QStringLiteral( "Hello")); |
4720 | QVERIFY(!testObject->property("someObject").isNull()); |
4721 | } |
4722 | |
4723 | void tst_qqmllanguage::customParserWithExtendedObject() |
4724 | { |
4725 | QQmlComponent component(&engine, testFileUrl(fileName: "customExtendedParserProperties.qml")); |
4726 | VERIFY_ERRORS(0); |
4727 | QScopedPointer<QObject> o(component.create()); |
4728 | QVERIFY(!o.isNull()); |
4729 | SimpleObjectWithCustomParser *testObject = qobject_cast<SimpleObjectWithCustomParser*>(object: o.data()); |
4730 | QVERIFY(testObject); |
4731 | QCOMPARE(testObject->customBindingsCount(), 0); |
4732 | QCOMPARE(testObject->intProperty(), 42); |
4733 | QCOMPARE(testObject->property("qmlString").toString(), QStringLiteral( "Hello")); |
4734 | QVERIFY(!testObject->property("someObject").isNull()); |
4735 | |
4736 | QVariant returnValue; |
4737 | QVERIFY(QMetaObject::invokeMethod(o.data(), "getExtendedProperty", Q_RETURN_ARG(QVariant, returnValue))); |
4738 | QCOMPARE(returnValue.toInt(), 1584); |
4739 | } |
4740 | |
4741 | void tst_qqmllanguage::nestedCustomParsers() |
4742 | { |
4743 | QQmlComponent component(&engine, testFileUrl(fileName: "nestedCustomParsers.qml")); |
4744 | VERIFY_ERRORS(0); |
4745 | QScopedPointer<QObject> o(component.create()); |
4746 | QVERIFY(!o.isNull()); |
4747 | SimpleObjectWithCustomParser *testObject = qobject_cast<SimpleObjectWithCustomParser*>(object: o.data()); |
4748 | QVERIFY(testObject); |
4749 | QCOMPARE(testObject->customBindingsCount(), 1); |
4750 | SimpleObjectWithCustomParser *nestedObject = qobject_cast<SimpleObjectWithCustomParser*>(object: testObject->property(name: "nested").value<QObject*>()); |
4751 | QVERIFY(nestedObject); |
4752 | QCOMPARE(nestedObject->customBindingsCount(), 1); |
4753 | } |
4754 | |
4755 | void tst_qqmllanguage::preservePropertyCacheOnGroupObjects() |
4756 | { |
4757 | QQmlComponent component(&engine, testFileUrl(fileName: "preservePropertyCacheOnGroupObjects.qml")); |
4758 | VERIFY_ERRORS(0); |
4759 | QScopedPointer<QObject> o(component.create()); |
4760 | QVERIFY(!o.isNull()); |
4761 | QObject *subObject = qvariant_cast<QObject*>(v: o->property(name: "subObject")); |
4762 | QVERIFY(subObject); |
4763 | QCOMPARE(subObject->property("value").toInt(), 42); |
4764 | |
4765 | QQmlData *ddata = QQmlData::get(object: subObject); |
4766 | QVERIFY(ddata); |
4767 | QQmlPropertyCache *subCache = ddata->propertyCache; |
4768 | QVERIFY(subCache); |
4769 | QQmlPropertyData *pd = subCache->property(QStringLiteral("newProperty"), /*object*/nullptr, /*context*/nullptr); |
4770 | QVERIFY(pd); |
4771 | QCOMPARE(pd->propType(), qMetaTypeId<int>()); |
4772 | } |
4773 | |
4774 | void tst_qqmllanguage::propertyCacheInSync() |
4775 | { |
4776 | QQmlComponent component(&engine, testFileUrl(fileName: "propertyCacheInSync.qml")); |
4777 | VERIFY_ERRORS(0); |
4778 | QScopedPointer<QObject> o(component.create()); |
4779 | QVERIFY(!o.isNull()); |
4780 | QObject *anchors = qvariant_cast<QObject*>(v: o->property(name: "anchors")); |
4781 | QVERIFY(anchors); |
4782 | QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: anchors); |
4783 | QVERIFY(vmemo); |
4784 | QQmlPropertyCache *vmemoCache = vmemo->propertyCache(); |
4785 | QVERIFY(vmemoCache); |
4786 | QQmlData *ddata = QQmlData::get(object: anchors); |
4787 | QVERIFY(ddata); |
4788 | QVERIFY(ddata->propertyCache); |
4789 | // Those always have to be in sync and correct. |
4790 | QCOMPARE(ddata->propertyCache, vmemoCache); |
4791 | QCOMPARE(anchors->property("margins").toInt(), 50); |
4792 | } |
4793 | |
4794 | void tst_qqmllanguage::rootObjectInCreationNotForSubObjects() |
4795 | { |
4796 | QQmlComponent component(&engine, testFileUrl(fileName: "rootObjectInCreationNotForSubObjects.qml")); |
4797 | VERIFY_ERRORS(0); |
4798 | QScopedPointer<QObject> o(component.create()); |
4799 | QVERIFY(!o.isNull()); |
4800 | |
4801 | // QQmlComponent should have set this back to false anyway |
4802 | QQmlData *ddata = QQmlData::get(object: o.data()); |
4803 | QVERIFY(!ddata->rootObjectInCreation); |
4804 | |
4805 | QObject *subObject = qvariant_cast<QObject*>(v: o->property(name: "subObject")); |
4806 | QVERIFY(!subObject); |
4807 | |
4808 | qmlExecuteDeferred(o.data()); |
4809 | |
4810 | subObject = qvariant_cast<QObject*>(v: o->property(name: "subObject")); |
4811 | QVERIFY(subObject); |
4812 | |
4813 | ddata = QQmlData::get(object: subObject); |
4814 | // This should never have been set in the first place as there is no |
4815 | // QQmlComponent to set it back to false. |
4816 | QVERIFY(!ddata->rootObjectInCreation); |
4817 | } |
4818 | |
4819 | // QTBUG-63036 |
4820 | void tst_qqmllanguage::lazyDeferredSubObject() |
4821 | { |
4822 | QQmlComponent component(&engine, testFileUrl(fileName: "lazyDeferredSubObject.qml")); |
4823 | VERIFY_ERRORS(0); |
4824 | QScopedPointer<QObject> object(component.create()); |
4825 | QVERIFY(!object.isNull()); |
4826 | |
4827 | QObject *subObject = qvariant_cast<QObject *>(v: object->property(name: "subObject")); |
4828 | QVERIFY(subObject); |
4829 | |
4830 | QCOMPARE(object->objectName(), QStringLiteral("custom")); |
4831 | QCOMPARE(subObject->objectName(), QStringLiteral("custom")); |
4832 | } |
4833 | |
4834 | // QTBUG-63200 |
4835 | void tst_qqmllanguage::deferredProperties() |
4836 | { |
4837 | QQmlComponent component(&engine, testFileUrl(fileName: "deferredProperties.qml")); |
4838 | VERIFY_ERRORS(0); |
4839 | QScopedPointer<QObject> object(component.create()); |
4840 | QVERIFY(!object.isNull()); |
4841 | |
4842 | QObject *innerObj = object->findChild<QObject *>(QStringLiteral("innerobj")); |
4843 | QVERIFY(!innerObj); |
4844 | |
4845 | QObject *outerObj = object->findChild<QObject *>(QStringLiteral("outerobj")); |
4846 | QVERIFY(!outerObj); |
4847 | |
4848 | QObject *groupProperty = object->property(name: "groupProperty").value<QObject *>(); |
4849 | QVERIFY(!groupProperty); |
4850 | |
4851 | QQmlListProperty<QObject> listProperty = object->property(name: "listProperty").value<QQmlListProperty<QObject>>(); |
4852 | QCOMPARE(listProperty.count(&listProperty), 0); |
4853 | |
4854 | QQmlData *qmlData = QQmlData::get(object: object.data()); |
4855 | QVERIFY(qmlData); |
4856 | |
4857 | QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml |
4858 | QCOMPARE(qmlData->deferredData.first()->bindings.count(), 3); // "innerobj", "innerlist1", "innerlist2" |
4859 | QCOMPARE(qmlData->deferredData.last()->bindings.count(), 3); // "outerobj", "outerlist1", "outerlist2" |
4860 | |
4861 | qmlExecuteDeferred(object.data()); |
4862 | |
4863 | QCOMPARE(qmlData->deferredData.count(), 0); |
4864 | |
4865 | innerObj = object->findChild<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml |
4866 | QVERIFY(innerObj); |
4867 | QCOMPARE(innerObj->property("wasCompleted"), QVariant(true)); |
4868 | |
4869 | outerObj = object->findChild<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml |
4870 | QVERIFY(outerObj); |
4871 | QCOMPARE(outerObj->property("wasCompleted"), QVariant(true)); |
4872 | |
4873 | groupProperty = object->property(name: "groupProperty").value<QObject *>(); |
4874 | QCOMPARE(groupProperty, outerObj); |
4875 | |
4876 | listProperty = object->property(name: "listProperty").value<QQmlListProperty<QObject>>(); |
4877 | QCOMPARE(listProperty.count(&listProperty), 4); |
4878 | |
4879 | QCOMPARE(listProperty.at(&listProperty, 0)->objectName(), QStringLiteral("innerlist1")); // MyDeferredListProperty.qml |
4880 | QCOMPARE(listProperty.at(&listProperty, 0)->property("wasCompleted"), QVariant(true)); |
4881 | QCOMPARE(listProperty.at(&listProperty, 1)->objectName(), QStringLiteral("innerlist2")); // MyDeferredListProperty.qml |
4882 | QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true)); |
4883 | |
4884 | QCOMPARE(listProperty.at(&listProperty, 2)->objectName(), QStringLiteral("outerlist1")); // deferredListProperty.qml |
4885 | QCOMPARE(listProperty.at(&listProperty, 2)->property("wasCompleted"), QVariant(true)); |
4886 | QCOMPARE(listProperty.at(&listProperty, 3)->objectName(), QStringLiteral("outerlist2")); // deferredListProperty.qml |
4887 | QCOMPARE(listProperty.at(&listProperty, 3)->property("wasCompleted"), QVariant(true)); |
4888 | } |
4889 | |
4890 | static void beginDeferredOnce(QQmlEnginePrivate *enginePriv, |
4891 | const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState) |
4892 | { |
4893 | QObject *object = property.object(); |
4894 | QQmlData *ddata = QQmlData::get(object); |
4895 | Q_ASSERT(!ddata->deferredData.isEmpty()); |
4896 | |
4897 | int propertyIndex = property.index(); |
4898 | |
4899 | for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) { |
4900 | QQmlData::DeferredData *deferData = *dit; |
4901 | |
4902 | auto range = deferData->bindings.equal_range(key: propertyIndex); |
4903 | if (range.first == deferData->bindings.end()) |
4904 | continue; |
4905 | |
4906 | QQmlComponentPrivate::ConstructionState *state = new QQmlComponentPrivate::ConstructionState; |
4907 | state->completePending = true; |
4908 | |
4909 | QQmlContextData *creationContext = nullptr; |
4910 | state->creator.reset(other: new QQmlObjectCreator(deferData->context->parent, deferData->compilationUnit, creationContext)); |
4911 | |
4912 | enginePriv->inProgressCreations++; |
4913 | |
4914 | typedef QMultiHash<int, const QV4::CompiledData::Binding *> QV4PropertyBindingHash; |
4915 | auto it = std::reverse_iterator<QV4PropertyBindingHash::iterator>(range.second); |
4916 | auto last = std::reverse_iterator<QV4PropertyBindingHash::iterator>(range.first); |
4917 | state->creator->beginPopulateDeferred(context: deferData->context); |
4918 | while (it != last) { |
4919 | state->creator->populateDeferredBinding(qmlProperty: property, deferredIndex: deferData->deferredIdx, binding: *it); |
4920 | ++it; |
4921 | } |
4922 | state->creator->finalizePopulateDeferred(); |
4923 | state->errors << state->creator->errors; |
4924 | |
4925 | deferredState->constructionStates += state; |
4926 | |
4927 | // Cleanup any remaining deferred bindings for this property, also in inner contexts, |
4928 | // to avoid executing them later and overriding the property that was just populated. |
4929 | while (dit != ddata->deferredData.rend()) { |
4930 | (*dit)->bindings.remove(key: propertyIndex); |
4931 | ++dit; |
4932 | } |
4933 | break; |
4934 | } |
4935 | } |
4936 | |
4937 | static void testExecuteDeferredOnce(const QQmlProperty &property) |
4938 | { |
4939 | QObject *object = property.object(); |
4940 | QQmlData *data = QQmlData::get(object); |
4941 | if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) { |
4942 | QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine); |
4943 | |
4944 | QQmlComponentPrivate::DeferredState state; |
4945 | beginDeferredOnce(enginePriv: ep, property, deferredState: &state); |
4946 | |
4947 | // Release deferred data for those compilation units that no longer have deferred bindings |
4948 | data->releaseDeferredData(); |
4949 | |
4950 | QQmlComponentPrivate::completeDeferred(enginePriv: ep, deferredState: &state); |
4951 | } |
4952 | } |
4953 | |
4954 | void tst_qqmllanguage::executeDeferredPropertiesOnce() |
4955 | { |
4956 | QQmlComponent component(&engine, testFileUrl(fileName: "deferredProperties.qml")); |
4957 | VERIFY_ERRORS(0); |
4958 | QScopedPointer<QObject> object(component.create()); |
4959 | QVERIFY(!object.isNull()); |
4960 | |
4961 | QObjectList innerObjsAtCreation = object->findChildren<QObject *>(QStringLiteral("innerobj")); |
4962 | QVERIFY(innerObjsAtCreation.isEmpty()); |
4963 | |
4964 | QObjectList outerObjsAtCreation = object->findChildren<QObject *>(QStringLiteral("outerobj")); |
4965 | QVERIFY(outerObjsAtCreation.isEmpty()); |
4966 | |
4967 | QObject *groupProperty = object->property(name: "groupProperty").value<QObject *>(); |
4968 | QVERIFY(!groupProperty); |
4969 | |
4970 | QQmlListProperty<QObject> listProperty = object->property(name: "listProperty").value<QQmlListProperty<QObject>>(); |
4971 | QCOMPARE(listProperty.count(&listProperty), 0); |
4972 | |
4973 | QQmlData *qmlData = QQmlData::get(object: object.data()); |
4974 | QVERIFY(qmlData); |
4975 | |
4976 | QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml |
4977 | QCOMPARE(qmlData->deferredData.first()->bindings.count(), 3); // "innerobj", "innerlist1", "innerlist2" |
4978 | QCOMPARE(qmlData->deferredData.last()->bindings.count(), 3); // "outerobj", "outerlist1", "outerlist2" |
4979 | |
4980 | // first execution creates the outer object |
4981 | testExecuteDeferredOnce(property: QQmlProperty(object.data(), "groupProperty")); |
4982 | |
4983 | QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml |
4984 | QCOMPARE(qmlData->deferredData.first()->bindings.count(), 2); // "innerlist1", "innerlist2" |
4985 | QCOMPARE(qmlData->deferredData.last()->bindings.count(), 2); // "outerlist1", "outerlist2" |
4986 | |
4987 | QObjectList innerObjsAfterFirstExecute = object->findChildren<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml |
4988 | QVERIFY(innerObjsAfterFirstExecute.isEmpty()); |
4989 | |
4990 | QObjectList outerObjsAfterFirstExecute = object->findChildren<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml |
4991 | QCOMPARE(outerObjsAfterFirstExecute.count(), 1); |
4992 | QCOMPARE(outerObjsAfterFirstExecute.first()->property("wasCompleted"), QVariant(true)); |
4993 | |
4994 | groupProperty = object->property(name: "groupProperty").value<QObject *>(); |
4995 | QCOMPARE(groupProperty, outerObjsAfterFirstExecute.first()); |
4996 | |
4997 | listProperty = object->property(name: "listProperty").value<QQmlListProperty<QObject>>(); |
4998 | QCOMPARE(listProperty.count(&listProperty), 0); |
4999 | |
5000 | // re-execution does nothing (to avoid overriding the property) |
5001 | testExecuteDeferredOnce(property: QQmlProperty(object.data(), "groupProperty")); |
5002 | |
5003 | QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml |
5004 | QCOMPARE(qmlData->deferredData.first()->bindings.count(), 2); // "innerlist1", "innerlist2" |
5005 | QCOMPARE(qmlData->deferredData.last()->bindings.count(), 2); // "outerlist1", "outerlist2" |
5006 | |
5007 | QObjectList innerObjsAfterSecondExecute = object->findChildren<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml |
5008 | QVERIFY(innerObjsAfterSecondExecute.isEmpty()); |
5009 | |
5010 | QObjectList outerObjsAfterSecondExecute = object->findChildren<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml |
5011 | QCOMPARE(outerObjsAfterFirstExecute, outerObjsAfterSecondExecute); |
5012 | |
5013 | groupProperty = object->property(name: "groupProperty").value<QObject *>(); |
5014 | QCOMPARE(groupProperty, outerObjsAfterFirstExecute.first()); |
5015 | |
5016 | listProperty = object->property(name: "listProperty").value<QQmlListProperty<QObject>>(); |
5017 | QCOMPARE(listProperty.count(&listProperty), 0); |
5018 | |
5019 | // execution of a list property should execute all outer list bindings |
5020 | testExecuteDeferredOnce(property: QQmlProperty(object.data(), "listProperty")); |
5021 | |
5022 | QCOMPARE(qmlData->deferredData.count(), 0); |
5023 | |
5024 | listProperty = object->property(name: "listProperty").value<QQmlListProperty<QObject>>(); |
5025 | QCOMPARE(listProperty.count(&listProperty), 2); |
5026 | |
5027 | QCOMPARE(listProperty.at(&listProperty, 0)->objectName(), QStringLiteral("outerlist1")); // deferredListProperty.qml |
5028 | QCOMPARE(listProperty.at(&listProperty, 0)->property("wasCompleted"), QVariant(true)); |
5029 | QCOMPARE(listProperty.at(&listProperty, 1)->objectName(), QStringLiteral("outerlist2")); // deferredListProperty.qml |
5030 | QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true)); |
5031 | } |
5032 | |
5033 | void tst_qqmllanguage::noChildEvents() |
5034 | { |
5035 | QQmlComponent component(&engine); |
5036 | component.setData("import QtQml 2.0; import Test 1.0; MyQmlObject { property QtObject child: QtObject {} }", baseUrl: QUrl()); |
5037 | VERIFY_ERRORS(0); |
5038 | QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create())); |
5039 | QVERIFY(object != nullptr); |
5040 | QCOMPARE(object->childAddedEventCount(), 0); |
5041 | } |
5042 | |
5043 | void tst_qqmllanguage::earlyIdObjectAccess() |
5044 | { |
5045 | QQmlComponent component(&engine, testFileUrl(fileName: "earlyIdObjectAccess.qml")); |
5046 | QScopedPointer<QObject> o(component.create()); |
5047 | QVERIFY(!o.isNull()); |
5048 | QVERIFY(o->property("success").toBool()); |
5049 | } |
5050 | |
5051 | void tst_qqmllanguage::deleteSingletons() |
5052 | { |
5053 | QPointer<QObject> singleton; |
5054 | { |
5055 | QQmlEngine tmpEngine; |
5056 | QQmlComponent component(&tmpEngine, testFileUrl(fileName: "singletonTest5.qml")); |
5057 | VERIFY_ERRORS(0); |
5058 | QScopedPointer<QObject> o(component.create()); |
5059 | QVERIFY(o != nullptr); |
5060 | QObject *s1 = nullptr; |
5061 | getSingletonInstance(o: o.data(), propertyName: "singletonInstance", result: &s1); |
5062 | QVERIFY(s1 != nullptr); |
5063 | singleton = s1; |
5064 | QVERIFY(singleton.data() != nullptr); |
5065 | } |
5066 | QVERIFY(singleton.data() == nullptr); |
5067 | } |
5068 | |
5069 | void tst_qqmllanguage::arrayBuffer_data() |
5070 | { |
5071 | QTest::addColumn<QString>(name: "file"); |
5072 | QTest::newRow(dataTag: "arraybuffer_property_get") << "arraybuffer_property_get.qml"; |
5073 | QTest::newRow(dataTag: "arraybuffer_property_set") << "arraybuffer_property_set.qml"; |
5074 | QTest::newRow(dataTag: "arraybuffer_signal_arg") << "arraybuffer_signal_arg.qml"; |
5075 | QTest::newRow(dataTag: "arraybuffer_method_arg") << "arraybuffer_method_arg.qml"; |
5076 | QTest::newRow(dataTag: "arraybuffer_method_return") << "arraybuffer_method_return.qml"; |
5077 | QTest::newRow(dataTag: "arraybuffer_method_overload") << "arraybuffer_method_overload.qml"; |
5078 | } |
5079 | |
5080 | void tst_qqmllanguage::arrayBuffer() |
5081 | { |
5082 | QFETCH(QString, file); |
5083 | QQmlComponent component(&engine, testFileUrl(fileName: file)); |
5084 | VERIFY_ERRORS(0); |
5085 | QScopedPointer<QObject> object(component.create()); |
5086 | QVERIFY(object != nullptr); |
5087 | QCOMPARE(object->property("ok").toBool(), true); |
5088 | } |
5089 | |
5090 | void tst_qqmllanguage::defaultListProperty() |
5091 | { |
5092 | QQmlComponent component(&engine, testFileUrl(fileName: "defaultListProperty.qml")); |
5093 | VERIFY_ERRORS(0); |
5094 | QScopedPointer<QObject> o(component.create()); |
5095 | } |
5096 | |
5097 | void tst_qqmllanguage::namespacedPropertyTypes() |
5098 | { |
5099 | QQmlComponent component(&engine, testFileUrl(fileName: "namespacedPropertyTypes.qml")); |
5100 | VERIFY_ERRORS(0); |
5101 | QScopedPointer<QObject> o(component.create()); |
5102 | QVERIFY(!o.isNull()); |
5103 | } |
5104 | |
5105 | void tst_qqmllanguage::qmlTypeCanBeResolvedByName_data() |
5106 | { |
5107 | QTest::addColumn<QUrl>(name: "componentUrl"); |
5108 | |
5109 | // Built-in C++ types |
5110 | QTest::newRow(dataTag: "C++ - Anonymous") << testFileUrl(fileName: "quickTypeByName_anon.qml"); |
5111 | QTest::newRow(dataTag: "C++ - Named") << testFileUrl(fileName: "quickTypeByName_named.qml"); |
5112 | |
5113 | // Composite types with a qmldir |
5114 | QTest::newRow(dataTag: "QML - Anonymous - qmldir") << testFileUrl(fileName: "compositeTypeByName_anon_qmldir.qml"); |
5115 | QTest::newRow(dataTag: "QML - Named - qmldir") << testFileUrl(fileName: "compositeTypeByName_named_qmldir.qml"); |
5116 | } |
5117 | |
5118 | void tst_qqmllanguage::qmlTypeCanBeResolvedByName() |
5119 | { |
5120 | QFETCH(QUrl, componentUrl); |
5121 | |
5122 | QQmlEngine engine; |
5123 | QQmlComponent component(&engine, componentUrl); |
5124 | VERIFY_ERRORS(0); |
5125 | QTest::ignoreMessage(type: QtMsgType::QtWarningMsg, message: "[object Object]"); // a bit crude, but it will do |
5126 | |
5127 | QScopedPointer<QObject> o(component.create()); |
5128 | QVERIFY(!o.isNull()); |
5129 | } |
5130 | |
5131 | // Tests for the QML-only extensions of instanceof. Tests for the regular JS |
5132 | // instanceof belong in tst_qqmlecmascript! |
5133 | void tst_qqmllanguage::instanceof_data() |
5134 | { |
5135 | QTest::addColumn<QUrl>(name: "documentToTestIn"); |
5136 | QTest::addColumn<QVariant>(name: "expectedValue"); |
5137 | |
5138 | // so the way this works is that the name of the test tag defines the test |
5139 | // to run. |
5140 | // |
5141 | // the expectedValue is either a boolean true or false for whether the two |
5142 | // operands are indeed an instanceof each other, or a string for the |
5143 | // expected error message. |
5144 | |
5145 | // assert that basic types don't convert to QObject |
5146 | QTest::newRow(dataTag: "1 instanceof QtObject") |
5147 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5148 | << QVariant(false); |
5149 | QTest::newRow(dataTag: "true instanceof QtObject") |
5150 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5151 | << QVariant(false); |
5152 | QTest::newRow(dataTag: "\"foobar\" instanceof QtObject") |
5153 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5154 | << QVariant(false); |
5155 | |
5156 | // assert that Managed don't either |
5157 | QTest::newRow(dataTag: "new String(\"foobar\") instanceof QtObject") |
5158 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5159 | << QVariant(false); |
5160 | QTest::newRow(dataTag: "new Object() instanceof QtObject") |
5161 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5162 | << QVariant(false); |
5163 | QTest::newRow(dataTag: "new Date() instanceof QtObject") |
5164 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5165 | << QVariant(false); |
5166 | |
5167 | // test that simple QtQml comparisons work |
5168 | QTest::newRow(dataTag: "qtobjectInstance instanceof QtObject") |
5169 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5170 | << QVariant(true); |
5171 | QTest::newRow(dataTag: "qtobjectInstance instanceof Timer") |
5172 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5173 | << QVariant(false); |
5174 | QTest::newRow(dataTag: "timerInstance instanceof QtObject") |
5175 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5176 | << QVariant(true); |
5177 | QTest::newRow(dataTag: "timerInstance instanceof Timer") |
5178 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5179 | << QVariant(true); |
5180 | QTest::newRow(dataTag: "connectionsInstance instanceof QtObject") |
5181 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5182 | << QVariant(true); |
5183 | QTest::newRow(dataTag: "connectionsInstance instanceof Timer") |
5184 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5185 | << QVariant(false); |
5186 | QTest::newRow(dataTag: "connectionsInstance instanceof Connections") |
5187 | << testFileUrl(fileName: "instanceof_qtqml.qml") |
5188 | << QVariant(true); |
5189 | |
5190 | // make sure they still work when imported with a qualifier |
5191 | QTest::newRow(dataTag: "qtobjectInstance instanceof QmlImport.QtObject") |
5192 | << testFileUrl(fileName: "instanceof_qtqml_qualified.qml") |
5193 | << QVariant(true); |
5194 | QTest::newRow(dataTag: "qtobjectInstance instanceof QmlImport.Timer") |
5195 | << testFileUrl(fileName: "instanceof_qtqml_qualified.qml") |
5196 | << QVariant(false); |
5197 | QTest::newRow(dataTag: "timerInstance instanceof QmlImport.QtObject") |
5198 | << testFileUrl(fileName: "instanceof_qtqml_qualified.qml") |
5199 | << QVariant(true); |
5200 | QTest::newRow(dataTag: "timerInstance instanceof QmlImport.Timer") |
5201 | << testFileUrl(fileName: "instanceof_qtqml_qualified.qml") |
5202 | << QVariant(true); |
5203 | QTest::newRow(dataTag: "connectionsInstance instanceof QmlImport.QtObject") |
5204 | << testFileUrl(fileName: "instanceof_qtqml_qualified.qml") |
5205 | << QVariant(true); |
5206 | QTest::newRow(dataTag: "connectionsInstance instanceof QmlImport.Timer") |
5207 | << testFileUrl(fileName: "instanceof_qtqml_qualified.qml") |
5208 | << QVariant(false); |
5209 | QTest::newRow(dataTag: "connectionsInstance instanceof QmlImport.Connections") |
5210 | << testFileUrl(fileName: "instanceof_qtqml_qualified.qml") |
5211 | << QVariant(true); |
5212 | |
5213 | // test that Quick C++ types work ok |
5214 | QTest::newRow(dataTag: "itemInstance instanceof QtObject") |
5215 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5216 | << QVariant(true); |
5217 | QTest::newRow(dataTag: "itemInstance instanceof Timer") |
5218 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5219 | << QVariant(false); |
5220 | QTest::newRow(dataTag: "itemInstance instanceof Rectangle") |
5221 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5222 | << QVariant(false); |
5223 | QTest::newRow(dataTag: "rectangleInstance instanceof Item") |
5224 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5225 | << QVariant(true); |
5226 | QTest::newRow(dataTag: "rectangleInstance instanceof Rectangle") |
5227 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5228 | << QVariant(true); |
5229 | QTest::newRow(dataTag: "rectangleInstance instanceof MouseArea") |
5230 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5231 | << QVariant(false); |
5232 | QTest::newRow(dataTag: "mouseAreaInstance instanceof Item") |
5233 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5234 | << QVariant(true); |
5235 | QTest::newRow(dataTag: "mouseAreaInstance instanceof Rectangle") |
5236 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5237 | << QVariant(false); |
5238 | QTest::newRow(dataTag: "mouseAreaInstance instanceof MouseArea") |
5239 | << testFileUrl(fileName: "instanceof_qtquick.qml") |
5240 | << QVariant(true); |
5241 | |
5242 | // test that unqualified quick composite types work ok |
5243 | QTest::newRow(dataTag: "rectangleInstance instanceof CustomRectangle") |
5244 | << testFileUrl(fileName: "instanceof_qtquick_composite.qml") |
5245 | << QVariant(false); |
5246 | QTest::newRow(dataTag: "customRectangleInstance instanceof Rectangle") |
5247 | << testFileUrl(fileName: "instanceof_qtquick_composite.qml") |
5248 | << QVariant(true); |
5249 | QTest::newRow(dataTag: "customRectangleInstance instanceof Item") |
5250 | << testFileUrl(fileName: "instanceof_qtquick_composite.qml") |
5251 | << QVariant(true); |
5252 | QTest::newRow(dataTag: "customRectangleWithPropInstance instanceof CustomRectangleWithProp") |
5253 | << testFileUrl(fileName: "instanceof_qtquick_composite.qml") |
5254 | << QVariant(true); |
5255 | QTest::newRow(dataTag: "customRectangleWithPropInstance instanceof CustomRectangle") |
5256 | << testFileUrl(fileName: "instanceof_qtquick_composite.qml") |
5257 | << QVariant(false); // ### XXX: QTBUG-58477 |
5258 | QTest::newRow(dataTag: "customRectangleWithPropInstance instanceof Rectangle") |
5259 | << testFileUrl(fileName: "instanceof_qtquick_composite.qml") |
5260 | << QVariant(true); |
5261 | QTest::newRow(dataTag: "customRectangleInstance instanceof MouseArea") |
5262 | << testFileUrl(fileName: "instanceof_qtquick_composite.qml") |
5263 | << QVariant(false); |
5264 | QTest::newRow(dataTag: "customMouseAreaInstance instanceof MouseArea") |
5265 | << testFileUrl(fileName: "instanceof_qtquick_composite.qml") |
5266 | << QVariant(true); |
5267 | |
5268 | // test that they still work when qualified |
5269 | QTest::newRow(dataTag: "rectangleInstance instanceof CustomImport.CustomRectangle") |
5270 | << testFileUrl(fileName: "instanceof_qtquick_composite_qualified.qml") |
5271 | << QVariant(false); |
5272 | QTest::newRow(dataTag: "customRectangleInstance instanceof QuickImport.Rectangle") |
5273 | << testFileUrl(fileName: "instanceof_qtquick_composite_qualified.qml") |
5274 | << QVariant(true); |
5275 | QTest::newRow(dataTag: "customRectangleInstance instanceof QuickImport.Item") |
5276 | << testFileUrl(fileName: "instanceof_qtquick_composite_qualified.qml") |
5277 | << QVariant(true); |
5278 | QTest::newRow(dataTag: "customRectangleWithPropInstance instanceof CustomImport.CustomRectangleWithProp") |
5279 | << testFileUrl(fileName: "instanceof_qtquick_composite_qualified.qml") |
5280 | << QVariant(true); |
5281 | QTest::newRow(dataTag: "customRectangleWithPropInstance instanceof CustomImport.CustomRectangle") |
5282 | << testFileUrl(fileName: "instanceof_qtquick_composite_qualified.qml") |
5283 | << QVariant(false); // ### XXX: QTBUG-58477 |
5284 | QTest::newRow(dataTag: "customRectangleWithPropInstance instanceof QuickImport.Rectangle") |
5285 | << testFileUrl(fileName: "instanceof_qtquick_composite_qualified.qml") |
5286 | << QVariant(true); |
5287 | QTest::newRow(dataTag: "customRectangleInstance instanceof QuickImport.MouseArea") |
5288 | << testFileUrl(fileName: "instanceof_qtquick_composite_qualified.qml") |
5289 | << QVariant(false); |
5290 | QTest::newRow(dataTag: "customMouseAreaInstance instanceof QuickImport.MouseArea") |
5291 | << testFileUrl(fileName: "instanceof_qtquick_composite_qualified.qml") |
5292 | << QVariant(true); |
5293 | } |
5294 | |
5295 | void tst_qqmllanguage::instanceof() |
5296 | { |
5297 | QFETCH(QUrl, documentToTestIn); |
5298 | QFETCH(QVariant, expectedValue); |
5299 | |
5300 | QQmlEngine engine; |
5301 | QQmlComponent component(&engine, documentToTestIn); |
5302 | VERIFY_ERRORS(0); |
5303 | |
5304 | QScopedPointer<QObject> o(component.create()); |
5305 | QVERIFY(o != nullptr); |
5306 | |
5307 | QQmlExpression expr(engine.contextForObject(o.data()), nullptr, QString::fromLatin1(str: QTest::currentDataTag())); |
5308 | QVariant ret = expr.evaluate(); |
5309 | |
5310 | if (expectedValue.type() == QVariant::Bool) { |
5311 | // no error expected |
5312 | QVERIFY2(!expr.hasError(), qPrintable(expr.error().description())); |
5313 | bool returnValue = ret.toBool(); |
5314 | |
5315 | if (QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomRectangle") || |
5316 | QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle")) |
5317 | QCOMPARE(returnValue, expectedValue.toBool()); |
5318 | } else { |
5319 | QVERIFY(expr.hasError()); |
5320 | QCOMPARE(expr.error().description(), expectedValue.toString()); |
5321 | } |
5322 | } |
5323 | |
5324 | void tst_qqmllanguage::concurrentLoadQmlDir() |
5325 | { |
5326 | ThreadedTestHTTPServer server(dataDirectory()); |
5327 | QString serverdir = server.urlString(documentPath: "/lib/"); |
5328 | engine.setImportPathList(QStringList(defaultImportPathList) << serverdir); |
5329 | |
5330 | QQmlComponent component(&engine, testFileUrl(fileName: "concurrentLoad_main.qml")); |
5331 | QTRY_VERIFY(component.isReady()); |
5332 | VERIFY_ERRORS(0); |
5333 | QScopedPointer<QObject> o(component.create()); |
5334 | QVERIFY(!o.isNull()); |
5335 | engine.setImportPathList(defaultImportPathList); |
5336 | } |
5337 | |
5338 | // Test that deleting an object and then accessing it doesn't crash. |
5339 | // QTBUG-44153 |
5340 | class ObjectCreator : public QObject |
5341 | { |
5342 | Q_OBJECT |
5343 | public slots: |
5344 | QObject *create() { return (new ObjectCreator); } |
5345 | void del() { delete this; } |
5346 | }; |
5347 | |
5348 | void tst_qqmllanguage::accessDeletedObject() |
5349 | { |
5350 | QQmlEngine engine; |
5351 | |
5352 | QScopedPointer<ObjectCreator> creator(new ObjectCreator); |
5353 | engine.rootContext()->setContextProperty("objectCreator", creator.get()); |
5354 | QQmlComponent component(&engine, testFileUrl(fileName: "accessDeletedObject.qml")); |
5355 | VERIFY_ERRORS(0); |
5356 | |
5357 | QScopedPointer<QObject> o(component.create()); |
5358 | QVERIFY(!o.isNull()); |
5359 | } |
5360 | |
5361 | void tst_qqmllanguage::lowercaseTypeNames() |
5362 | { |
5363 | QCOMPARE(qmlRegisterType<QObject>("Test", 1, 0, "lowerCaseTypeName"), -1); |
5364 | QCOMPARE(qmlRegisterSingletonType<QObject>("Test", 1, 0, "lowerCaseTypeName", nullptr), -1); |
5365 | } |
5366 | |
5367 | void tst_qqmllanguage::thisInQmlScope() |
5368 | { |
5369 | QQmlEngine engine; |
5370 | |
5371 | QQmlComponent component(&engine, testFileUrl(fileName: "thisInQmlScope.qml")); |
5372 | QTRY_VERIFY(component.isReady()); |
5373 | VERIFY_ERRORS(0); |
5374 | QScopedPointer<QObject> o(component.create()); |
5375 | QVERIFY(!o.isNull()); |
5376 | QCOMPARE(o->property("x"), QVariant(42)); |
5377 | QCOMPARE(o->property("y"), QVariant(42)); |
5378 | QCOMPARE(o->property("a"), QVariant(42)); |
5379 | QCOMPARE(o->property("b"), QVariant(42)); |
5380 | } |
5381 | |
5382 | void tst_qqmllanguage::valueTypeGroupPropertiesInBehavior() |
5383 | { |
5384 | QQmlEngine engine; |
5385 | QQmlComponent component(&engine, testFileUrl(fileName: "groupPropertyInPropertyValueSource.qml")); |
5386 | VERIFY_ERRORS(0); |
5387 | QScopedPointer<QObject> o(component.create()); |
5388 | QVERIFY(!o.isNull()); |
5389 | |
5390 | QObject *animation = qmlContext(o.data())->contextProperty("animation").value<QObject*>(); |
5391 | QVERIFY(animation); |
5392 | |
5393 | QCOMPARE(animation->property("easing").value<QEasingCurve>().type(), QEasingCurve::InOutQuad); |
5394 | } |
5395 | |
5396 | void tst_qqmllanguage::retrieveQmlTypeId() |
5397 | { |
5398 | // Register in reverse order to provoke wrong minor version matching. |
5399 | int id2 = qmlRegisterType<QObject>(uri: "Test", versionMajor: 2, versionMinor: 3, qmlName: "SomeTestType"); |
5400 | int id1 = qmlRegisterType<QObject>(uri: "Test", versionMajor: 2, versionMinor: 1, qmlName: "SomeTestType"); |
5401 | QCOMPARE(qmlTypeId("Test", 2, 1, "SomeTestType"), id1); |
5402 | QCOMPARE(qmlTypeId("Test", 2, 2, "SomeTestType"), id1); |
5403 | QCOMPARE(qmlTypeId("Test", 2, 3, "SomeTestType"), id2); |
5404 | |
5405 | // Error cases |
5406 | QCOMPARE(qmlTypeId("Test", 2, 0, "SomeTestType"), -1); |
5407 | QCOMPARE(qmlTypeId("Test", 2, 3, "DoesNotExist"), -1); |
5408 | QCOMPARE(qmlTypeId("DoesNotExist", 2, 3, "SomeTestType"), -1); |
5409 | |
5410 | // Must also work for other types (defined in testtpes.cpp) |
5411 | QVERIFY(qmlTypeId("Test", 1, 0, "MyExtendedUncreateableBaseClass") >= 0); |
5412 | QVERIFY(qmlTypeId("Test", 1, 0, "MyUncreateableBaseClass") >= 0); |
5413 | QVERIFY(qmlTypeId("Test", 1, 0, "MyTypeObjectSingleton") >= 0); |
5414 | } |
5415 | |
5416 | void tst_qqmllanguage::polymorphicFunctionLookup() |
5417 | { |
5418 | QQmlEngine engine; |
5419 | QQmlComponent component(&engine, testFileUrl(fileName: "polymorphicFunctionLookup.qml")); |
5420 | VERIFY_ERRORS(0); |
5421 | QScopedPointer<QObject> o(component.create()); |
5422 | QVERIFY(!o.isNull()); |
5423 | |
5424 | QVERIFY(o->property("ok").toBool()); |
5425 | } |
5426 | |
5427 | void tst_qqmllanguage::anchorsToParentInPropertyChanges() |
5428 | { |
5429 | QQmlEngine engine; |
5430 | QQmlComponent component(&engine, testFileUrl(fileName: "anchorsToParentInPropertyChagnes.qml")); |
5431 | VERIFY_ERRORS(0); |
5432 | QScopedPointer<QObject> o(component.create()); |
5433 | QVERIFY(!o.isNull()); |
5434 | QTRY_COMPARE(o->property("edgeWidth").toInt(), 200); |
5435 | } |
5436 | |
5437 | void tst_qqmllanguage::typeWrapperToVariant() |
5438 | { |
5439 | QQmlEngine engine; |
5440 | QQmlComponent component(&engine, testFileUrl(fileName: "typeWrapperToVariant.qml")); |
5441 | VERIFY_ERRORS(0); |
5442 | QScopedPointer<QObject> o(component.create()); |
5443 | QVERIFY(!o.isNull()); |
5444 | QObject *connections = qvariant_cast<QObject *>(v: o->property(name: "connections")); |
5445 | QVERIFY(connections); |
5446 | QObject *target = qvariant_cast<QObject *>(v: connections->property(name: "target")); |
5447 | QVERIFY(target); |
5448 | } |
5449 | |
5450 | void tst_qqmllanguage::extendedForeignTypes() |
5451 | { |
5452 | QQmlEngine engine; |
5453 | QQmlComponent component(&engine, testFileUrl(fileName: "foreignExtended.qml")); |
5454 | VERIFY_ERRORS(0); |
5455 | QScopedPointer<QObject> o(component.create()); |
5456 | QVERIFY(!o.isNull()); |
5457 | |
5458 | QCOMPARE(o->property("extendedBase").toInt(), 43); |
5459 | QCOMPARE(o->property("extendedExtension").toInt(), 42); |
5460 | QCOMPARE(o->property("foreignExtendedExtension").toInt(), 42); |
5461 | QCOMPARE(o->property("foreignObjectName").toString(), QLatin1String( "foreign")); |
5462 | QCOMPARE(o->property("foreignExtendedObjectName").toString(), QLatin1String( "foreignExtended")); |
5463 | } |
5464 | |
5465 | void tst_qqmllanguage::selfReference() |
5466 | { |
5467 | QQmlEngine engine; |
5468 | QQmlComponent component(&engine, testFileUrl(fileName: "SelfReference.qml")); |
5469 | VERIFY_ERRORS(0); |
5470 | QScopedPointer<QObject> o(component.create()); |
5471 | QVERIFY(!o.isNull()); |
5472 | |
5473 | QQmlComponentPrivate *componentPrivate = QQmlComponentPrivate::get(c: &component); |
5474 | auto compilationUnit = componentPrivate->compilationUnit; |
5475 | QVERIFY(compilationUnit); |
5476 | |
5477 | const QMetaObject *metaObject = o->metaObject(); |
5478 | QMetaProperty selfProperty = metaObject->property(index: metaObject->indexOfProperty(name: "self")); |
5479 | QCOMPARE(selfProperty.userType(), compilationUnit->metaTypeId); |
5480 | |
5481 | QByteArray typeName = selfProperty.typeName(); |
5482 | QVERIFY(typeName.endsWith('*')); |
5483 | typeName = typeName.chopped(len: 1); |
5484 | QCOMPARE(typeName, metaObject->className()); |
5485 | |
5486 | QMetaMethod selfFunction = metaObject->method(index: metaObject->indexOfMethod(method: "returnSelf()")); |
5487 | QVERIFY(selfFunction.isValid()); |
5488 | QCOMPARE(selfFunction.returnType(), compilationUnit->metaTypeId); |
5489 | |
5490 | QMetaMethod selfSignal; |
5491 | |
5492 | for (int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) { |
5493 | QMetaMethod method = metaObject->method(index: i); |
5494 | if (method.isValid() && method.name().startsWith(c: "blah")) { |
5495 | selfSignal = method; |
5496 | break; |
5497 | } |
5498 | } |
5499 | |
5500 | QVERIFY(selfSignal.isValid()); |
5501 | QCOMPARE(selfSignal.parameterCount(), 1); |
5502 | QCOMPARE(selfSignal.parameterType(0), compilationUnit->metaTypeId); |
5503 | } |
5504 | |
5505 | void tst_qqmllanguage::selfReferencingSingleton() |
5506 | { |
5507 | QQmlEngine engine; |
5508 | engine.addImportPath(dir: dataDirectory()); |
5509 | |
5510 | QPointer<QObject> singletonPointer; |
5511 | { |
5512 | QQmlComponent component(&engine); |
5513 | component.setData(QByteArray(R"(import QtQml 2.0 |
5514 | import selfreferencingsingletonmodule 1.0 |
5515 | QtObject { |
5516 | property SelfReferencingSingleton singletonPointer: SelfReferencingSingleton |
5517 | })"), baseUrl: QUrl()); |
5518 | VERIFY_ERRORS(0); |
5519 | QScopedPointer<QObject> o(component.create()); |
5520 | QVERIFY(!o.isNull()); |
5521 | singletonPointer = o->property(name: "singletonPointer").value<QObject*>(); |
5522 | } |
5523 | |
5524 | QVERIFY(!singletonPointer.isNull()); |
5525 | QCOMPARE(singletonPointer->property("dummy").toInt(), 42); |
5526 | } |
5527 | |
5528 | void tst_qqmllanguage::listContainingDeletedObject() |
5529 | { |
5530 | QQmlEngine engine; |
5531 | auto url = testFileUrl(fileName: "listContainingDeleted.qml"); |
5532 | const QString message = url.toString() + ":24: TypeError: Cannot read property 'enabled' of null"; |
5533 | QTest::ignoreMessage(type: QtMsgType::QtWarningMsg, message: message.toUtf8().data()); |
5534 | QQmlComponent comp(&engine, url); |
5535 | QScopedPointer<QObject> root(comp.create()); |
5536 | QVERIFY(root); |
5537 | |
5538 | auto cmp = root->property(name: "a").value<QQmlComponent*>(); |
5539 | auto o = cmp->create(); |
5540 | |
5541 | QMetaObject::invokeMethod(obj: root.get(), member: "doAssign", Q_ARG(QVariant, QVariant::fromValue(o))); |
5542 | delete o; |
5543 | QMetaObject::invokeMethod(obj: root.get(), member: "use"); |
5544 | |
5545 | } |
5546 | |
5547 | void tst_qqmllanguage::overrideSingleton() |
5548 | { |
5549 | auto check = [](const QString &name, const QByteArray &singletonElement) { |
5550 | const QByteArray testQml = "import Test 1.0\n" |
5551 | "import QtQml 2.0\n" |
5552 | "QtObject { objectName: "+ singletonElement + ".objectName }"; |
5553 | QQmlEngine engine; |
5554 | QQmlComponent component(&engine, nullptr); |
5555 | component.setData(testQml, baseUrl: QUrl("singleton.qml")); |
5556 | QVERIFY(component.isReady()); |
5557 | QScopedPointer<QObject> obj(component.create()); |
5558 | QCOMPARE(obj->objectName(), name); |
5559 | }; |
5560 | |
5561 | check("statically registered", "BareSingleton"); |
5562 | |
5563 | BareSingleton singleton; |
5564 | singleton.setObjectName("dynamically registered"); |
5565 | qmlRegisterSingletonInstance(uri: "Test", versionMajor: 1, versionMinor: 0, typeName: "BareSingleton", cppObject: &singleton); |
5566 | |
5567 | check("dynamically registered", "BareSingleton"); |
5568 | |
5569 | QTest::ignoreMessage( |
5570 | type: QtWarningMsg, |
5571 | message: "singleton.qml:3: TypeError: Cannot read property 'objectName' of undefined"); |
5572 | check("", "UncreatableSingleton"); |
5573 | |
5574 | qmlRegisterSingletonInstance(uri: "Test", versionMajor: 1, versionMinor: 0, typeName: "UncreatableSingleton", |
5575 | cppObject: UncreatableSingleton::instance()); |
5576 | check("uncreatable", "UncreatableSingleton"); |
5577 | } |
5578 | |
5579 | void tst_qqmllanguage::inlineComponent() |
5580 | { |
5581 | QFETCH(QUrl, componentUrl); |
5582 | QFETCH(QColor, color); |
5583 | QFETCH(int, width); |
5584 | QFETCH(bool, checkProperties); |
5585 | QQmlEngine engine; |
5586 | QQmlComponent component(&engine, componentUrl); |
5587 | QScopedPointer<QObject> o(component.create()); |
5588 | if (component.isError()) { |
5589 | qDebug() << component.errorString(); |
5590 | } |
5591 | QVERIFY(!o.isNull()); |
5592 | if (checkProperties) { |
5593 | auto icInstance = o->findChild<QObject *>(aName: "icInstance"); |
5594 | QVERIFY(icInstance); |
5595 | QCOMPARE(icInstance->property("color").value<QColor>(),color); |
5596 | QCOMPARE(icInstance->property("width").value<qreal>(), width); |
5597 | } |
5598 | } |
5599 | |
5600 | void tst_qqmllanguage::inlineComponent_data() |
5601 | { |
5602 | QTest::addColumn<QUrl>(name: "componentUrl"); |
5603 | QTest::addColumn<QColor>(name: "color"); |
5604 | QTest::addColumn<int>(name: "width"); |
5605 | QTest::addColumn<bool>(name: "checkProperties"); |
5606 | |
5607 | QTest::newRow(dataTag: "Usage from other component") << testFileUrl(fileName: "inlineComponentUser1.qml") << QColorConstants::Blue << 24 << true; |
5608 | QTest::newRow(dataTag: "Reexport") << testFileUrl(fileName: "inlineComponentUser2.qml") << QColorConstants::Svg::green << 24 << true; |
5609 | QTest::newRow(dataTag: "Usage in same component") << testFileUrl(fileName: "inlineComponentUser3.qml") << QColorConstants::Blue << 24 << true; |
5610 | |
5611 | QTest::newRow(dataTag: "Resolution happens at instantiation") << testFileUrl(fileName: "inlineComponentUser4.qml") << QColorConstants::Blue << 24 << true; |
5612 | QTest::newRow(dataTag: "Non-toplevel IC is found") << testFileUrl(fileName: "inlineComponentUser5.qml") << QColorConstants::Svg::red << 24 << true; |
5613 | |
5614 | QTest::newRow(dataTag: "Resolved in correct order") << testFileUrl(fileName: "inlineComponentOrder.qml") << QColorConstants::Blue << 200 << true; |
5615 | |
5616 | QTest::newRow(dataTag: "ID resolves correctly") << testFileUrl(fileName: "inlineComponentWithId.qml") << QColorConstants::Svg::red << 42 << true; |
5617 | QTest::newRow(dataTag: "Alias resolves correctly") << testFileUrl(fileName: "inlineComponentWithAlias.qml") << QColorConstants::Svg::lime << 42 << true; |
5618 | |
5619 | QTest::newRow(dataTag: "Two inline components in same do not crash (QTBUG-86989)") << testFileUrl(fileName: "twoInlineComponents.qml") << QColor() << 0 << false; |
5620 | QTest::newRow(dataTag: "Inline components used in same file (QTBUG-89173)") << testFileUrl(fileName: "inlineComponentsSameFile.qml") << QColor() << 0 << false; |
5621 | } |
5622 | |
5623 | void tst_qqmllanguage::inlineComponentReferenceCycle_data() |
5624 | { |
5625 | QTest::addColumn<QUrl>(name: "componentUrl"); |
5626 | |
5627 | QTest::newRow(dataTag: "Simple cycle") << testFileUrl(fileName: "icSimpleCycle.qml"); |
5628 | QTest::newRow(dataTag: "Via property") << testFileUrl(fileName: "icCycleViaProperty.qml"); |
5629 | } |
5630 | |
5631 | void tst_qqmllanguage::inlineComponentReferenceCycle() |
5632 | { |
5633 | QFETCH(QUrl, componentUrl); |
5634 | QQmlEngine engine; |
5635 | QTest::ignoreMessage(type: QtMsgType::QtWarningMsg, message: "QQmlComponent: Component is not ready"); |
5636 | QQmlComponent component(&engine, componentUrl); |
5637 | QScopedPointer<QObject> o(component.create()); |
5638 | QVERIFY(o.isNull()); |
5639 | QVERIFY(component.isError()); |
5640 | QCOMPARE(component.errorString(), componentUrl.toString() + QLatin1String(":-1 Inline components form a cycle!\n")); |
5641 | } |
5642 | |
5643 | void tst_qqmllanguage::nestedInlineComponentNotAllowed() |
5644 | { |
5645 | QQmlEngine engine; |
5646 | auto url = testFileUrl(fileName: "nestedIC.qml"); |
5647 | QQmlComponent component(&engine, url); |
5648 | QTest::ignoreMessage(type: QtMsgType::QtWarningMsg, message: "QQmlComponent: Component is not ready"); |
5649 | QScopedPointer<QObject> o(component.create()); |
5650 | QVERIFY(component.isError()); |
5651 | QCOMPARE(component.errorString(), QLatin1String("%1:%2").arg(url.toString(), QLatin1String( "5 Nested inline components are not supported\n"))); |
5652 | } |
5653 | |
5654 | void tst_qqmllanguage::inlineComponentStaticTypeResolution() |
5655 | { |
5656 | QQmlEngine engine; |
5657 | QQmlComponent component(&engine, testFileUrl(fileName: "InlineComponentChild.qml")); |
5658 | VERIFY_ERRORS(0); |
5659 | QScopedPointer<QObject> o(component.create()); |
5660 | QVERIFY(o); |
5661 | QCOMPARE(o->property("i").toInt(), 42); |
5662 | } |
5663 | |
5664 | void tst_qqmllanguage::inlineComponentInSingleton() |
5665 | { |
5666 | QQmlEngine engine; |
5667 | QQmlComponent component(&engine, testFileUrl(fileName: "singletonICTest.qml")); |
5668 | VERIFY_ERRORS(0); |
5669 | QScopedPointer<QObject> o(component.create()); |
5670 | QVERIFY(!o.isNull()); |
5671 | auto untyped = o->property(name: "singleton1"); |
5672 | QVERIFY(untyped.isValid()); |
5673 | auto singleton1 = untyped.value<QObject*>(); |
5674 | QVERIFY(singleton1); |
5675 | QCOMPARE(singleton1->property("iProp").value<int>(), 42); |
5676 | QCOMPARE(singleton1->property("sProp").value<QString>(), QString::fromLatin1( "Hello, world")); |
5677 | QVERIFY(!o.isNull()); |
5678 | } |
5679 | |
5680 | void tst_qqmllanguage::nonExistingInlineComponent_data() |
5681 | { |
5682 | QTest::addColumn<QUrl>(name: "componentUrl"); |
5683 | QTest::addColumn<QString>(name: "errorMessage"); |
5684 | QTest::addColumn<int>(name: "line"); |
5685 | QTest::addColumn<int>(name: "column"); |
5686 | |
5687 | QTest::newRow(dataTag: "Property type") << testFileUrl(fileName: "nonExistingICUser1.qml") << QString( "Type InlineComponentProvider has no inline component type called NonExisting") << 4 << 5; |
5688 | QTest::newRow(dataTag: "Instantiation") << testFileUrl(fileName: "nonExistingICUser2.qml") << QString( "Type InlineComponentProvider has no inline component type called NotExisting") << 4 << 5; |
5689 | QTest::newRow(dataTag: "Inheritance") << testFileUrl(fileName: "nonExistingICUser3.qml") << QString( "Type InlineComponentProvider has no inline component type called NotExisting") << 3 << 1; |
5690 | QTest::newRow(dataTag: "From singleton") << testFileUrl(fileName: "nonExistingICUser4.qml") << QString( "Type MySingleton.SingletonTypeWithIC has no inline component type called NonExisting") << 5 << 5; |
5691 | |
5692 | QTest::newRow(dataTag: "Cannot access parent inline components from child") << testFileUrl(fileName: "nonExistingICUser5.qml") << QString( "Type InlineComponentProviderChild has no inline component type called StyledRectangle") << 4 << 5; |
5693 | } |
5694 | |
5695 | void tst_qqmllanguage::nonExistingInlineComponent() |
5696 | { |
5697 | QFETCH(QUrl, componentUrl); |
5698 | QFETCH(QString, errorMessage); |
5699 | QFETCH(int, line); |
5700 | QFETCH(int, column); |
5701 | QQmlEngine engine; |
5702 | QQmlComponent component(&engine, componentUrl); |
5703 | auto errors = component.errors(); |
5704 | QCOMPARE(errors.size(), 1); |
5705 | const auto &error = errors.first(); |
5706 | QCOMPARE(error.description(), errorMessage); |
5707 | QCOMPARE(error.line(), line); |
5708 | QCOMPARE(error.column(), column); |
5709 | } |
5710 | |
5711 | void tst_qqmllanguage::inlineComponentFoundBeforeOtherImports() |
5712 | { |
5713 | QQmlEngine engine; |
5714 | QUrl url = testFileUrl(fileName: "inlineComponentFoundBeforeOtherImports.qml"); |
5715 | QQmlComponent component(&engine, url); |
5716 | |
5717 | QTest::ignoreMessage(type: QtMsgType::QtInfoMsg, message: "Created"); |
5718 | QScopedPointer<QObject> root {component.create()}; |
5719 | } |
5720 | |
5721 | void tst_qqmllanguage::inlineComponentDuplicateNameError() |
5722 | { |
5723 | QQmlEngine engine; |
5724 | QUrl url = testFileUrl(fileName: "inlineComponentDuplicateName.qml"); |
5725 | QQmlComponent component(&engine, url); |
5726 | |
5727 | QString message = QLatin1String("%1:5 Inline component names must be unique per file\n").arg(args: url.toString()); |
5728 | QScopedPointer<QObject> root {component.create()}; |
5729 | QVERIFY(root.isNull()); |
5730 | QVERIFY(component.isError()); |
5731 | QCOMPARE(component.errorString(), message); |
5732 | } |
5733 | |
5734 | struct QJSValueConvertible { |
5735 | |
5736 | Q_GADGET |
5737 | |
5738 | public: |
5739 | QString msg; |
5740 | }; |
5741 | |
5742 | bool operator==(const QJSValueConvertible &lhs, const QJSValueConvertible &rhs) { |
5743 | return lhs.msg == rhs.msg; |
5744 | } |
5745 | |
5746 | class TestItem : public QObject |
5747 | { |
5748 | Q_OBJECT |
5749 | Q_PROPERTY( QVector<QPointF> positions MEMBER m_points ) |
5750 | Q_PROPERTY( QSet<QByteArray> barrays MEMBER m_barrays ) |
5751 | Q_PROPERTY( QVector<QJSValueConvertible> convertibles MEMBER m_convertibles) |
5752 | |
5753 | public: |
5754 | TestItem() = default; |
5755 | QVector< QPointF > m_points; |
5756 | QSet<QByteArray> m_barrays; |
5757 | QVector<QJSValueConvertible> m_convertibles; |
5758 | }; |
5759 | |
5760 | |
5761 | Q_DECLARE_METATYPE(QVector<QPointF>); |
5762 | Q_DECLARE_METATYPE(QSet<QByteArray>); |
5763 | Q_DECLARE_METATYPE(QJSValueConvertible); |
5764 | Q_DECLARE_METATYPE(QVector<QJSValueConvertible>); |
5765 | |
5766 | void tst_qqmllanguage::arrayToContainer() |
5767 | { |
5768 | QMetaType::registerConverter< QJSValue, QJSValueConvertible >( |
5769 | |
5770 | function: [](const QJSValue& value) |
5771 | { |
5772 | return QJSValueConvertible{.msg: value.toString()}; |
5773 | } |
5774 | ); |
5775 | QQmlEngine engine; |
5776 | qmlRegisterType<TestItem>(uri: "qt.test", versionMajor: 1, versionMinor: 0, qmlName: "TestItem"); |
5777 | QVector<QPointF> points { QPointF (2.0, 3.0) }; |
5778 | QSet<QByteArray> barrays { QByteArray("hello"), QByteArray( "world") }; |
5779 | engine.rootContext()->setContextProperty("test", QVariant::fromValue(value: points)); |
5780 | QQmlComponent component(&engine, testFileUrl(fileName: "arrayToContainer.qml")); |
5781 | VERIFY_ERRORS(0); |
5782 | QScopedPointer<TestItem> root(qobject_cast<TestItem *>(object: component.createWithInitialProperties( initialProperties: {{"vector", QVariant::fromValue(value: points)}, { "myset", QVariant::fromValue(value: barrays)} } ))); |
5783 | QVERIFY(root); |
5784 | QCOMPARE(root->m_points.at(0), QPointF (2.0, 3.0) ); |
5785 | QVERIFY(root->m_barrays.contains("hello")); |
5786 | QVERIFY(root->m_barrays.contains("world")); |
5787 | QCOMPARE(root->m_convertibles.at(0).msg, QLatin1String("hello")); |
5788 | QCOMPARE(root->m_convertibles.at(1).msg, QLatin1String("world")); |
5789 | } |
5790 | |
5791 | class EnumTester : public QObject |
5792 | { |
5793 | Q_OBJECT |
5794 | public: |
5795 | enum Types |
5796 | { |
5797 | FIRST = 0, |
5798 | SECOND, |
5799 | THIRD |
5800 | }; |
5801 | Q_ENUM(Types) |
5802 | }; |
5803 | |
5804 | void tst_qqmllanguage::qualifiedScopeInCustomParser() |
5805 | { |
5806 | qmlRegisterUncreatableType<EnumTester>(uri: "scoped.custom.test", versionMajor: 1, versionMinor: 0, qmlName: "EnumTester", |
5807 | reason: "Object only creatable in C++"); |
5808 | QQmlEngine engine; |
5809 | QQmlComponent component(&engine); |
5810 | component.setData("import QtQml.Models 2.12\n" |
5811 | "import scoped.custom.test 1.0 as BACKEND\n" |
5812 | "ListModel {\n" |
5813 | " ListElement { text: \"a\"; type: BACKEND.EnumTester.FIRST }\n" |
5814 | "}\n", baseUrl: QUrl()); |
5815 | QVERIFY(component.isReady()); |
5816 | QScopedPointer<QObject> obj(component.create()); |
5817 | QVERIFY(!obj.isNull()); |
5818 | } |
5819 | |
5820 | void tst_qqmllanguage::accessNullPointerPropertyCache() |
5821 | { |
5822 | QQmlEngine engine; |
5823 | QQmlComponent c(&engine, testFileUrl(fileName: "NullPointerPropertyCache.qml")); |
5824 | QVERIFY(c.isReady()); |
5825 | QScopedPointer<QObject> obj(c.create()); |
5826 | QVERIFY(!obj.isNull()); |
5827 | } |
5828 | |
5829 | void tst_qqmllanguage::bareInlineComponent() |
5830 | { |
5831 | QQmlEngine engine; |
5832 | |
5833 | QQmlComponent c(&engine, testFileUrl(fileName: "bareInline.qml")); |
5834 | QVERIFY2(c.isReady(), qPrintable(c.errorString())); |
5835 | QScopedPointer<QObject> o(c.create()); |
5836 | QVERIFY(!o.isNull()); |
5837 | |
5838 | QQmlMetaType::freeUnusedTypesAndCaches(); |
5839 | |
5840 | bool tab1Found = false; |
5841 | const auto types = QQmlMetaType::qmlTypes(); |
5842 | for (const QQmlType &type : types) { |
5843 | if (type.elementName() == QStringLiteral("Tab1")) { |
5844 | QVERIFY(type.module().isEmpty()); |
5845 | tab1Found = true; |
5846 | const auto ics = type.priv()->objectIdToICType; |
5847 | QVERIFY(ics.size() > 0); |
5848 | for (const QQmlType &ic : ics) |
5849 | QVERIFY(ic.containingType() == type); |
5850 | } |
5851 | } |
5852 | QVERIFY(tab1Found); |
5853 | } |
5854 | |
5855 | void tst_qqmllanguage::hangOnWarning() |
5856 | { |
5857 | QTest::ignoreMessage(type: QtWarningMsg, |
5858 | qPrintable(QStringLiteral("%1:3 : Ignored annotation") |
5859 | .arg(testFileUrl("hangOnWarning.qml").toString()))); |
5860 | QQmlComponent component(&engine, testFileUrl(fileName: "hangOnWarning.qml")); |
5861 | QScopedPointer<QObject> object(component.create()); |
5862 | QVERIFY(object != nullptr); |
5863 | } |
5864 | |
5865 | void tst_qqmllanguage::ambiguousContainingType() |
5866 | { |
5867 | // Need to do it twice, so that we load from disk cache the second time. |
5868 | for (int i = 0; i < 2; ++i) { |
5869 | QQmlEngine engine; |
5870 | |
5871 | // Should not crash when loading the type |
5872 | QQmlComponent c(&engine, testFileUrl(fileName: "ambiguousBinding/ambiguousContainingType.qml")); |
5873 | QVERIFY2(c.isReady(), qPrintable(c.errorString())); |
5874 | QScopedPointer<QObject> o(c.create()); |
5875 | QVERIFY(!o.isNull()); |
5876 | } |
5877 | } |
5878 | |
5879 | QTEST_MAIN(tst_qqmllanguage) |
5880 | |
5881 | #include "tst_qqmllanguage.moc" |
5882 |
Definitions
- qmlCheckTypes
- isCaseSensitiveFileSystem
- tst_qqmllanguage
- isJSNumberType
- cleanupTestCase
- insertedSemicolon_data
- insertedSemicolon
- errors_data
- errors
- simpleObject
- simpleContainer
- interfaceProperty
- interfaceQList
- assignObjectToSignal
- assignObjectToVariant
- assignLiteralSignalProperty
- assignQmlComponent
- assignBasicTypes
- assignTypeExtremes
- assignCompositeToType
- assignLiteralToVariant
- assignLiteralToVar
- assignLiteralToJSValue
- assignNullStrings
- bindJSValueToVar
- bindJSValueToVariant
- bindJSValueToType
- bindTypeToJSValue
- customParserTypes
- customParserTypeInInlineComponent
- rootAsQmlComponent
- rootItemIsComponent
- inlineQmlComponents
- idProperty
- autoNotifyConnection
- assignSignal
- assignSignalFunctionExpression
- overrideSignal_data
- overrideSignal
- dynamicProperties
- dynamicPropertiesNested
- listProperties
- listPropertiesInheritanceNoCrash
- badListItemType
- dynamicObjectProperties
- dynamicSignalsAndSlots
- simpleBindings
- EvaluationCounter
- increaseEvaluationCounter
- noDoubleEvaluationForFlushedBindings_data
- noDoubleEvaluationForFlushedBindings
- autoComponentCreation
- autoComponentCreationInGroupProperty
- propertyValueSource
- requiredProperty
- MyClassWithRequiredProperty
- ChildClassWithoutOwnRequired
- ChildClassWithOwnRequired
- requiredPropertyFromCpp_data
- requiredPropertyFromCpp
- attachedProperties
- dynamicObjects
- customVariantTypes
- valueTypes
- cppnamespace
- aliasProperties
- aliasPropertiesAndSignals
- qtbug_89822
- componentCompositeType
- TestType
- TestType
- TestType2
- TestType2
- i18n_data
- i18n
- onCompleted
- onDestruction
- scriptString
- scriptStringJs
- FreeUnitData
- cleanup
- scriptStringWithoutSourceCode
- scriptStringComparison
- defaultPropertyListOrder
- declaredPropertyValues
- dontDoubleCallClassBegin
- reservedWords_data
- reservedWords
- testType
- inlineAssignmentsOverrideBindings
- nestedComponentRoots
- importsBuiltin_data
- importsBuiltin
- importsLocal_data
- importsLocal
- basicRemote_data
- basicRemote
- importsRemote_data
- importsRemote
- importsInstalled_data
- importsInstalled
- importsInstalledRemote_data
- importsInstalledRemote
- importsPath_data
- importsPath
- importsOrder_data
- importsOrder
- importIncorrectCase
- importJs_data
- importJs
- importJsModule_data
- importJsModule
- explicitSelfImport
- importInternalType
- qmlAttachedPropertiesObjectMethod
- crash1
- crash2
- customOnProperty
- variantNotify
- revisions
- revisionOverloads
- subclassedUncreateableRevision_data
- subclassedUncreateableRevision
- subclassedExtendedUncreateableRevision_data
- subclassedExtendedUncreateableRevision
- uncreatableTypesAsProperties
- initTestCase
- aliasPropertyChangeSignals
- propertyInit
- registrationOrder
- readonly
- readonlyObjectProperties
- receivers
- registeredCompositeType
- registeredCompositeTypeWithEnum
- registeredCompositeTypeWithAttachedProperty
- remoteLoadCrash
- signalWithDefaultArg
- signalParameterTypes
- functionParameterTypes
- globalEnums
- lowercaseEnumRuntime_data
- lowercaseEnumRuntime
- lowercaseEnumCompileTime_data
- lowercaseEnumCompileTime
- scopedEnum
- scopedEnumsWithNameClash
- scopedEnumsWithResolvedNameClash
- qmlEnums
- literals_data
- literals
- objectDeletionNotify_data
- objectDeletionNotify
- scopedProperties
- deepProperty
- implicitImportsLast
- getSingletonInstance
- getSingletonInstance
- verifyCompositeSingletonPropertyValues
- compositeSingletonProperties
- compositeSingletonSameEngine
- compositeSingletonDifferentEngine
- compositeSingletonNonTypeError
- compositeSingletonQualifiedNamespace
- compositeSingletonModule
- compositeSingletonModuleVersioned
- compositeSingletonModuleQualified
- compositeSingletonInstantiateError
- compositeSingletonDynamicPropertyError
- compositeSingletonDynamicSignalAndJavaScriptPragma
- compositeSingletonQmlRegisterTypeError
- compositeSingletonQmldirNoPragmaError
- compositeSingletonQmlDirError
- compositeSingletonRemote
- compositeSingletonSelectors
- compositeSingletonRegistered
- compositeSingletonCircular
- singletonsHaveContextAndEngine
- customParserBindingScopes
- customParserEvaluateEnum
- customParserProperties
- customParserWithExtendedObject
- nestedCustomParsers
- preservePropertyCacheOnGroupObjects
- propertyCacheInSync
- rootObjectInCreationNotForSubObjects
- lazyDeferredSubObject
- deferredProperties
- beginDeferredOnce
- testExecuteDeferredOnce
- executeDeferredPropertiesOnce
- noChildEvents
- earlyIdObjectAccess
- deleteSingletons
- arrayBuffer_data
- arrayBuffer
- defaultListProperty
- namespacedPropertyTypes
- qmlTypeCanBeResolvedByName_data
- qmlTypeCanBeResolvedByName
- instanceof_data
- instanceof
- concurrentLoadQmlDir
- ObjectCreator
- create
- del
- accessDeletedObject
- lowercaseTypeNames
- thisInQmlScope
- valueTypeGroupPropertiesInBehavior
- retrieveQmlTypeId
- polymorphicFunctionLookup
- anchorsToParentInPropertyChanges
- typeWrapperToVariant
- extendedForeignTypes
- selfReference
- selfReferencingSingleton
- listContainingDeletedObject
- overrideSingleton
- inlineComponent
- inlineComponent_data
- inlineComponentReferenceCycle_data
- inlineComponentReferenceCycle
- nestedInlineComponentNotAllowed
- inlineComponentStaticTypeResolution
- inlineComponentInSingleton
- nonExistingInlineComponent_data
- nonExistingInlineComponent
- inlineComponentFoundBeforeOtherImports
- inlineComponentDuplicateNameError
- QJSValueConvertible
- operator==
- TestItem
- TestItem
- arrayToContainer
- EnumTester
- Types
- qualifiedScopeInCustomParser
- accessNullPointerPropertyCache
- bareInlineComponent
- hangOnWarning
Learn Advanced QML with KDAB
Find out more