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
62DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
63
64static 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/*
76This test case covers QML language issues. This covers everything that does not
77involve evaluating ECMAScript expressions and bindings.
78
79Evaluation of expressions and bindings is covered in qmlecmascript
80*/
81class tst_qqmllanguage : public QQmlDataTest
82{
83 Q_OBJECT
84
85private 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
342private:
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
406void 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
412void 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
421void 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
439void 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
676void 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
707void 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
715void 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
724void 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
734void 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
745void 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
755void 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
765void 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)
775void 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
788void 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
833void 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
844void 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
853void 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.
890void 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
936void 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
1029void 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
1042void 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
1091void 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
1140void 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
1175void 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
1315void 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
1325void 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
1335void 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
1345void 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
1359void 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
1374void 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"
1406void 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
1421void 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
1433void 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
1445void 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
1458void 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
1475void 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
1492void 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
1506void 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)
1518void 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
1526void 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
1535void 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
1557void 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
1575void 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
1588class EvaluationCounter : public QObject
1589{
1590 Q_OBJECT
1591public:
1592 int counter = 0;
1593 Q_INVOKABLE void increaseEvaluationCounter() { ++counter; }
1594};
1595
1596void 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
1603void 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
1619void 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
1643void 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
1655void 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
1700void 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
1745class MyClassWithRequiredProperty : public QObject
1746{
1747public:
1748 Q_OBJECT
1749 Q_PROPERTY(int test MEMBER m_test REQUIRED NOTIFY testChanged)
1750 Q_SIGNAL void testChanged();
1751private:
1752 int m_test;
1753};
1754
1755class ChildClassWithoutOwnRequired : public MyClassWithRequiredProperty
1756{
1757public:
1758 Q_OBJECT
1759 Q_PROPERTY(int test2 MEMBER m_test2 NOTIFY test2Changed)
1760 Q_SIGNAL void test2Changed();
1761private:
1762 int m_test2;
1763};
1764
1765class ChildClassWithOwnRequired : public MyClassWithRequiredProperty
1766{
1767public:
1768 Q_OBJECT
1769 Q_PROPERTY(int test2 MEMBER m_test2 REQUIRED NOTIFY test2Changed)
1770 Q_SIGNAL void test2Changed();
1771private:
1772 int m_test2;
1773};
1774
1775void 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
1792void 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
1814void 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
1827void 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
1836void 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
1845void 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
1880void 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
1908void 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
2229void 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
2238void 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
2245void 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
2253class TestType : public QObject {
2254 Q_OBJECT
2255public:
2256 TestType(QObject *p=nullptr) : QObject(p) {}
2257};
2258
2259class TestType2 : public QObject {
2260 Q_OBJECT
2261public:
2262 TestType2(QObject *p=nullptr) : QObject(p) {}
2263};
2264
2265void 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
2277void 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
2289void 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
2301void 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
2313void 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
2414void 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
2491struct 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
2500void 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.
2558void 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
2639void 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
2656void tst_qqmllanguage::declaredPropertyValues()
2657{
2658 QQmlComponent component(&engine, testFileUrl(fileName: "declaredPropertyValues.qml"));
2659 VERIFY_ERRORS(0);
2660}
2661
2662void 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
2674void 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
2741void 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.
2750void 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
2788void 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
2798void tst_qqmllanguage::nestedComponentRoots()
2799{
2800 QQmlComponent component(&engine, testFileUrl(fileName: "nestedComponentRoots.qml"));
2801}
2802
2803// Import tests (QT-558)
2804void 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
2918void tst_qqmllanguage::importsBuiltin()
2919{
2920 QFETCH(QString, qml);
2921 QFETCH(QString, type);
2922 QFETCH(QString, error);
2923 testType(qml,type,expectederror: error);
2924}
2925
2926void 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
2984void tst_qqmllanguage::importsLocal()
2985{
2986 QFETCH(QString, qml);
2987 QFETCH(QString, type);
2988 QFETCH(QString, error);
2989 testType(qml,type,expectederror: error);
2990}
2991
2992void 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
3005void 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
3028void 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
3052void 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
3065void 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
3131void tst_qqmllanguage::importsInstalled()
3132{
3133 QFETCH(QString, qml);
3134 QFETCH(QString, type);
3135 QFETCH(QString, error);
3136 testType(qml,type,expectederror: error);
3137}
3138
3139void tst_qqmllanguage::importsInstalledRemote_data()
3140{
3141 // Repeat the tests for local installed data
3142 importsInstalled_data();
3143}
3144
3145void 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
3161void 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
3210void 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
3236void 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
3328void 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
3337void 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
3356void 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
3418void 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
3447void 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
3461void 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
3477void 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
3487void 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
3510void 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
3538void tst_qqmllanguage::crash1()
3539{
3540 QQmlComponent component(&engine);
3541 component.setData("import QtQuick 2.0\nComponent {}", baseUrl: QUrl());
3542}
3543
3544void tst_qqmllanguage::crash2()
3545{
3546 QQmlComponent component(&engine, testFileUrl(fileName: "crash2.qml"));
3547}
3548
3549// QTBUG-8676
3550void 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
3562void 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
3573void 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
3608void 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
3622void 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
3638void 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
3674void 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
3693void 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
3729void 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
3737void 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
3787void 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
3812void 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
3837void 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
3846void 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
3878void 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
3896void 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
3911void 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
3921void 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
3935void 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
3947void 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
3959void 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
3987void 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
4014void 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
4039void 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
4094void 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
4103void 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
4115void 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
4124void 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
4133void 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
4150void 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
4169void 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
4184void 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
4220void 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
4250void 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
4262void 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
4272void 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
4291void 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
4300void 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
4312void 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
4334void 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
4350void 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
4371void 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
4382void 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.
4394void 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.
4411void 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
4429void 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
4436void 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
4459void 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
4485void 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
4511void 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
4537void 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
4544void tst_qqmllanguage::compositeSingletonDynamicPropertyError()
4545{
4546 QQmlComponent component(&engine, testFileUrl(fileName: "singletonTest10.qml"));
4547 VERIFY_ERRORS(0);
4548}
4549
4550void 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.
4583void 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.
4592void 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
4599void 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
4606void 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.
4631void 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.
4646void 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
4656void 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
4672void 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
4690void 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
4701void 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
4709void 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
4723void 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
4741void 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
4755void 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
4774void 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
4794void 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
4820void 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
4835void 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
4890static 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
4937static 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
4954void 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
5033void 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
5043void 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
5051void 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
5069void 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
5080void 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
5090void tst_qqmllanguage::defaultListProperty()
5091{
5092 QQmlComponent component(&engine, testFileUrl(fileName: "defaultListProperty.qml"));
5093 VERIFY_ERRORS(0);
5094 QScopedPointer<QObject> o(component.create());
5095}
5096
5097void 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
5105void 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
5118void 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!
5133void 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
5295void 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
5324void 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
5340class ObjectCreator : public QObject
5341{
5342 Q_OBJECT
5343public slots:
5344 QObject *create() { return (new ObjectCreator); }
5345 void del() { delete this; }
5346};
5347
5348void 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
5361void 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
5367void 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
5382void 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
5396void 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
5416void 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
5427void 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
5437void 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
5450void 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
5465void 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
5505void 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
5528void 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
5547void 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
5579void 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
5600void 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
5623void 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
5631void 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
5643void 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
5654void 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
5664void 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
5680void 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
5695void 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
5711void 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
5721void 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
5734struct QJSValueConvertible {
5735
5736 Q_GADGET
5737
5738public:
5739 QString msg;
5740};
5741
5742bool operator==(const QJSValueConvertible &lhs, const QJSValueConvertible &rhs) {
5743 return lhs.msg == rhs.msg;
5744}
5745
5746class 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
5753public:
5754 TestItem() = default;
5755 QVector< QPointF > m_points;
5756 QSet<QByteArray> m_barrays;
5757 QVector<QJSValueConvertible> m_convertibles;
5758};
5759
5760
5761Q_DECLARE_METATYPE(QVector<QPointF>);
5762Q_DECLARE_METATYPE(QSet<QByteArray>);
5763Q_DECLARE_METATYPE(QJSValueConvertible);
5764Q_DECLARE_METATYPE(QVector<QJSValueConvertible>);
5765
5766void 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
5791class EnumTester : public QObject
5792{
5793 Q_OBJECT
5794public:
5795 enum Types
5796 {
5797 FIRST = 0,
5798 SECOND,
5799 THIRD
5800 };
5801 Q_ENUM(Types)
5802};
5803
5804void 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
5820void 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
5829void 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
5855void 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
5865void 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
5879QTEST_MAIN(tst_qqmllanguage)
5880
5881#include "tst_qqmllanguage.moc"
5882

source code of qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp