2022-05-13 13:12:05 +00:00
// Copyright (C) 2016 The Qt Company Ltd.
2024-02-22 14:51:16 +00:00
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
2011-04-27 10:05:43 +00:00
2012-02-16 04:43:03 +00:00
# include <QQmlEngine>
# include <QQmlContext>
2011-04-27 10:05:43 +00:00
# include <QNetworkAccessManager>
# include <QPointer>
# include <QDir>
2011-10-23 23:03:51 +00:00
# include <QStandardPaths>
2012-04-19 12:38:38 +00:00
# include <QSignalSpy>
2011-04-27 10:05:43 +00:00
# include <QDebug>
2013-02-28 17:13:23 +00:00
# include <QBuffer>
2017-05-08 08:24:39 +00:00
# include <QCryptographicHash>
2012-02-16 04:43:03 +00:00
# include <QQmlComponent>
# include <QQmlNetworkAccessManagerFactory>
# include <QQmlExpression>
2012-05-03 22:32:45 +00:00
# include <QQmlIncubationController>
2017-05-31 13:54:15 +00:00
# include <QTemporaryDir>
2022-12-06 12:28:48 +00:00
# include <QQmlEngineExtensionPlugin>
2012-05-03 22:32:45 +00:00
# include <private/qqmlengine_p.h>
2019-07-09 07:25:40 +00:00
# include <private/qqmltypedata_p.h>
2021-03-17 14:08:22 +00:00
# include <private/qqmlcomponentattached_p.h>
2013-12-03 16:41:04 +00:00
# include <QQmlAbstractUrlInterceptor>
2021-08-06 10:27:35 +00:00
# include <QtQuickTestUtils/private/qmlutils_p.h>
2011-04-27 10:05:43 +00:00
2022-12-06 12:28:48 +00:00
# include "declarativelyregistered.h"
Q_IMPORT_QML_PLUGIN ( OnlyDeclarativePlugin )
2012-05-03 22:32:45 +00:00
class tst_qqmlengine : public QQmlDataTest
2011-04-27 10:05:43 +00:00
{
Q_OBJECT
public :
2021-08-06 10:27:35 +00:00
tst_qqmlengine ( ) : QQmlDataTest ( QT_QMLTEST_DATADIR ) { }
2011-04-27 10:05:43 +00:00
private slots :
2017-05-31 13:54:15 +00:00
void initTestCase ( ) override ;
2011-04-27 10:05:43 +00:00
void rootContext ( ) ;
2024-04-24 11:40:49 +00:00
# if QT_CONFIG(qml_network)
2011-04-27 10:05:43 +00:00
void networkAccessManager ( ) ;
2013-02-28 17:13:23 +00:00
void synchronousNetworkAccessManager ( ) ;
2024-04-24 11:40:49 +00:00
# endif
2011-04-27 10:05:43 +00:00
void baseUrl ( ) ;
void contextForObject ( ) ;
void offlineStoragePath ( ) ;
2017-01-09 16:06:25 +00:00
void offlineDatabaseStoragePath ( ) ;
2011-04-27 10:05:43 +00:00
void clearComponentCache ( ) ;
2012-05-03 22:32:45 +00:00
void trimComponentCache ( ) ;
void trimComponentCache_data ( ) ;
2021-08-19 13:08:41 +00:00
void clearSingletons ( ) ;
2012-05-21 03:40:22 +00:00
void repeatedCompilation ( ) ;
void failedCompilation ( ) ;
void failedCompilation_data ( ) ;
2011-04-27 10:05:43 +00:00
void outputWarningsToStandardError ( ) ;
void objectOwnership ( ) ;
2011-07-25 05:47:40 +00:00
void multipleEngines ( ) ;
2012-05-23 08:05:10 +00:00
void qtqmlModule_data ( ) ;
void qtqmlModule ( ) ;
2013-03-01 01:03:43 +00:00
void urlInterceptor_data ( ) ;
void urlInterceptor ( ) ;
2015-10-27 11:21:00 +00:00
void qmlContextProperties ( ) ;
2017-04-21 10:29:22 +00:00
void testGCCorruption ( ) ;
2017-09-14 12:38:36 +00:00
void testGroupedPropertyRevisions ( ) ;
2018-03-02 08:09:17 +00:00
void componentFromEval ( ) ;
2018-04-24 09:35:43 +00:00
void qrcUrls ( ) ;
2018-05-29 12:27:46 +00:00
void cppSignalAndEval ( ) ;
2018-05-31 19:41:47 +00:00
void singletonInstance ( ) ;
2019-03-26 12:57:33 +00:00
void aggressiveGc ( ) ;
2019-05-09 10:38:22 +00:00
void cachedGetterLookup_qtbug_75335 ( ) ;
2020-01-10 09:01:36 +00:00
void createComponentOnSingletonDestruction ( ) ;
2020-01-22 12:37:48 +00:00
void uiLanguage ( ) ;
2023-04-19 12:57:13 +00:00
void markCurrentFunctionAsTranslationBinding ( ) ;
2021-02-08 14:42:59 +00:00
void executeRuntimeFunction ( ) ;
2021-02-26 08:01:25 +00:00
void captureQProperty ( ) ;
2021-03-17 14:08:22 +00:00
void listWrapperAsListReference ( ) ;
void attachedObjectAsObject ( ) ;
2021-03-23 13:16:15 +00:00
void listPropertyAsQJSValue ( ) ;
2021-09-03 12:35:26 +00:00
void stringToColor ( ) ;
2022-03-25 09:56:41 +00:00
void qobjectToString ( ) ;
2022-06-24 07:56:01 +00:00
void qtNamespaceInQtObject ( ) ;
2022-08-25 09:42:23 +00:00
void nativeModuleImport ( ) ;
Allow limited extensions to globals
We can allow
a, overriding data members of globals, such as Error.name
b, adding members that don't clash with any internals
c, any manipulation of toString(), toLocaleString(), valueOf(),
and constructor
To that effect, add a "Locked" flag to our internal classes. If that is
set, disallow changing prototypes and when defining a property, check if
it shadows any non-configurable property. Furthermore, make all
non-primitive properties that are not meant to be overridden
non-configurable and non-writable.
constructor, toString(), toLocaleString() and valueOf() are exempt
because they are explicitly meant to be overridden by users. Therefore,
we let that happen and refrain from optimizing them or triggering their
implicit invocation in optimized code.
[ChangeLog][QtQml][Important Behavior Changes] The JavaScript global
objects are not frozen anymore in a QML engine. Instead, they are
selectively locked. You can extend the objects with new members as long
as you don't shadow any existing methods, and you can change or override
data members. This also means that most methods of Object.prototype,
which was previously exempt from the freezing, cannot be changed
anymore. You can, however, change or override constructor, toString(),
toLocaleString() and valueOf() on any prototype. Those are clearly meant
to be overridden by user code.
Fixes: QTBUG-101298
Task-number: QTBUG-84341
Change-Id: Id77db971f76c8f48b18e7a93607da5f947ecfc3e
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2022-08-29 10:00:52 +00:00
void lockedRootObject ( ) ;
2023-03-22 12:01:24 +00:00
void crossReferencingSingletonsDeletion ( ) ;
2023-09-28 08:36:46 +00:00
void bindingInstallUseAfterFree ( ) ;
2024-04-08 13:28:12 +00:00
void variantListQJsonConversion ( ) ;
2015-10-27 11:21:00 +00:00
2012-04-19 12:38:38 +00:00
public slots :
QObject * createAQObjectForOwnershipTest ( )
{
static QObject * ptr = new QObject ( ) ;
return ptr ;
}
2017-05-31 13:54:15 +00:00
private :
QTemporaryDir m_tempDir ;
2011-04-27 10:05:43 +00:00
} ;
2021-08-19 13:08:41 +00:00
class ObjectCaller : public QObject
{
Q_OBJECT
signals :
void doubleReply ( const double a ) ;
} ;
class CppSingleton : public QObject {
Q_OBJECT
public :
2021-08-27 11:42:44 +00:00
static uint instantiations ;
uint id = 0 ;
CppSingleton ( ) : id ( + + instantiations ) { }
2021-08-19 13:08:41 +00:00
static QObject * create ( QQmlEngine * qmlEngine , QJSEngine * jsEngine )
{
Q_UNUSED ( qmlEngine ) ;
Q_UNUSED ( jsEngine ) ;
return new CppSingleton ( ) ;
}
} ;
2021-08-27 11:42:44 +00:00
uint CppSingleton : : instantiations = 0 ;
2021-08-19 13:08:41 +00:00
class JsSingleton : public QObject {
Q_OBJECT
public :
2021-09-22 08:00:44 +00:00
static uint instantiations ;
uint id = 0 ;
JsSingleton ( ) : id ( + + instantiations ) { }
2021-08-19 13:08:41 +00:00
static QJSValue create ( QQmlEngine * qmlEngine , QJSEngine * jsEngine )
{
Q_UNUSED ( qmlEngine ) ;
QJSValue value = jsEngine - > newQObject ( new JsSingleton ( ) ) ;
return value ;
}
} ;
2021-09-22 08:00:44 +00:00
uint JsSingleton : : instantiations = 0 ;
2017-05-31 13:54:15 +00:00
void tst_qqmlengine : : initTestCase ( )
{
QVERIFY2 ( m_tempDir . isValid ( ) , qPrintable ( m_tempDir . errorString ( ) ) ) ;
QQmlDataTest : : initTestCase ( ) ;
}
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : rootContext ( )
2011-04-27 10:05:43 +00:00
{
2012-02-16 04:43:03 +00:00
QQmlEngine engine ;
2011-04-27 10:05:43 +00:00
QVERIFY ( engine . rootContext ( ) ) ;
QCOMPARE ( engine . rootContext ( ) - > engine ( ) , & engine ) ;
2015-07-24 13:27:58 +00:00
QVERIFY ( ! engine . rootContext ( ) - > parentContext ( ) ) ;
2011-04-27 10:05:43 +00:00
}
2024-04-24 11:40:49 +00:00
# if QT_CONFIG(qml_network)
2012-02-16 04:43:03 +00:00
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
2011-04-27 10:05:43 +00:00
{
public :
2018-02-21 09:41:54 +00:00
NetworkAccessManagerFactory ( ) : manager ( nullptr ) { }
2011-04-27 10:05:43 +00:00
2021-04-11 09:12:51 +00:00
QNetworkAccessManager * create ( QObject * parent ) override {
2011-04-27 10:05:43 +00:00
manager = new QNetworkAccessManager ( parent ) ;
return manager ;
}
QNetworkAccessManager * manager ;
} ;
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : networkAccessManager ( )
2011-04-27 10:05:43 +00:00
{
2023-07-28 12:57:16 +00:00
std : : unique_ptr < QQmlEngine > engine = std : : make_unique < QQmlEngine > ( ) ;
2011-04-27 10:05:43 +00:00
2012-02-16 04:43:03 +00:00
// Test QQmlEngine created manager
2011-04-27 10:05:43 +00:00
QPointer < QNetworkAccessManager > manager = engine - > networkAccessManager ( ) ;
2018-02-21 09:41:54 +00:00
QVERIFY ( manager ! = nullptr ) ;
2011-04-27 10:05:43 +00:00
// Test factory created manager
2023-07-28 12:57:16 +00:00
engine . reset ( new QQmlEngine ) ;
2011-04-27 10:05:43 +00:00
NetworkAccessManagerFactory factory ;
engine - > setNetworkAccessManagerFactory ( & factory ) ;
2015-07-24 13:27:58 +00:00
QCOMPARE ( engine - > networkAccessManagerFactory ( ) , & factory ) ;
QNetworkAccessManager * engineNam = engine - > networkAccessManager ( ) ; // calls NetworkAccessManagerFactory::create()
QCOMPARE ( engineNam , factory . manager ) ;
2011-04-27 10:05:43 +00:00
}
2013-02-28 17:13:23 +00:00
class ImmediateReply : public QNetworkReply {
Q_OBJECT
public :
ImmediateReply ( ) {
setFinished ( true ) ;
}
2021-03-25 13:44:04 +00:00
qint64 readData ( char * , qint64 ) override {
2013-02-28 17:13:23 +00:00
return 0 ;
}
2021-03-25 13:44:04 +00:00
void abort ( ) override { }
2013-02-28 17:13:23 +00:00
} ;
class ImmediateManager : public QNetworkAccessManager {
Q_OBJECT
public :
2018-02-21 09:41:54 +00:00
ImmediateManager ( QObject * parent = nullptr ) : QNetworkAccessManager ( parent ) {
2013-02-28 17:13:23 +00:00
}
2021-04-11 09:12:51 +00:00
QNetworkReply * createRequest ( Operation , const QNetworkRequest & , QIODevice * outgoingData = nullptr ) override
{
2013-02-28 17:13:23 +00:00
Q_UNUSED ( outgoingData ) ;
return new ImmediateReply ;
}
} ;
class ImmediateFactory : public QQmlNetworkAccessManagerFactory {
public :
2021-04-11 09:12:51 +00:00
QNetworkAccessManager * create ( QObject * ) override
{ return new ImmediateManager ; }
2013-02-28 17:13:23 +00:00
} ;
void tst_qqmlengine : : synchronousNetworkAccessManager ( )
{
ImmediateFactory factory ;
QQmlEngine engine ;
engine . setNetworkAccessManagerFactory ( & factory ) ;
QQmlComponent c ( & engine , QUrl ( " myScheme://test.qml " ) ) ;
// reply is finished, so should not be in loading state.
QVERIFY ( ! c . isLoading ( ) ) ;
}
2024-04-24 11:40:49 +00:00
# endif
2013-02-28 17:13:23 +00:00
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : baseUrl ( )
2011-04-27 10:05:43 +00:00
{
2012-02-16 04:43:03 +00:00
QQmlEngine engine ;
2011-04-27 10:05:43 +00:00
QUrl cwd = QUrl : : fromLocalFile ( QDir : : currentPath ( ) + QDir : : separator ( ) ) ;
QCOMPARE ( engine . baseUrl ( ) , cwd ) ;
QCOMPARE ( engine . rootContext ( ) - > resolvedUrl ( QUrl ( " main.qml " ) ) , cwd . resolved ( QUrl ( " main.qml " ) ) ) ;
QDir dir = QDir : : current ( ) ;
dir . cdUp ( ) ;
QVERIFY ( dir ! = QDir : : current ( ) ) ;
QDir : : setCurrent ( dir . path ( ) ) ;
2015-07-24 13:27:58 +00:00
QCOMPARE ( QDir : : current ( ) , dir ) ;
2011-04-27 10:05:43 +00:00
QUrl cwd2 = QUrl : : fromLocalFile ( QDir : : currentPath ( ) + QDir : : separator ( ) ) ;
QCOMPARE ( engine . baseUrl ( ) , cwd2 ) ;
QCOMPARE ( engine . rootContext ( ) - > resolvedUrl ( QUrl ( " main.qml " ) ) , cwd2 . resolved ( QUrl ( " main.qml " ) ) ) ;
engine . setBaseUrl ( cwd ) ;
QCOMPARE ( engine . baseUrl ( ) , cwd ) ;
QCOMPARE ( engine . rootContext ( ) - > resolvedUrl ( QUrl ( " main.qml " ) ) , cwd . resolved ( QUrl ( " main.qml " ) ) ) ;
2018-02-22 23:56:31 +00:00
const QString testPath = QDir : : currentPath ( ) + QLatin1String ( " / " ) ;
const QString rootPath = QDir : : rootPath ( ) ;
engine . setBaseUrl ( QUrl ( ) ) ;
// Check that baseUrl returns a url to a localFile
QCOMPARE ( engine . baseUrl ( ) . toLocalFile ( ) , testPath ) ;
QDir : : setCurrent ( QDir : : rootPath ( ) ) ;
// Make sure this also works when in the rootPath
QCOMPARE ( engine . baseUrl ( ) . toLocalFile ( ) , rootPath ) ;
2011-04-27 10:05:43 +00:00
}
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : contextForObject ( )
2011-04-27 10:05:43 +00:00
{
2023-07-28 12:57:16 +00:00
std : : unique_ptr < QQmlEngine > engine = std : : make_unique < QQmlEngine > ( ) ;
2011-04-27 10:05:43 +00:00
// Test null-object
2018-02-21 09:41:54 +00:00
QVERIFY ( ! QQmlEngine : : contextForObject ( nullptr ) ) ;
2011-04-27 10:05:43 +00:00
// Test an object with no context
QObject object ;
2015-07-24 13:27:58 +00:00
QVERIFY ( ! QQmlEngine : : contextForObject ( & object ) ) ;
2011-04-27 10:05:43 +00:00
// Test setting null-object
2018-02-21 09:41:54 +00:00
QQmlEngine : : setContextForObject ( nullptr , engine - > rootContext ( ) ) ;
2011-04-27 10:05:43 +00:00
// Test setting null-context
2018-02-21 09:41:54 +00:00
QQmlEngine : : setContextForObject ( & object , nullptr ) ;
2011-04-27 10:05:43 +00:00
// Test setting context
2012-02-16 04:43:03 +00:00
QQmlEngine : : setContextForObject ( & object , engine - > rootContext ( ) ) ;
2015-07-24 13:27:58 +00:00
QCOMPARE ( QQmlEngine : : contextForObject ( & object ) , engine - > rootContext ( ) ) ;
2011-04-27 10:05:43 +00:00
2012-02-16 04:43:03 +00:00
QQmlContext context ( engine - > rootContext ( ) ) ;
2011-04-27 10:05:43 +00:00
// Try changing context
2012-02-16 04:43:03 +00:00
QTest : : ignoreMessage ( QtWarningMsg , " QQmlEngine::setContextForObject(): Object already has a QQmlContext " ) ;
QQmlEngine : : setContextForObject ( & object , & context ) ;
2015-07-24 13:27:58 +00:00
QCOMPARE ( QQmlEngine : : contextForObject ( & object ) , engine - > rootContext ( ) ) ;
2011-04-27 10:05:43 +00:00
// Delete context
2023-07-28 12:57:16 +00:00
engine . reset ( ) ;
2015-07-24 13:27:58 +00:00
QVERIFY ( ! QQmlEngine : : contextForObject ( & object ) ) ;
2011-04-27 10:05:43 +00:00
}
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : offlineStoragePath ( )
2011-04-27 10:05:43 +00:00
{
// Without these set, QDesktopServices::storageLocation returns
// strings with extra "//" at the end. We set them to ignore this problem.
2012-02-16 04:43:03 +00:00
qApp - > setApplicationName ( " tst_qqmlengine " ) ;
2013-07-15 12:59:07 +00:00
qApp - > setOrganizationName ( " QtProject " ) ;
qApp - > setOrganizationDomain ( " www.qt-project.org " ) ;
2011-04-27 10:05:43 +00:00
2012-02-16 04:43:03 +00:00
QQmlEngine engine ;
2011-04-27 10:05:43 +00:00
2020-10-16 13:04:23 +00:00
QString dataLocation = QStandardPaths : : writableLocation ( QStandardPaths : : AppDataLocation ) ;
2012-04-19 03:50:39 +00:00
QCOMPARE ( dataLocation . isEmpty ( ) , engine . offlineStoragePath ( ) . isEmpty ( ) ) ;
QDir dir ( dataLocation ) ;
2011-04-27 10:05:43 +00:00
dir . mkpath ( " QML " ) ;
dir . cd ( " QML " ) ;
dir . mkpath ( " OfflineStorage " ) ;
dir . cd ( " OfflineStorage " ) ;
QCOMPARE ( QDir : : fromNativeSeparators ( engine . offlineStoragePath ( ) ) , dir . path ( ) ) ;
2022-09-27 08:27:30 +00:00
QSignalSpy offlineStoragePathSpy ( & engine , & QQmlEngine : : offlineStoragePathChanged ) ;
2011-04-27 10:05:43 +00:00
engine . setOfflineStoragePath ( QDir : : homePath ( ) ) ;
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that on() was replaced with a matcher that doesn't ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Change-Id: I58e1b41b91c34d2e860dbb5847b3752edbfc6fc9
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-10-08 17:56:03 +00:00
QCOMPARE ( offlineStoragePathSpy . size ( ) , 1 ) ;
2011-04-27 10:05:43 +00:00
QCOMPARE ( engine . offlineStoragePath ( ) , QDir : : homePath ( ) ) ;
}
2017-01-09 16:06:25 +00:00
void tst_qqmlengine : : offlineDatabaseStoragePath ( )
{
// Without these set, QDesktopServices::storageLocation returns
// strings with extra "//" at the end. We set them to ignore this problem.
qApp - > setApplicationName ( " tst_qqmlengine " ) ;
qApp - > setOrganizationName ( " QtProject " ) ;
qApp - > setOrganizationDomain ( " www.qt-project.org " ) ;
QQmlEngine engine ;
2020-10-16 13:04:23 +00:00
QString dataLocation = QStandardPaths : : writableLocation ( QStandardPaths : : AppDataLocation ) ;
2017-01-09 16:06:25 +00:00
const QString databaseName = QLatin1String ( " foo " ) ;
QString databaseLocation = engine . offlineStorageDatabaseFilePath ( databaseName ) ;
QCOMPARE ( dataLocation . isEmpty ( ) , databaseLocation . isEmpty ( ) ) ;
QDir dir ( dataLocation ) ;
dir . mkpath ( " QML " ) ;
dir . cd ( " QML " ) ;
dir . mkpath ( " OfflineStorage " ) ;
dir . cd ( " OfflineStorage " ) ;
dir . mkpath ( " Databases " ) ;
dir . cd ( " Databases " ) ;
QCOMPARE ( QFileInfo ( databaseLocation ) . dir ( ) . path ( ) , dir . path ( ) ) ;
QCryptographicHash md5 ( QCryptographicHash : : Md5 ) ;
md5 . addData ( databaseName . toUtf8 ( ) ) ;
QCOMPARE ( databaseLocation , QDir : : toNativeSeparators ( dir . filePath ( QLatin1String ( md5 . result ( ) . toHex ( ) ) ) ) ) ;
}
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : clearComponentCache ( )
2011-04-27 10:05:43 +00:00
{
2012-02-16 04:43:03 +00:00
QQmlEngine engine ;
2011-04-27 10:05:43 +00:00
2017-05-31 13:54:15 +00:00
const QString fileName = m_tempDir . filePath ( QStringLiteral ( " temp.qml " ) ) ;
const QUrl fileUrl = QUrl : : fromLocalFile ( fileName ) ;
2011-04-27 10:05:43 +00:00
// Create original qml file
{
2017-05-31 13:54:15 +00:00
QFile file ( fileName ) ;
2011-04-27 10:05:43 +00:00
QVERIFY ( file . open ( QIODevice : : WriteOnly ) ) ;
2012-01-31 06:52:36 +00:00
file . write ( " import QtQuick 2.0 \n QtObject { \n property int test: 10 \n } \n " ) ;
2011-04-27 10:05:43 +00:00
file . close ( ) ;
}
// Test "test" property
{
2017-05-31 13:54:15 +00:00
QQmlComponent component ( & engine , fileUrl ) ;
2023-07-28 12:57:16 +00:00
std : : unique_ptr < QObject > obj { component . create ( ) } ;
QVERIFY ( obj . get ( ) ! = nullptr ) ;
2011-04-27 10:05:43 +00:00
QCOMPARE ( obj - > property ( " test " ) . toInt ( ) , 10 ) ;
}
// Modify qml file
{
2016-08-03 11:50:44 +00:00
// On macOS with HFS+ the precision of file times is measured in seconds, so to ensure that
// the newly written file has a modification date newer than an existing cache file, we must
// wait.
// Similar effects of lacking precision have been observed on some Linux systems.
QThread : : sleep ( 1 ) ;
2017-05-31 13:54:15 +00:00
QFile file ( fileName ) ;
2011-04-27 10:05:43 +00:00
QVERIFY ( file . open ( QIODevice : : WriteOnly ) ) ;
2012-01-31 06:52:36 +00:00
file . write ( " import QtQuick 2.0 \n QtObject { \n property int test: 11 \n } \n " ) ;
2011-04-27 10:05:43 +00:00
file . close ( ) ;
}
// Test cache hit
{
2017-05-31 13:54:15 +00:00
QQmlComponent component ( & engine , fileUrl ) ;
2023-07-28 12:57:16 +00:00
std : : unique_ptr < QObject > obj { component . create ( ) } ;
QVERIFY ( obj . get ( ) ! = nullptr ) ;
2011-04-27 10:05:43 +00:00
QCOMPARE ( obj - > property ( " test " ) . toInt ( ) , 10 ) ;
}
// Clear cache
engine . clearComponentCache ( ) ;
// Test cache refresh
{
2017-05-31 13:54:15 +00:00
QQmlComponent component ( & engine , fileUrl ) ;
2023-07-28 12:57:16 +00:00
std : : unique_ptr < QObject > obj { component . create ( ) } ;
QVERIFY ( obj . get ( ) ! = nullptr ) ;
2011-04-27 10:05:43 +00:00
QCOMPARE ( obj - > property ( " test " ) . toInt ( ) , 11 ) ;
}
2017-05-31 13:54:15 +00:00
// Regular Synchronous loading will leave us with an event posted
// to the gui thread and an extra refcount that will only be dropped after the
// event delivery. Call sendPostedEvents() to get rid of it so that
// the temporary directory can be removed.
QCoreApplication : : sendPostedEvents ( ) ;
2011-04-27 10:05:43 +00:00
}
2012-05-03 22:32:45 +00:00
struct ComponentCacheFunctions : public QObject , public QQmlIncubationController
{
Q_OBJECT
public :
QQmlEngine * engine ;
ComponentCacheFunctions ( QQmlEngine & e ) : engine ( & e ) { }
Q_INVOKABLE void trim ( )
{
// Wait for any pending deletions to occur
2018-02-21 09:41:54 +00:00
QCoreApplication : : sendPostedEvents ( nullptr , QEvent : : DeferredDelete ) ;
2012-05-03 22:32:45 +00:00
QCoreApplication : : processEvents ( ) ;
2016-05-29 17:21:54 +00:00
// There might be JS function objects around that hold a last ref to the compilation unit that's
// keeping the type compilation data (CompilationUnit) around. Let's collect them as well so that
// trim works well.
2023-10-19 08:05:28 +00:00
gc ( * engine ) ;
2016-05-29 17:21:54 +00:00
2012-05-03 22:32:45 +00:00
engine - > trimComponentCache ( ) ;
}
Q_INVOKABLE bool isTypeLoaded ( QString file )
{
return QQmlEnginePrivate : : get ( engine ) - > isTypeLoaded ( tst_qqmlengine : : instance ( ) - > testFileUrl ( file ) ) ;
}
Q_INVOKABLE bool isScriptLoaded ( QString file )
{
return QQmlEnginePrivate : : get ( engine ) - > isScriptLoaded ( tst_qqmlengine : : instance ( ) - > testFileUrl ( file ) ) ;
}
Q_INVOKABLE void beginIncubation ( )
{
startTimer ( 0 ) ;
}
Q_INVOKABLE void waitForIncubation ( )
{
while ( incubatingObjectCount ( ) > 0 ) {
QCoreApplication : : processEvents ( ) ;
}
}
private :
2021-03-25 13:44:04 +00:00
void timerEvent ( QTimerEvent * ) override
2012-05-03 22:32:45 +00:00
{
incubateFor ( 1000 ) ;
}
} ;
void tst_qqmlengine : : trimComponentCache ( )
{
QFETCH ( QString , file ) ;
QQmlEngine engine ;
ComponentCacheFunctions componentCache ( engine ) ;
engine . setIncubationController ( & componentCache ) ;
QQmlComponent component ( & engine , testFileUrl ( file ) ) ;
2019-07-03 15:25:55 +00:00
QVERIFY2 ( component . isReady ( ) , qPrintable ( component . errorString ( ) ) ) ;
2019-10-04 11:49:18 +00:00
QScopedPointer < QObject > object ( component . createWithInitialProperties ( {
{ " componentCache " , QVariant : : fromValue ( & componentCache ) }
} ) ) ;
2018-02-21 09:41:54 +00:00
QVERIFY ( object ! = nullptr ) ;
2012-05-03 22:32:45 +00:00
QCOMPARE ( object - > property ( " success " ) . toBool ( ) , true ) ;
}
void tst_qqmlengine : : trimComponentCache_data ( )
{
QTest : : addColumn < QString > ( " file " ) ;
// The various tests here are for two types of components: those that are
// empty apart from their inherited elements, and those that define new properties.
// For each there are five types of composition: extension, aggregation,
// aggregation via component, property and object-created-via-transient-component.
2023-08-07 08:20:42 +00:00
const QStringList components = {
" EmptyComponent " ,
" VMEComponent " ,
" EmptyExtendEmptyComponent " ,
" VMEExtendEmptyComponent " ,
" EmptyExtendVMEComponent " ,
" VMEExtendVMEComponent " ,
" EmptyAggregateEmptyComponent " ,
" VMEAggregateEmptyComponent " ,
" EmptyAggregateVMEComponent " ,
" VMEAggregateVMEComponent " ,
" EmptyPropertyEmptyComponent " ,
" VMEPropertyEmptyComponent " ,
" EmptyPropertyVMEComponent " ,
" VMEPropertyVMEComponent " ,
" VMETransientEmptyComponent " ,
" VMETransientVMEComponent " ,
} ;
for ( const QString & test : components ) {
2012-05-03 22:32:45 +00:00
// For these cases, we first test that the component instance keeps the components
// referenced, and then that the instantiated object keeps the components referenced
for ( int i = 1 ; i < = 2 ; + + i ) {
QString name ( QString ( " %1-%2 " ) . arg ( test ) . arg ( i ) ) ;
QString file ( QString ( " test%1.%2.qml " ) . arg ( test ) . arg ( i ) ) ;
QTest : : newRow ( name . toLatin1 ( ) . constData ( ) ) < < file ;
}
}
// Test that a transient component is correctly referenced
QTest : : newRow ( " TransientComponent-1 " ) < < " testTransientComponent.1.qml " ;
QTest : : newRow ( " TransientComponent-2 " ) < < " testTransientComponent.2.qml " ;
// Test that components can be reloaded after unloading
QTest : : newRow ( " ReloadComponent " ) < < " testReloadComponent.qml " ;
// Test that components are correctly referenced when dynamically loaded
QTest : : newRow ( " LoaderComponent " ) < < " testLoaderComponent.qml " ;
// Test that components are correctly referenced when incubated
QTest : : newRow ( " IncubatedComponent " ) < < " testIncubatedComponent.qml " ;
// Test that a top-level omponents is correctly referenced
QTest : : newRow ( " TopLevelComponent " ) < < " testTopLevelComponent.qml " ;
// TODO:
// Test that scripts are unloaded when no longer referenced
QTest : : newRow ( " ScriptComponent " ) < < " testScriptComponent.qml " ;
}
2021-08-19 13:08:41 +00:00
static QJSValue createValueSingleton ( QQmlEngine * , QJSEngine * ) { return 13u ; }
void tst_qqmlengine : : clearSingletons ( )
{
ObjectCaller objectCaller1 ;
ObjectCaller objectCaller2 ;
const int cppInstance = qmlRegisterSingletonInstance (
" ClearSingletons " , 1 , 0 , " CppInstance " , & objectCaller1 ) ;
2022-08-19 14:14:36 +00:00
# if QT_DEPRECATED_SINCE(6, 3)
QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
2021-08-19 13:08:41 +00:00
QQmlPrivate : : SingletonFunctor deprecatedSingletonFunctor ;
deprecatedSingletonFunctor . m_object = & objectCaller2 ;
const int deprecatedCppInstance = qmlRegisterSingletonType < ObjectCaller > (
" ClearSingletons " , 1 , 0 , " DeprecatedCppInstance " , deprecatedSingletonFunctor ) ;
2022-08-19 14:14:36 +00:00
QT_WARNING_POP
# endif // QT_DEPRECATED_SINCE(6, 3)
2021-08-19 13:08:41 +00:00
const int cppFactory = qmlRegisterSingletonType < CppSingleton > (
" ClearSingletons " , 1 , 0 , " CppFactory " , & CppSingleton : : create ) ;
const int jsValue = qmlRegisterSingletonType (
" ClearSingletons " , 1 , 0 , " JsValue " , & createValueSingleton ) ;
const int jsObject = qmlRegisterSingletonType (
" ClearSingletons " , 1 , 0 , " JsObject " , & JsSingleton : : create ) ;
const int qmlObject = qmlRegisterSingletonType (
testFileUrl ( " QmlSingleton.qml " ) , " ClearSingletons " , 1 , 0 , " QmlSingleton " ) ;
QQmlEngine engine ;
QCOMPARE ( engine . singletonInstance < ObjectCaller * > ( cppInstance ) , & objectCaller1 ) ;
2022-08-19 14:14:36 +00:00
# if QT_DEPRECATED_SINCE(6, 3)
2021-08-19 13:08:41 +00:00
QCOMPARE ( engine . singletonInstance < ObjectCaller * > ( deprecatedCppInstance ) , & objectCaller2 ) ;
2022-08-19 14:14:36 +00:00
# endif
2021-08-19 13:08:41 +00:00
const CppSingleton * oldCppSingleton = engine . singletonInstance < CppSingleton * > ( cppFactory ) ;
QVERIFY ( oldCppSingleton ! = nullptr ) ;
2021-08-27 11:42:44 +00:00
const uint oldCppSingletonId = oldCppSingleton - > id ;
QVERIFY ( oldCppSingletonId > 0 ) ;
QCOMPARE ( CppSingleton : : instantiations , oldCppSingletonId ) ;
2021-08-19 13:08:41 +00:00
QCOMPARE ( engine . singletonInstance < QJSValue > ( jsValue ) . toUInt ( ) , 13u ) ;
const JsSingleton * oldJsSingleton = engine . singletonInstance < JsSingleton * > ( jsObject ) ;
QVERIFY ( oldJsSingleton ! = nullptr ) ;
2021-09-22 08:00:44 +00:00
const uint oldJsSingletonId = oldJsSingleton - > id ;
2021-08-19 13:08:41 +00:00
const QObject * oldQmlSingleton = engine . singletonInstance < QObject * > ( qmlObject ) ;
QVERIFY ( oldQmlSingleton ! = nullptr ) ;
QQmlComponent c ( & engine ) ;
c . setData ( " import ClearSingletons \n "
" import QtQml \n "
" QtObject { \n "
" property QtObject a: CppInstance \n "
2022-08-19 14:14:36 +00:00
# if QT_DEPRECATED_SINCE(6, 3)
2021-08-19 13:08:41 +00:00
" property QtObject f: DeprecatedCppInstance \n "
2022-08-19 14:14:36 +00:00
# endif
2021-08-19 13:08:41 +00:00
" property QtObject b: CppFactory \n "
" property int c: JsValue \n "
" property QtObject d: JsObject \n "
" property QtObject e: QmlSingleton \n "
" } " , QUrl ( ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
QScopedPointer < QObject > singletonUser ( c . create ( ) ) ;
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " a " ) ) , & objectCaller1 ) ;
2022-08-19 14:14:36 +00:00
# if QT_DEPRECATED_SINCE(6, 3)
2021-08-19 13:08:41 +00:00
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " f " ) ) , & objectCaller2 ) ;
2022-08-19 14:14:36 +00:00
# endif
2021-08-19 13:08:41 +00:00
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " b " ) ) , oldCppSingleton ) ;
QCOMPARE ( singletonUser - > property ( " c " ) . toUInt ( ) , 13u ) ;
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " d " ) ) , oldJsSingleton ) ;
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " e " ) ) , oldQmlSingleton ) ;
engine . clearSingletons ( ) ;
2021-08-27 11:42:44 +00:00
QCOMPARE ( CppSingleton : : instantiations , oldCppSingletonId ) ;
2021-08-19 13:08:41 +00:00
QCOMPARE ( engine . singletonInstance < ObjectCaller * > ( cppInstance ) , & objectCaller1 ) ;
2022-08-19 14:14:36 +00:00
# if QT_DEPRECATED_SINCE(6, 3)
2021-08-19 13:08:41 +00:00
// Singleton instances created with previous versions of qmlRegisterSingletonInstance()
// are lost and cannot be accessed anymore. We can only call their synthesized factory
// functions once. This is not a big problem as you have to recompile in order to use
// clearSingletons() anyway.
QTest : : ignoreMessage (
QtWarningMsg ,
" <Unknown File>: Singleton registered by registerSingletonInstance "
" must only be accessed from one engine " ) ;
QTest : : ignoreMessage (
QtCriticalMsg ,
" <Unknown File>: qmlRegisterSingletonType(): \" DeprecatedCppInstance \" is not "
" available because the callback function returns a null pointer. " ) ;
QCOMPARE ( engine . singletonInstance < ObjectCaller * > ( deprecatedCppInstance ) , nullptr ) ;
2022-08-19 14:14:36 +00:00
# endif
2021-08-19 13:08:41 +00:00
2021-08-27 11:42:44 +00:00
QCOMPARE ( CppSingleton : : instantiations , oldCppSingletonId ) ;
2021-08-19 13:08:41 +00:00
const CppSingleton * newCppSingleton = engine . singletonInstance < CppSingleton * > ( cppFactory ) ;
2021-08-27 11:42:44 +00:00
QVERIFY ( newCppSingleton ! = nullptr ) ; // The pointer may be the same as the old one
QCOMPARE ( CppSingleton : : instantiations , oldCppSingletonId + 1 ) ;
QCOMPARE ( newCppSingleton - > id , CppSingleton : : instantiations ) ;
2021-08-19 13:08:41 +00:00
QCOMPARE ( engine . singletonInstance < QJSValue > ( jsValue ) . toUInt ( ) , 13u ) ;
const JsSingleton * newJsSingleton = engine . singletonInstance < JsSingleton * > ( jsObject ) ;
QVERIFY ( newJsSingleton ! = nullptr ) ;
2021-09-22 08:00:44 +00:00
QVERIFY ( newJsSingleton - > id ! = oldJsSingletonId ) ;
2021-08-19 13:08:41 +00:00
const QObject * newQmlSingleton = engine . singletonInstance < QObject * > ( qmlObject ) ;
QVERIFY ( newQmlSingleton ! = nullptr ) ;
QVERIFY ( newQmlSingleton ! = oldQmlSingleton ) ;
// Holding on to an old singleton instance is OK. We don't delete those.
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " a " ) ) , & objectCaller1 ) ;
2022-08-19 14:14:36 +00:00
# if QT_DEPRECATED_SINCE(6, 3)
2021-08-19 13:08:41 +00:00
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " f " ) ) , & objectCaller2 ) ;
2022-08-19 14:14:36 +00:00
# endif
2021-08-19 13:08:41 +00:00
// Deleting the singletons created by factories has cleared their references in QML.
// We don't renew them as the point of clearing the singletons is not having any
// singletons left afterwards.
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " b " ) ) , nullptr ) ;
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " d " ) ) , nullptr ) ;
QCOMPARE ( qvariant_cast < QObject * > ( singletonUser - > property ( " e " ) ) , nullptr ) ;
// Value types are unaffected as they are copied.
QCOMPARE ( singletonUser - > property ( " c " ) . toUInt ( ) , 13u ) ;
}
2012-05-21 03:40:22 +00:00
void tst_qqmlengine : : repeatedCompilation ( )
{
QQmlEngine engine ;
for ( int i = 0 ; i < 100 ; + + i ) {
engine . collectGarbage ( ) ;
engine . trimComponentCache ( ) ;
QQmlComponent component ( & engine , testFileUrl ( " repeatedCompilation.qml " ) ) ;
QVERIFY ( component . isReady ( ) ) ;
QScopedPointer < QObject > object ( component . create ( ) ) ;
2018-02-21 09:41:54 +00:00
QVERIFY ( object ! = nullptr ) ;
2012-05-21 03:40:22 +00:00
QCOMPARE ( object - > property ( " success " ) . toBool ( ) , true ) ;
}
}
void tst_qqmlengine : : failedCompilation ( )
{
QFETCH ( QString , file ) ;
QQmlEngine engine ;
QQmlComponent component ( & engine , testFileUrl ( file ) ) ;
2019-10-04 11:49:18 +00:00
QTest : : ignoreMessage ( QtMsgType : : QtWarningMsg , " QQmlComponent: Component is not ready " ) ;
2012-05-21 03:40:22 +00:00
QVERIFY ( ! component . isReady ( ) ) ;
QScopedPointer < QObject > object ( component . create ( ) ) ;
2015-07-24 13:27:58 +00:00
QVERIFY ( object . isNull ( ) ) ;
2012-05-21 03:40:22 +00:00
engine . collectGarbage ( ) ;
engine . trimComponentCache ( ) ;
engine . clearComponentCache ( ) ;
}
void tst_qqmlengine : : failedCompilation_data ( )
{
QTest : : addColumn < QString > ( " file " ) ;
QTest : : newRow ( " Invalid URL " ) < < " failedCompilation.does.not.exist.qml " ;
QTest : : newRow ( " Invalid content " ) < < " failedCompilation.1.qml " ;
}
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : outputWarningsToStandardError ( )
2011-04-27 10:05:43 +00:00
{
2012-02-16 04:43:03 +00:00
QQmlEngine engine ;
2011-04-27 10:05:43 +00:00
QCOMPARE ( engine . outputWarningsToStandardError ( ) , true ) ;
2012-02-16 04:43:03 +00:00
QQmlComponent c ( & engine ) ;
2012-01-31 06:52:36 +00:00
c . setData ( " import QtQuick 2.0; QtObject { property int a: undefined } " , QUrl ( ) ) ;
2011-04-27 10:05:43 +00:00
2015-07-24 11:39:05 +00:00
QVERIFY ( c . isReady ( ) ) ;
2011-04-27 10:05:43 +00:00
2012-12-18 10:41:06 +00:00
QQmlTestMessageHandler messageHandler ;
2011-04-27 10:05:43 +00:00
2020-06-08 14:30:18 +00:00
QScopedPointer < QObject > o ( c . create ( ) ) ;
2011-04-27 10:05:43 +00:00
2018-02-21 09:41:54 +00:00
QVERIFY ( o ! = nullptr ) ;
2020-06-08 14:30:18 +00:00
o . reset ( ) ;
2011-04-27 10:05:43 +00:00
2022-10-05 05:29:16 +00:00
QCOMPARE ( messageHandler . messages ( ) . size ( ) , 1 ) ;
2018-04-09 11:39:23 +00:00
QCOMPARE ( messageHandler . messages ( ) . at ( 0 ) , QLatin1String ( " <Unknown File>:1:32: Unable to assign [undefined] to int " ) ) ;
2012-12-18 10:41:06 +00:00
messageHandler . clear ( ) ;
2011-04-27 10:05:43 +00:00
engine . setOutputWarningsToStandardError ( false ) ;
QCOMPARE ( engine . outputWarningsToStandardError ( ) , false ) ;
2020-06-08 14:30:18 +00:00
o . reset ( c . create ( ) ) ;
2011-04-27 10:05:43 +00:00
2018-02-21 09:41:54 +00:00
QVERIFY ( o ! = nullptr ) ;
2020-06-08 14:30:18 +00:00
o . reset ( ) ;
2011-04-27 10:05:43 +00:00
2012-12-18 10:41:06 +00:00
QVERIFY2 ( messageHandler . messages ( ) . isEmpty ( ) , qPrintable ( messageHandler . messageString ( ) ) ) ;
2011-04-27 10:05:43 +00:00
}
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : objectOwnership ( )
2011-04-27 10:05:43 +00:00
{
{
2018-02-21 09:41:54 +00:00
QCOMPARE ( QQmlEngine : : objectOwnership ( nullptr ) , QQmlEngine : : CppOwnership ) ;
QQmlEngine : : setObjectOwnership ( nullptr , QQmlEngine : : JavaScriptOwnership ) ;
QCOMPARE ( QQmlEngine : : objectOwnership ( nullptr ) , QQmlEngine : : CppOwnership ) ;
2011-04-27 10:05:43 +00:00
}
{
QObject o ;
2012-02-16 04:43:03 +00:00
QCOMPARE ( QQmlEngine : : objectOwnership ( & o ) , QQmlEngine : : CppOwnership ) ;
QQmlEngine : : setObjectOwnership ( & o , QQmlEngine : : CppOwnership ) ;
QCOMPARE ( QQmlEngine : : objectOwnership ( & o ) , QQmlEngine : : CppOwnership ) ;
QQmlEngine : : setObjectOwnership ( & o , QQmlEngine : : JavaScriptOwnership ) ;
QCOMPARE ( QQmlEngine : : objectOwnership ( & o ) , QQmlEngine : : JavaScriptOwnership ) ;
QQmlEngine : : setObjectOwnership ( & o , QQmlEngine : : CppOwnership ) ;
QCOMPARE ( QQmlEngine : : objectOwnership ( & o ) , QQmlEngine : : CppOwnership ) ;
2011-04-27 10:05:43 +00:00
}
{
2012-02-16 04:43:03 +00:00
QQmlEngine engine ;
QQmlComponent c ( & engine ) ;
2012-01-31 06:52:36 +00:00
c . setData ( " import QtQuick 2.0; QtObject { property QtObject object: QtObject {} } " , QUrl ( ) ) ;
2011-04-27 10:05:43 +00:00
2020-06-08 14:30:18 +00:00
QScopedPointer < QObject > o ( c . create ( ) ) ;
QVERIFY ( ! o . isNull ( ) ) ;
2011-04-27 10:05:43 +00:00
2020-06-08 14:30:18 +00:00
QCOMPARE ( QQmlEngine : : objectOwnership ( o . data ( ) ) , QQmlEngine : : CppOwnership ) ;
2011-04-27 10:05:43 +00:00
QObject * o2 = qvariant_cast < QObject * > ( o - > property ( " object " ) ) ;
2012-02-16 04:43:03 +00:00
QCOMPARE ( QQmlEngine : : objectOwnership ( o2 ) , QQmlEngine : : JavaScriptOwnership ) ;
2011-04-27 10:05:43 +00:00
2020-06-08 14:30:18 +00:00
o . reset ( ) ;
2011-04-27 10:05:43 +00:00
}
2012-04-19 12:38:38 +00:00
{
QObject * ptr = createAQObjectForOwnershipTest ( ) ;
QSignalSpy spy ( ptr , SIGNAL ( destroyed ( ) ) ) ;
{
QQmlEngine engine ;
QQmlComponent c ( & engine ) ;
QQmlEngine : : setObjectOwnership ( ptr , QQmlEngine : : JavaScriptOwnership ) ;
2019-10-04 11:49:18 +00:00
c . setData ( " import QtQuick 2.0; Item { required property QtObject test; property int data: test.createAQObjectForOwnershipTest() ? 0 : 1 } " , QUrl ( ) ) ;
2012-04-19 12:38:38 +00:00
QVERIFY ( c . isReady ( ) ) ;
2020-06-08 14:30:18 +00:00
QScopedPointer < QObject > o (
c . createWithInitialProperties ( { { " test " , QVariant : : fromValue ( this ) } } ) ) ;
2018-02-21 09:41:54 +00:00
QVERIFY ( o ! = nullptr ) ;
2012-04-19 12:38:38 +00:00
}
2022-10-05 05:29:16 +00:00
QTRY_VERIFY ( spy . size ( ) ) ;
2012-04-19 12:38:38 +00:00
}
{
QObject * ptr = new QObject ( ) ;
QSignalSpy spy ( ptr , SIGNAL ( destroyed ( ) ) ) ;
{
QQmlEngine engine ;
QQmlComponent c ( & engine ) ;
QQmlEngine : : setObjectOwnership ( ptr , QQmlEngine : : JavaScriptOwnership ) ;
2019-10-04 11:49:18 +00:00
c . setData ( " import QtQuick 2.0; QtObject { required property QtObject test; property var object: { var i = test; test ? 0 : 1 } } " , QUrl ( ) ) ;
2012-04-19 12:38:38 +00:00
QVERIFY ( c . isReady ( ) ) ;
2020-06-08 14:30:18 +00:00
QScopedPointer < QObject > o (
c . createWithInitialProperties ( { { " test " , QVariant : : fromValue ( ptr ) } } ) ) ;
2018-02-21 09:41:54 +00:00
QVERIFY ( o ! = nullptr ) ;
2020-06-08 14:30:18 +00:00
QQmlProperty testProp ( o . data ( ) , " test " ) ;
2019-10-04 11:49:18 +00:00
testProp . write ( QVariant : : fromValue < QObject * > ( nullptr ) ) ;
2012-04-19 12:38:38 +00:00
}
2022-10-05 05:29:16 +00:00
QTRY_VERIFY ( spy . size ( ) ) ;
2012-04-19 12:38:38 +00:00
}
2011-04-27 10:05:43 +00:00
}
2011-07-25 05:47:40 +00:00
// Test an object can be accessed by multiple engines
2012-02-16 04:43:03 +00:00
void tst_qqmlengine : : multipleEngines ( )
2011-07-25 05:47:40 +00:00
{
QObject o ;
o . setObjectName ( " TestName " ) ;
// Simultaneous engines
{
2012-02-16 04:43:03 +00:00
QQmlEngine engine1 ;
QQmlEngine engine2 ;
2011-07-25 05:47:40 +00:00
engine1 . rootContext ( ) - > setContextProperty ( " object " , & o ) ;
engine2 . rootContext ( ) - > setContextProperty ( " object " , & o ) ;
2018-02-21 09:41:54 +00:00
QQmlExpression expr1 ( engine1 . rootContext ( ) , nullptr , QString ( " object.objectName " ) ) ;
QQmlExpression expr2 ( engine2 . rootContext ( ) , nullptr , QString ( " object.objectName " ) ) ;
2011-07-25 05:47:40 +00:00
QCOMPARE ( expr1 . evaluate ( ) . toString ( ) , QString ( " TestName " ) ) ;
QCOMPARE ( expr2 . evaluate ( ) . toString ( ) , QString ( " TestName " ) ) ;
}
// Serial engines
{
2012-02-16 04:43:03 +00:00
QQmlEngine engine1 ;
2011-07-25 05:47:40 +00:00
engine1 . rootContext ( ) - > setContextProperty ( " object " , & o ) ;
2018-02-21 09:41:54 +00:00
QQmlExpression expr1 ( engine1 . rootContext ( ) , nullptr , QString ( " object.objectName " ) ) ;
2011-07-25 05:47:40 +00:00
QCOMPARE ( expr1 . evaluate ( ) . toString ( ) , QString ( " TestName " ) ) ;
}
{
2012-02-16 04:43:03 +00:00
QQmlEngine engine1 ;
2011-07-25 05:47:40 +00:00
engine1 . rootContext ( ) - > setContextProperty ( " object " , & o ) ;
2018-02-21 09:41:54 +00:00
QQmlExpression expr1 ( engine1 . rootContext ( ) , nullptr , QString ( " object.objectName " ) ) ;
2011-07-25 05:47:40 +00:00
QCOMPARE ( expr1 . evaluate ( ) . toString ( ) , QString ( " TestName " ) ) ;
}
}
2012-05-23 08:05:10 +00:00
void tst_qqmlengine : : qtqmlModule_data ( )
{
QTest : : addColumn < QUrl > ( " testFile " ) ;
QTest : : addColumn < QString > ( " expectedError " ) ;
QTest : : addColumn < QStringList > ( " expectedWarnings " ) ;
QTest : : newRow ( " import QtQml of correct version (2.0) " )
< < testFileUrl ( " qtqmlModule.1.qml " )
< < QString ( )
< < QStringList ( ) ;
QTest : : newRow ( " import QtQml of incorrect version (3.0) " )
< < testFileUrl ( " qtqmlModule.2.qml " )
< < QString ( testFileUrl ( " qtqmlModule.2.qml " ) . toString ( ) + QLatin1String ( " :1 module \" QtQml \" version 3.0 is not installed \n " ) )
< < QStringList ( ) ;
QTest : : newRow ( " import QtQml of incorrect version (1.0) " )
< < testFileUrl ( " qtqmlModule.3.qml " )
< < QString ( testFileUrl ( " qtqmlModule.3.qml " ) . toString ( ) + QLatin1String ( " :1 module \" QtQml \" version 1.0 is not installed \n " ) )
< < QStringList ( ) ;
2020-01-17 09:46:54 +00:00
QTest : : newRow ( " import QtQml of old version (2.50) " )
2012-05-23 08:05:10 +00:00
< < testFileUrl ( " qtqmlModule.4.qml " )
2020-01-17 09:46:54 +00:00
< < QString ( )
2012-05-23 08:05:10 +00:00
< < QStringList ( ) ;
2012-11-24 19:36:33 +00:00
QTest : : newRow ( " QtQml 2.0 module provides Component, QtObject, Connections, Binding and Timer " )
2012-05-23 08:05:10 +00:00
< < testFileUrl ( " qtqmlModule.5.qml " )
< < QString ( )
< < QStringList ( ) ;
QTest : : newRow ( " can import QtQml then QtQuick " )
< < testFileUrl ( " qtqmlModule.6.qml " )
< < QString ( )
< < QStringList ( ) ;
QTest : : newRow ( " can import QtQuick then QtQml " )
< < testFileUrl ( " qtqmlModule.7.qml " )
< < QString ( )
< < QStringList ( ) ;
QTest : : newRow ( " no import results in no QtObject availability " )
< < testFileUrl ( " qtqmlModule.8.qml " )
< < QString ( testFileUrl ( " qtqmlModule.8.qml " ) . toString ( ) + QLatin1String ( " :4 QtObject is not a type \n " ) )
< < QStringList ( ) ;
QTest : : newRow ( " importing QtQml only results in no Item availability " )
< < testFileUrl ( " qtqmlModule.9.qml " )
< < QString ( testFileUrl ( " qtqmlModule.9.qml " ) . toString ( ) + QLatin1String ( " :4 Item is not a type \n " ) )
< < QStringList ( ) ;
2020-01-17 09:46:54 +00:00
QTest : : newRow ( " import QtQml of incorrect version (6.50) " )
< < testFileUrl ( " qtqmlModule.10.qml " )
< < QString ( testFileUrl ( " qtqmlModule.10.qml " ) . toString ( ) + QLatin1String ( " :1 module \" QtQml \" version 6.50 is not installed \n " ) )
< < QStringList ( ) ;
2012-05-23 08:05:10 +00:00
}
// Test that the engine registers the QtQml module
void tst_qqmlengine : : qtqmlModule ( )
{
QFETCH ( QUrl , testFile ) ;
QFETCH ( QString , expectedError ) ;
QFETCH ( QStringList , expectedWarnings ) ;
2023-08-07 08:20:42 +00:00
for ( const QString & w : std : : as_const ( expectedWarnings ) )
2012-05-23 08:05:10 +00:00
QTest : : ignoreMessage ( QtWarningMsg , qPrintable ( w ) ) ;
QQmlEngine e ;
QQmlComponent c ( & e , testFile ) ;
if ( expectedError . isEmpty ( ) ) {
2022-08-03 10:20:26 +00:00
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
2020-06-08 14:30:18 +00:00
QScopedPointer < QObject > o ( c . create ( ) ) ;
2012-05-23 08:05:10 +00:00
QVERIFY ( o ) ;
} else {
QCOMPARE ( c . errorString ( ) , expectedError ) ;
}
}
2013-03-01 01:03:43 +00:00
class CustomSelector : public QQmlAbstractUrlInterceptor
{
public :
2013-07-18 13:02:37 +00:00
CustomSelector ( const QUrl & base ) : m_base ( base ) { }
2021-03-25 13:44:04 +00:00
QUrl intercept ( const QUrl & url , QQmlAbstractUrlInterceptor : : DataType d ) override
2013-03-01 01:03:43 +00:00
{
2019-07-03 15:25:55 +00:00
if ( ( url . scheme ( ) ! = QStringLiteral ( " file " ) & & url . scheme ( ) ! = QStringLiteral ( " qrc " ) )
| | url . path ( ) . contains ( " QtQml " ) )
2013-03-01 01:03:43 +00:00
return url ;
if ( ! m_interceptionPoints . contains ( d ) )
return url ;
2019-07-03 15:25:55 +00:00
if ( url . path ( ) . endsWith ( " Test.2/qmldir " ) ) { //Special case
QUrl url = m_base ;
url . setPath ( m_base . path ( ) + " interception/module/intercepted/qmldir " ) ;
return url ;
}
2017-04-20 13:59:31 +00:00
// Special case: with 5.10 we always add the implicit import, so we need to explicitly handle this case now
if ( url . path ( ) . endsWith ( " intercepted/qmldir " ) )
return url ;
2013-07-18 13:02:37 +00:00
2013-03-01 01:03:43 +00:00
QString alteredPath = url . path ( ) ;
int a = alteredPath . lastIndexOf ( ' / ' ) ;
if ( a < 0 )
a = 0 ;
alteredPath . insert ( a , QStringLiteral ( " /intercepted " ) ) ;
QUrl ret = url ;
ret . setPath ( alteredPath ) ;
return ret ;
}
QList < QQmlAbstractUrlInterceptor : : DataType > m_interceptionPoints ;
2013-07-18 13:02:37 +00:00
QUrl m_base ;
2013-03-01 01:03:43 +00:00
} ;
Q_DECLARE_METATYPE ( QList < QQmlAbstractUrlInterceptor : : DataType > ) ;
2019-10-04 11:49:18 +00:00
2013-03-01 01:03:43 +00:00
void tst_qqmlengine : : urlInterceptor_data ( )
{
QTest : : addColumn < QUrl > ( " testFile " ) ;
QTest : : addColumn < QList < QQmlAbstractUrlInterceptor : : DataType > > ( " interceptionPoint " ) ;
QTest : : addColumn < QString > ( " expectedChildString " ) ;
QTest : : addColumn < QString > ( " expectedScriptString " ) ;
QTest : : addColumn < QString > ( " expectedResolvedUrl " ) ;
QTest : : addColumn < QString > ( " expectedAbsoluteUrl " ) ;
QTest : : newRow ( " InterceptTypes " )
< < testFileUrl ( " interception/types/urlInterceptor.qml " )
< < ( QList < QQmlAbstractUrlInterceptor : : DataType > ( ) < < QQmlAbstractUrlInterceptor : : QmlFile < < QQmlAbstractUrlInterceptor : : JavaScriptFile < < QQmlAbstractUrlInterceptor : : UrlString )
< < QStringLiteral ( " intercepted " )
< < QStringLiteral ( " intercepted " )
< < testFileUrl ( " interception/types/intercepted/doesNotExist.file " ) . toString ( )
< < QStringLiteral ( " file:///intercepted/doesNotExist.file " ) ;
QTest : : newRow ( " InterceptQmlDir " )
< < testFileUrl ( " interception/qmldir/urlInterceptor.qml " )
< < ( QList < QQmlAbstractUrlInterceptor : : DataType > ( ) < < QQmlAbstractUrlInterceptor : : QmldirFile < < QQmlAbstractUrlInterceptor : : UrlString )
< < QStringLiteral ( " intercepted " )
< < QStringLiteral ( " base file " )
< < testFileUrl ( " interception/qmldir/intercepted/doesNotExist.file " ) . toString ( )
< < QStringLiteral ( " file:///intercepted/doesNotExist.file " ) ;
2013-07-18 13:02:37 +00:00
QTest : : newRow ( " InterceptModule " ) //just a Test{}, needs to intercept the module import for it to work
< < testFileUrl ( " interception/module/urlInterceptor.qml " )
< < ( QList < QQmlAbstractUrlInterceptor : : DataType > ( ) < < QQmlAbstractUrlInterceptor : : QmldirFile )
< < QStringLiteral ( " intercepted " )
< < QStringLiteral ( " intercepted " )
< < testFileUrl ( " interception/module/intercepted/doesNotExist.file " ) . toString ( )
< < QStringLiteral ( " file:///doesNotExist.file " ) ;
2013-03-01 01:03:43 +00:00
QTest : : newRow ( " InterceptStrings " )
< < testFileUrl ( " interception/strings/urlInterceptor.qml " )
< < ( QList < QQmlAbstractUrlInterceptor : : DataType > ( ) < < QQmlAbstractUrlInterceptor : : UrlString )
< < QStringLiteral ( " base file " )
< < QStringLiteral ( " base file " )
< < testFileUrl ( " interception/strings/intercepted/doesNotExist.file " ) . toString ( )
< < QStringLiteral ( " file:///intercepted/doesNotExist.file " ) ;
2015-10-12 14:24:59 +00:00
QTest : : newRow ( " InterceptIncludes " )
< < testFileUrl ( " interception/includes/urlInterceptor.qml " )
< < ( QList < QQmlAbstractUrlInterceptor : : DataType > ( ) < < QQmlAbstractUrlInterceptor : : JavaScriptFile )
< < QStringLiteral ( " base file " )
< < QStringLiteral ( " intercepted include file " )
< < testFileUrl ( " interception/includes/doesNotExist.file " ) . toString ( )
< < QStringLiteral ( " file:///doesNotExist.file " ) ;
2013-03-01 01:03:43 +00:00
}
void tst_qqmlengine : : urlInterceptor ( )
{
QFETCH ( QUrl , testFile ) ;
QFETCH ( QList < QQmlAbstractUrlInterceptor : : DataType > , interceptionPoint ) ;
QFETCH ( QString , expectedChildString ) ;
QFETCH ( QString , expectedScriptString ) ;
QFETCH ( QString , expectedResolvedUrl ) ;
QFETCH ( QString , expectedAbsoluteUrl ) ;
QQmlEngine e ;
2019-07-03 15:25:55 +00:00
e . addImportPath ( testFileUrl ( " interception/imports " ) . url ( ) ) ;
2013-07-18 13:02:37 +00:00
CustomSelector cs ( testFileUrl ( " " ) ) ;
2013-03-01 01:03:43 +00:00
cs . m_interceptionPoints = interceptionPoint ;
2020-02-07 15:22:53 +00:00
e . addUrlInterceptor ( & cs ) ;
2013-03-01 01:03:43 +00:00
QQmlComponent c ( & e , testFile ) ; //Note that this can get intercepted too
2020-06-08 14:30:18 +00:00
QScopedPointer < QObject > o ( c . create ( ) ) ;
2013-03-01 01:03:43 +00:00
if ( ! o )
qDebug ( ) < < c . errorString ( ) ;
QVERIFY ( o ) ;
//Test a URL as a property initialization
2020-06-15 15:53:16 +00:00
QCOMPARE ( o - > property ( " filePath " ) . toString ( ) , QUrl ( " doesNotExist.file " ) . toString ( ) ) ;
2013-03-01 01:03:43 +00:00
//Test a URL as a Type location
QCOMPARE ( o - > property ( " childString " ) . toString ( ) , expectedChildString ) ;
//Test a URL as a Script location
QCOMPARE ( o - > property ( " scriptString " ) . toString ( ) , expectedScriptString ) ;
//Test a URL as a resolveUrl() call
QCOMPARE ( o - > property ( " resolvedUrl " ) . toString ( ) , expectedResolvedUrl ) ;
QCOMPARE ( o - > property ( " absoluteUrl " ) . toString ( ) , expectedAbsoluteUrl ) ;
}
2015-10-27 11:21:00 +00:00
void tst_qqmlengine : : qmlContextProperties ( )
{
QQmlEngine e ;
QQmlComponent c ( & e , testFileUrl ( " TypeofQmlProperty.qml " ) ) ;
2020-06-08 14:30:18 +00:00
QScopedPointer < QObject > o ( c . create ( ) ) ;
2015-10-27 11:21:00 +00:00
if ( ! o ) {
qDebug ( ) < < c . errorString ( ) ;
}
QVERIFY ( o ) ;
}
2017-04-21 10:29:22 +00:00
void tst_qqmlengine : : testGCCorruption ( )
{
QQmlEngine e ;
QQmlComponent c ( & e , testFileUrl ( " testGCCorruption.qml " ) ) ;
2020-06-08 14:30:18 +00:00
QScopedPointer < QObject > o ( c . create ( ) ) ;
2017-04-21 10:29:22 +00:00
QVERIFY2 ( o , qPrintable ( c . errorString ( ) ) ) ;
}
2017-09-14 12:38:36 +00:00
void tst_qqmlengine : : testGroupedPropertyRevisions ( )
{
QQmlEngine e ;
QQmlComponent c ( & e , testFileUrl ( " testGroupedPropertiesRevision.1.qml " ) ) ;
QScopedPointer < QObject > object ( c . create ( ) ) ;
QVERIFY2 ( object . data ( ) , qPrintable ( c . errorString ( ) ) ) ;
QQmlComponent c2 ( & e , testFileUrl ( " testGroupedPropertiesRevision.2.qml " ) ) ;
QVERIFY ( ! c2 . errorString ( ) . isEmpty ( ) ) ;
}
2018-03-02 08:09:17 +00:00
void tst_qqmlengine : : componentFromEval ( )
{
QQmlEngine engine ;
const QUrl testUrl = testFileUrl ( " EmptyComponent.qml " ) ;
QJSValue result = engine . evaluate ( " Qt.createComponent( \" " + testUrl . toString ( ) + " \" ); " ) ;
QPointer < QQmlComponent > component ( qobject_cast < QQmlComponent * > ( result . toQObject ( ) ) ) ;
QVERIFY ( ! component . isNull ( ) ) ;
QScopedPointer < QObject > item ( component - > create ( ) ) ;
QVERIFY ( ! item . isNull ( ) ) ;
}
2018-04-24 09:35:43 +00:00
void tst_qqmlengine : : qrcUrls ( )
{
QQmlEngine engine ;
QQmlEnginePrivate * pEngine = QQmlEnginePrivate : : get ( & engine ) ;
{
2018-05-04 07:28:23 +00:00
QQmlRefPointer < QQmlTypeData > oneQml ( pEngine - > typeLoader . getType ( QUrl ( " qrc:/qrcurls.qml " ) ) ) ;
QVERIFY ( oneQml . data ( ) ! = nullptr ) ;
2022-06-13 13:28:39 +00:00
QVERIFY ( ! oneQml - > backupSourceCode ( ) . isValid ( ) ) ;
2018-05-04 07:28:23 +00:00
QQmlRefPointer < QQmlTypeData > twoQml ( pEngine - > typeLoader . getType ( QUrl ( " qrc:///qrcurls.qml " ) ) ) ;
QVERIFY ( twoQml . data ( ) ! = nullptr ) ;
QCOMPARE ( oneQml . data ( ) , twoQml . data ( ) ) ;
2018-04-24 09:35:43 +00:00
}
{
2018-05-04 07:28:23 +00:00
QQmlRefPointer < QQmlTypeData > oneJS ( pEngine - > typeLoader . getType ( QUrl ( " qrc:/qrcurls.js " ) ) ) ;
QVERIFY ( oneJS . data ( ) ! = nullptr ) ;
2022-06-13 13:28:39 +00:00
QVERIFY ( ! oneJS - > backupSourceCode ( ) . isValid ( ) ) ;
2018-05-04 07:28:23 +00:00
QQmlRefPointer < QQmlTypeData > twoJS ( pEngine - > typeLoader . getType ( QUrl ( " qrc:///qrcurls.js " ) ) ) ;
QVERIFY ( twoJS . data ( ) ! = nullptr ) ;
QCOMPARE ( oneJS . data ( ) , twoJS . data ( ) ) ;
2018-04-24 09:35:43 +00:00
}
}
2018-05-29 12:27:46 +00:00
void tst_qqmlengine : : cppSignalAndEval ( )
{
ObjectCaller objectCaller ;
QQmlEngine engine ;
2019-10-04 11:49:18 +00:00
qmlRegisterSingletonInstance ( " Test " , 1 , 0 , " CallerCpp " , & objectCaller ) ;
2018-05-29 12:27:46 +00:00
QQmlComponent c ( & engine ) ;
c . setData ( " import QtQuick 2.9 \n "
2019-10-04 11:49:18 +00:00
" import Test 1.0 \n "
2018-05-29 12:27:46 +00:00
" Item { \n "
" property var r: 0 \n "
" Connections { \n "
" target: CallerCpp; \n "
2019-10-04 11:49:18 +00:00
" function onDoubleReply() { \n "
2018-05-29 12:27:46 +00:00
" eval('var z = 1'); \n "
" r = a; \n "
" } \n "
" } \n "
" } " ,
QUrl ( QStringLiteral ( " qrc:/main.qml " ) ) ) ;
QScopedPointer < QObject > object ( c . create ( ) ) ;
QVERIFY ( ! object . isNull ( ) ) ;
emit objectCaller . doubleReply ( 1.1234 ) ;
QCOMPARE ( object - > property ( " r " ) , 1.1234 ) ;
}
2018-05-31 19:41:47 +00:00
class SomeQObjectClass : public QObject {
Q_OBJECT
public :
SomeQObjectClass ( ) : QObject ( nullptr ) { }
} ;
2020-01-27 12:55:14 +00:00
class Dayfly : public QObject
{
Q_OBJECT
} ;
2018-05-31 19:41:47 +00:00
void tst_qqmlengine : : singletonInstance ( )
{
QQmlEngine engine ;
int cppSingletonTypeId = qmlRegisterSingletonType < CppSingleton > ( " Test " , 1 , 0 , " CppSingleton " , & CppSingleton : : create ) ;
int jsValueSingletonTypeId = qmlRegisterSingletonType ( " Test " , 1 , 0 , " JsSingleton " , & JsSingleton : : create ) ;
{
// Cpp QObject singleton type
QJSValue value = engine . singletonInstance < QJSValue > ( cppSingletonTypeId ) ;
QVERIFY ( ! value . isUndefined ( ) ) ;
QVERIFY ( value . isQObject ( ) ) ;
QObject * instance = value . toQObject ( ) ;
QVERIFY ( instance ) ;
QCOMPARE ( instance - > metaObject ( ) - > className ( ) , " CppSingleton " ) ;
2022-12-06 12:28:48 +00:00
QCOMPARE ( engine . singletonInstance < CppSingleton * > ( " Test " , " CppSingleton " ) , instance ) ;
2018-05-31 19:41:47 +00:00
}
{
// QJSValue QObject singleton type
QJSValue value = engine . singletonInstance < QJSValue > ( jsValueSingletonTypeId ) ;
QVERIFY ( ! value . isUndefined ( ) ) ;
QVERIFY ( value . isQObject ( ) ) ;
QObject * instance = value . toQObject ( ) ;
QVERIFY ( instance ) ;
QCOMPARE ( instance - > metaObject ( ) - > className ( ) , " JsSingleton " ) ;
}
2019-06-06 07:55:20 +00:00
{
int data = 30 ;
auto id = qmlRegisterSingletonType < CppSingleton > ( " Qt.test " , 1 , 0 , " CapturingLambda " , [ data ] ( QQmlEngine * , QJSEngine * ) { // register qobject singleton with capturing lambda
auto o = new CppSingleton ;
o - > setProperty ( " data " , data ) ;
return o ;
} ) ;
QJSValue value = engine . singletonInstance < QJSValue > ( id ) ;
QVERIFY ( ! value . isUndefined ( ) ) ;
QVERIFY ( value . isQObject ( ) ) ;
QObject * instance = value . toQObject ( ) ;
QVERIFY ( instance ) ;
QCOMPARE ( instance - > metaObject ( ) - > className ( ) , " CppSingleton " ) ;
QCOMPARE ( instance - > property ( " data " ) , data ) ;
}
{
qmlRegisterSingletonType < CppSingleton > ( " Qt.test " , 1 , 0 , " NotAmbiguous " , [ ] ( QQmlEngine * qeng , QJSEngine * jeng ) - > QObject * { return CppSingleton : : create ( qeng , jeng ) ; } ) ; // test that overloads for qmlRegisterSingleton are not ambiguous
}
2019-06-14 14:54:14 +00:00
{
// Register QObject* directly
CppSingleton single ;
int id = qmlRegisterSingletonInstance ( " Qt.test " , 1 , 0 , " CppOwned " ,
& single ) ;
QQmlEngine engine2 ;
CppSingleton * singlePtr = engine2 . singletonInstance < CppSingleton * > ( id ) ;
QVERIFY ( singlePtr ) ;
QCOMPARE ( & single , singlePtr ) ;
QVERIFY ( engine2 . objectOwnership ( singlePtr ) = = QQmlEngine : : CppOwnership ) ;
}
{
CppSingleton single ;
QQmlEngine engineA ;
QQmlEngine engineB ;
int id = qmlRegisterSingletonInstance ( " Qt.test " , 1 , 0 , " CppOwned " , & single ) ;
auto singlePtr = engineA . singletonInstance < CppSingleton * > ( id ) ;
QVERIFY ( singlePtr ) ;
singlePtr = engineA . singletonInstance < CppSingleton * > ( id ) ; // accessing the singleton multiple times from the same engine is fine
QVERIFY ( singlePtr ) ;
QTest : : ignoreMessage ( QtMsgType : : QtCriticalMsg , " <Unknown File>: qmlRegisterSingletonType(): \" CppOwned \" is not available because the callback function returns a null pointer. " ) ;
QTest : : ignoreMessage ( QtMsgType : : QtWarningMsg , " <Unknown File>: Singleton registered by registerSingletonInstance must only be accessed from one engine " ) ;
QCOMPARE ( & single , singlePtr ) ;
auto noSinglePtr = engineB . singletonInstance < CppSingleton * > ( id ) ;
QVERIFY ( ! noSinglePtr ) ;
}
{
CppSingleton single ;
QThread newThread { } ;
single . moveToThread ( & newThread ) ;
QCOMPARE ( single . thread ( ) , & newThread ) ;
QQmlEngine engineB ;
int id = qmlRegisterSingletonInstance ( " Qt.test " , 1 , 0 , " CppOwned " , & single ) ;
QTest : : ignoreMessage ( QtMsgType : : QtCriticalMsg , " <Unknown File>: qmlRegisterSingletonType(): \" CppOwned \" is not available because the callback function returns a null pointer. " ) ;
QTest : : ignoreMessage ( QtMsgType : : QtWarningMsg , " <Unknown File>: Registered object must live in the same thread as the engine it was registered with " ) ;
auto noSinglePtr = engineB . singletonInstance < CppSingleton * > ( id ) ;
QVERIFY ( ! noSinglePtr ) ;
}
2019-06-06 07:55:20 +00:00
2022-12-06 12:28:48 +00:00
// test the case where we haven't loaded the module yet
{
auto singleton = engine . singletonInstance < PurelyDeclarativeSingleton * > ( " OnlyDeclarative " , " PurelyDeclarativeSingleton " ) ;
QVERIFY ( singleton ) ;
// requesting the singleton twice yields the same result
auto again = engine . singletonInstance < PurelyDeclarativeSingleton * > ( " OnlyDeclarative " , " PurelyDeclarativeSingleton " ) ;
QCOMPARE ( again , singleton ) ;
// different engines -> different singletons
QQmlEngine engine2 ;
auto differentEngine = engine2 . singletonInstance < PurelyDeclarativeSingleton * > ( " OnlyDeclarative " , " PurelyDeclarativeSingleton " ) ;
QCOMPARE_NE ( differentEngine , singleton ) ;
}
2018-05-31 19:41:47 +00:00
{
// Invalid types
QJSValue value ;
value = engine . singletonInstance < QJSValue > ( - 4711 ) ;
QVERIFY ( value . isUndefined ( ) ) ;
value = engine . singletonInstance < QJSValue > ( 1701 ) ;
QVERIFY ( value . isUndefined ( ) ) ;
}
{
// Valid, but non-singleton type
int typeId = qmlRegisterType < CppSingleton > ( " Test " , 1 , 0 , " NotASingleton " ) ;
QJSValue value = engine . singletonInstance < QJSValue > ( typeId ) ;
QVERIFY ( value . isUndefined ( ) ) ;
}
{
// Cpp QObject singleton type
CppSingleton * instance = engine . singletonInstance < CppSingleton * > ( cppSingletonTypeId ) ;
QVERIFY ( instance ) ;
QCOMPARE ( instance - > metaObject ( ) - > className ( ) , " CppSingleton " ) ;
QCOMPARE ( instance , engine . singletonInstance < QJSValue > ( cppSingletonTypeId ) . toQObject ( ) ) ;
}
{
// Wrong destination type
SomeQObjectClass * instance = engine . singletonInstance < SomeQObjectClass * > ( cppSingletonTypeId ) ;
QVERIFY ( ! instance ) ;
}
2019-10-04 14:20:29 +00:00
{
// deleted object
2023-07-28 12:57:16 +00:00
auto dayfly = std : : make_unique < Dayfly > ( ) ;
auto id = qmlRegisterSingletonInstance ( " Vanity " , 1 , 0 , " Dayfly " , dayfly . get ( ) ) ;
dayfly . reset ( ) ;
2019-10-04 14:20:29 +00:00
QTest : : ignoreMessage ( QtMsgType : : QtWarningMsg , " <Unknown File>: The registered singleton has already been deleted. Ensure that it outlives the engine. " ) ;
QObject * instance = engine . singletonInstance < QObject * > ( id ) ;
QVERIFY ( ! instance ) ;
}
2018-05-31 19:41:47 +00:00
}
2019-03-26 12:57:33 +00:00
void tst_qqmlengine : : aggressiveGc ( )
{
const QByteArray origAggressiveGc = qgetenv ( " QV4_MM_AGGRESSIVE_GC " ) ;
qputenv ( " QV4_MM_AGGRESSIVE_GC " , " true " ) ;
{
QQmlEngine engine ; // freezing should not run into infinite recursion
QJSValue obj = engine . newObject ( ) ;
QVERIFY ( obj . isObject ( ) ) ;
}
qputenv ( " QV4_MM_AGGRESSIVE_GC " , origAggressiveGc ) ;
}
2019-05-09 10:38:22 +00:00
void tst_qqmlengine : : cachedGetterLookup_qtbug_75335 ( )
{
QQmlEngine engine ;
const QUrl testUrl = testFileUrl ( " CachedGetterLookup.qml " ) ;
QQmlComponent component ( & engine , testUrl ) ;
QVERIFY ( component . isReady ( ) ) ;
QScopedPointer < QObject > object ( component . create ( ) ) ;
QVERIFY ( object ! = nullptr ) ;
}
2020-01-10 09:01:36 +00:00
class EvilSingleton : public QObject
{
Q_OBJECT
public :
QPointer < QQmlEngine > m_engine ;
EvilSingleton ( QQmlEngine * engine ) : m_engine ( engine ) {
connect ( this , & QObject : : destroyed , this , [ this ] ( ) {
QQmlComponent component ( m_engine ) ;
component . setData ( " import QtQml 2.0 \n QtObject {} " , QUrl ( " file://Stuff.qml " ) ) ;
QVERIFY ( component . isReady ( ) ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( obj ) ;
} ) ;
}
} ;
void tst_qqmlengine : : createComponentOnSingletonDestruction ( )
{
qmlRegisterSingletonType < EvilSingleton > ( " foo.foo " , 1 , 0 , " Singleton " ,
[ ] ( QQmlEngine * engine , QJSEngine * ) {
return new EvilSingleton ( engine ) ;
} ) ;
QQmlEngine engine ;
QQmlComponent component ( & engine , testFileUrl ( " evilSingletonInstantiation.qml " ) ) ;
QVERIFY ( component . isReady ( ) ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( obj ) ;
}
2020-01-22 12:37:48 +00:00
void tst_qqmlengine : : uiLanguage ( )
{
2020-10-23 07:50:53 +00:00
{
QQmlEngine engine ;
2020-01-22 12:37:48 +00:00
2020-10-23 07:50:53 +00:00
QObject : : connect ( & engine , & QJSEngine : : uiLanguageChanged , [ & engine ] ( ) {
engine . retranslate ( ) ;
} ) ;
2020-01-22 12:37:48 +00:00
2020-10-23 07:50:53 +00:00
QSignalSpy uiLanguageChangeSpy ( & engine , SIGNAL ( uiLanguageChanged ( ) ) ) ;
2020-01-22 12:37:48 +00:00
2020-10-23 07:50:53 +00:00
QQmlComponent component ( & engine , testFileUrl ( " uiLanguage.qml " ) ) ;
2020-01-22 12:37:48 +00:00
2020-10-23 07:50:53 +00:00
QTest : : ignoreMessage ( QtMsgType : : QtWarningMsg , ( component . url ( ) . toString ( ) + " :2:1: QML QtObject: Binding loop detected for property \" textToTranslate \" " ) . toLatin1 ( ) ) ;
QScopedPointer < QObject > object ( component . create ( ) ) ;
QVERIFY ( ! object . isNull ( ) ) ;
2020-01-22 12:37:48 +00:00
2020-10-23 07:50:53 +00:00
QVERIFY ( engine . uiLanguage ( ) . isEmpty ( ) ) ;
QCOMPARE ( object - > property ( " numberOfTranslationBindingEvaluations " ) . toInt ( ) , 1 ) ;
2020-01-22 12:37:48 +00:00
2020-10-23 07:50:53 +00:00
QTest : : ignoreMessage ( QtMsgType : : QtWarningMsg , ( component . url ( ) . toString ( ) + " :2:1: QML QtObject: Binding loop detected for property \" textToTranslate \" " ) . toLatin1 ( ) ) ;
engine . setUiLanguage ( " TestLanguage " ) ;
QCOMPARE ( object - > property ( " numberOfTranslationBindingEvaluations " ) . toInt ( ) , 2 ) ;
QCOMPARE ( object - > property ( " chosenLanguage " ) . toString ( ) , " TestLanguage " ) ;
2020-01-22 12:37:48 +00:00
2020-10-23 07:50:53 +00:00
QTest : : ignoreMessage ( QtMsgType : : QtWarningMsg , ( component . url ( ) . toString ( ) + " :2:1: QML QtObject: Binding loop detected for property \" textToTranslate \" " ) . toLatin1 ( ) ) ;
engine . evaluate ( " Qt.uiLanguage = \" anotherLanguage \" " ) ;
QCOMPARE ( engine . uiLanguage ( ) , QString ( " anotherLanguage " ) ) ;
QCOMPARE ( object - > property ( " numberOfTranslationBindingEvaluations " ) . toInt ( ) , 3 ) ;
QCOMPARE ( object - > property ( " chosenLanguage " ) . toString ( ) , " anotherLanguage " ) ;
}
{
QQmlEngine engine ;
QQmlComponent component ( & engine , testFileUrl ( " uiLanguage.qml " ) ) ;
2020-01-22 12:37:48 +00:00
2020-10-23 07:50:53 +00:00
QTest : : ignoreMessage ( QtMsgType : : QtWarningMsg , ( component . url ( ) . toString ( ) + " :2:1: QML QtObject: Binding loop detected for property \" textToTranslate \" " ) . toLatin1 ( ) ) ;
QScopedPointer < QObject > object ( component . create ( ) ) ;
QVERIFY ( ! object . isNull ( ) ) ;
engine . setUiLanguage ( " TestLanguage " ) ;
QCOMPARE ( object - > property ( " chosenLanguage " ) . toString ( ) , " TestLanguage " ) ;
}
2020-01-22 12:37:48 +00:00
}
2023-04-19 12:57:13 +00:00
class I18nAwareClass : public QObject {
Q_OBJECT
QML_NAMED_ELEMENT ( I18nAware )
Q_PROPERTY ( QString text READ text NOTIFY textChanged )
signals :
void textChanged ( ) ;
public :
int counter = 0 ;
QString text ( )
{
if ( auto engine = qmlEngine ( this ) )
engine - > markCurrentFunctionAsTranslationBinding ( ) ;
return QLatin1String ( " Hello, %1 " ) . arg ( QString : : number ( counter + + ) ) ;
}
} ;
void tst_qqmlengine : : markCurrentFunctionAsTranslationBinding ( )
{
QQmlEngine engine ;
qmlRegisterTypesAndRevisions < I18nAwareClass > ( " i18ntest " , 1 ) ;
QQmlComponent comp ( & engine , testFileUrl ( " markCurrentFunctionAsTranslationBinding.qml " ) ) ;
std : : unique_ptr < QObject > root { comp . create ( ) } ;
QCOMPARE ( root - > property ( " result " ) , " Hello, 0 " ) ;
engine . retranslate ( ) ;
QCOMPARE ( root - > property ( " result " ) , " Hello, 1 " ) ;
}
2021-02-08 14:42:59 +00:00
void tst_qqmlengine : : executeRuntimeFunction ( )
{
QQmlEngine engine ;
QQmlEnginePrivate * priv = QQmlEnginePrivate : : get ( std : : addressof ( engine ) ) ;
const QUrl url = testFileUrl ( " runtimeFunctions.qml " ) ;
QQmlComponent component ( & engine , url ) ;
2022-04-08 13:49:24 +00:00
QVERIFY2 ( component . isReady ( ) , qPrintable ( component . errorString ( ) ) ) ;
2021-02-08 14:42:59 +00:00
QScopedPointer < QObject > dummy ( component . create ( ) ) ;
2022-04-08 13:49:24 +00:00
QVERIFY ( dummy ) ;
2021-02-08 14:42:59 +00:00
// getConstantValue():
2021-05-03 09:04:39 +00:00
int constant = 0 ;
void * a0 [ ] = { const_cast < void * > ( reinterpret_cast < const void * > ( std : : addressof ( constant ) ) ) } ;
QMetaType t0 [ ] = { QMetaType : : fromType < int > ( ) } ;
priv - > executeRuntimeFunction ( url , /* index = */ 0 , dummy . get ( ) , /* argc = */ 0 , a0 , t0 ) ;
2021-02-08 14:42:59 +00:00
QCOMPARE ( constant , 42 ) ;
// squareValue():
2021-05-03 09:04:39 +00:00
int squared = 0 ;
2021-02-08 14:42:59 +00:00
int x = 5 ;
2021-05-03 09:04:39 +00:00
void * a1 [ ] = { const_cast < void * > ( reinterpret_cast < const void * > ( std : : addressof ( squared ) ) ) ,
const_cast < void * > ( reinterpret_cast < const void * > ( std : : addressof ( x ) ) ) } ;
QMetaType t1 [ ] = { QMetaType : : fromType < int > ( ) , QMetaType : : fromType < int > ( ) } ;
priv - > executeRuntimeFunction ( url , /* index = */ 1 , dummy . get ( ) , /* argc = */ 1 , a1 , t1 ) ;
2021-02-08 14:42:59 +00:00
QCOMPARE ( squared , x * x ) ;
// concatenate():
2021-05-03 09:04:39 +00:00
QString concatenated ;
2021-02-08 14:42:59 +00:00
QString str1 = QStringLiteral ( " Hello " ) ; // uses "raw data" storage
QString str2 = QLatin1String ( " , Qml " ) ; // uses own QString storage
2021-05-03 09:04:39 +00:00
void * a2 [ ] = { const_cast < void * > ( reinterpret_cast < const void * > ( std : : addressof ( concatenated ) ) ) ,
2021-02-08 14:42:59 +00:00
const_cast < void * > ( reinterpret_cast < const void * > ( std : : addressof ( str1 ) ) ) ,
const_cast < void * > ( reinterpret_cast < const void * > ( std : : addressof ( str2 ) ) ) } ;
2021-05-03 09:04:39 +00:00
QMetaType t2 [ ] = { QMetaType : : fromType < QString > ( ) , QMetaType : : fromType < QString > ( ) ,
QMetaType : : fromType < QString > ( ) } ;
priv - > executeRuntimeFunction ( url , /* index = */ 2 , dummy . get ( ) , /* argc = */ 2 , a2 , t2 ) ;
2021-02-08 14:42:59 +00:00
QCOMPARE ( concatenated , str1 + str2 ) ;
2022-04-08 13:49:24 +00:00
// capture `this`:
QCOMPARE ( dummy - > property ( " foo " ) . toInt ( ) , 42 ) ;
QCOMPARE ( dummy - > property ( " bar " ) . toInt ( ) , 0 ) ;
priv - > executeRuntimeFunction ( url , /* index = */ 4 , dummy . get ( ) ) ;
QCOMPARE ( dummy - > property ( " bar " ) . toInt ( ) , 1 + 42 + 1 ) ;
QCOMPARE ( dummy - > property ( " baz " ) . toInt ( ) , - 100 ) ;
int y = 1 ;
void * a3 [ ] = { nullptr , const_cast < void * > ( reinterpret_cast < const void * > ( & y ) ) } ;
QMetaType t3 [ ] = { QMetaType : : fromType < void > ( ) , QMetaType : : fromType < int > ( ) } ;
priv - > executeRuntimeFunction ( url , /* index = */ 6 , dummy . get ( ) , 1 , a3 , t3 ) ;
QCOMPARE ( dummy - > property ( " bar " ) . toInt ( ) , - 98 ) ;
QCOMPARE ( dummy - > property ( " baz " ) . toInt ( ) , - 100 ) ;
2021-02-08 14:42:59 +00:00
}
2021-02-26 08:01:25 +00:00
class WithQProperty : public QObject
{
Q_OBJECT
Q_PROPERTY ( int foo READ foo WRITE setFoo BINDABLE fooBindable )
public :
WithQProperty ( QObject * parent = nullptr ) : QObject ( parent ) { m_foo . setValue ( 12 ) ; }
int foo ( ) const { return m_foo . value ( ) ; }
void setFoo ( int foo ) { m_foo . setValue ( foo ) ; }
QBindable < int > fooBindable ( ) { return QBindable < int > ( & m_foo ) ; }
int getFooWithCapture ( )
{
const QMetaObject * m = metaObject ( ) ;
currentEngine - > captureProperty ( this , m - > property ( m - > indexOfProperty ( " foo " ) ) ) ;
return m_foo . value ( ) ;
}
static QQmlEngine * currentEngine ;
private :
QProperty < int > m_foo ;
} ;
class WithoutQProperty : public QObject
{
Q_OBJECT
Q_PROPERTY ( int foo READ foo WRITE setFoo NOTIFY fooChanged )
public :
WithoutQProperty ( QObject * parent = nullptr ) : QObject ( parent ) , m_foo ( new WithQProperty ( this ) ) { }
int foo ( ) const { return m_foo - > getFooWithCapture ( ) ; }
void setFoo ( int foo ) {
if ( foo ! = m_foo - > foo ( ) ) {
m_foo - > setFoo ( foo ) ;
emit fooChanged ( ) ;
}
}
void triggerBinding ( int val )
{
m_foo - > setFoo ( val ) ;
}
signals :
void fooChanged ( ) ;
private :
WithQProperty * m_foo ;
} ;
QQmlEngine * WithQProperty : : currentEngine = nullptr ;
void tst_qqmlengine : : captureQProperty ( )
{
qmlRegisterType < WithoutQProperty > ( " Foo " , 1 , 0 , " WithoutQProperty " ) ;
QQmlEngine engine ;
WithQProperty : : currentEngine = & engine ;
QQmlComponent c ( & engine ) ;
c . setData ( " import Foo \n "
" WithoutQProperty { \n "
" property int x: foo \n "
" } " , QUrl ( ) ) ;
QVERIFY2 ( c . isReady ( ) , c . errorString ( ) . toUtf8 ( ) ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
QCOMPARE ( o - > property ( " x " ) . toInt ( ) , 12 ) ;
static_cast < WithoutQProperty * > ( o . data ( ) ) - > triggerBinding ( 13 ) ;
QCOMPARE ( o - > property ( " x " ) . toInt ( ) , 13 ) ;
}
2021-03-17 14:08:22 +00:00
void tst_qqmlengine : : listWrapperAsListReference ( )
{
QQmlEngine engine ;
QQmlComponent c ( & engine ) ;
c . setData ( " import QtQml \n QtObject { \n property list<QtObject> c: [ QtObject {} ] \n } " , QUrl ( ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
QJSManagedValue m = engine . toManagedValue ( o . data ( ) ) ;
QJSValue prop = m . property ( " c " ) ;
const QQmlListReference ref = qjsvalue_cast < QQmlListReference > ( prop ) ;
QCOMPARE ( ref . size ( ) , 1 ) ;
}
void tst_qqmlengine : : attachedObjectAsObject ( )
{
QQmlEngine engine ;
QQmlComponent c ( & engine ) ;
c . setData ( " import QtQml \n QtObject { property var a: Component } " , QUrl ( ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
QJSManagedValue m = engine . toManagedValue ( o . data ( ) ) ;
QJSValue prop = m . property ( " a " ) ;
const QQmlComponentAttached * attached = qjsvalue_cast < QQmlComponentAttached * > ( prop ) ;
QCOMPARE ( attached , qmlAttachedPropertiesObject < QQmlComponent > ( o . data ( ) ) ) ;
}
2021-03-23 13:16:15 +00:00
class WithListProperty : public QObject
{
Q_OBJECT
Q_PROPERTY ( QQmlListProperty < QQmlComponent > components READ components CONSTANT )
QML_ELEMENT
public :
QQmlListProperty < QQmlComponent > components ( )
{
return QQmlListProperty < QQmlComponent > ( this , & m_components ) ;
}
private :
QList < QQmlComponent * > m_components ;
} ;
void tst_qqmlengine : : listPropertyAsQJSValue ( )
{
qmlRegisterTypesAndRevisions < WithListProperty > ( " Foo " , 1 ) ;
QQmlEngine engine ;
QQmlComponent c ( & engine ) ;
c . setData ( " import Foo \n WithListProperty {} " , QUrl ( ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
WithListProperty * parent = qobject_cast < WithListProperty * > ( o . data ( ) ) ;
QVERIFY ( parent ) ;
QQmlListProperty < QQmlComponent > prop = parent - > components ( ) ;
QJSValue val = engine . toScriptValue ( prop ) ;
QQmlListReference ref = engine . fromScriptValue < QQmlListReference > ( val ) ;
ref . append ( & c ) ;
QCOMPARE ( prop . count ( & prop ) , 1 ) ;
QCOMPARE ( prop . at ( & prop , 0 ) , & c ) ;
}
2021-09-03 12:35:26 +00:00
void tst_qqmlengine : : stringToColor ( )
{
QQmlEngine engine ;
// Make it import QtQuick, so that color becomes available.
QQmlComponent c ( & engine ) ;
c . setData ( " import QtQuick \n Item {} " , QUrl ( ) ) ;
QVERIFY ( c . isReady ( ) ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
const QMetaType metaType ( QMetaType : : QColor ) ;
QVariant color ( metaType ) ;
2022-09-12 12:26:29 +00:00
QVERIFY ( QV4 : : ExecutionEngine : : metaTypeFromJS (
2021-09-03 12:35:26 +00:00
engine . handle ( ) - > newString ( QStringLiteral ( " #abcdef " ) ) - > asReturnedValue ( ) ,
metaType , color . data ( ) ) ) ;
QVERIFY ( color . isValid ( ) ) ;
QCOMPARE ( color . metaType ( ) , metaType ) ;
QVariant variant ( QStringLiteral ( " #abcdef " ) ) ;
QVERIFY ( variant . convert ( metaType ) ) ;
QCOMPARE ( variant . metaType ( ) , metaType ) ;
QCOMPARE ( color , variant ) ;
}
2022-03-25 09:56:41 +00:00
class WithToString : public QObject
{
Q_OBJECT
QML_ELEMENT
public :
Q_INVOKABLE QString toString ( ) const { return QStringLiteral ( " things " ) ; }
} ;
class WithToNumber : public QObject
{
Q_OBJECT
QML_ELEMENT
public :
Q_INVOKABLE int toString ( ) const { return 4 ; }
} ;
void tst_qqmlengine : : qobjectToString ( )
{
qmlRegisterTypesAndRevisions < WithToString > ( " WithToString " , 1 ) ;
qmlRegisterTypesAndRevisions < WithToNumber > ( " WithToString " , 1 ) ;
QQmlEngine engine ;
QQmlComponent c ( & engine ) ;
c . setData ( R " (
import WithToString
import QtQml
WithToString {
id : self
property QtObject weird : WithToNumber { }
objectName : toString ( ) + ' ' + self . toString ( ) + ' ' + weird . toString ( )
}
) " , QUrl());
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
QCOMPARE ( o - > objectName ( ) , QStringLiteral ( " things things 4 " ) ) ;
}
2022-06-24 07:56:01 +00:00
void tst_qqmlengine : : qtNamespaceInQtObject ( )
{
QQmlEngine engine ;
QJSValue qtObject = engine . globalObject ( ) . property ( QStringLiteral ( " Qt " ) ) ;
// Qt namespace enums are there.
QCOMPARE ( qtObject . property ( QStringLiteral ( " Checked " ) ) . toInt ( ) , 2 ) ;
// QtObject methods are also there.
QVERIFY ( qtObject . property ( QStringLiteral ( " rect " ) ) . isCallable ( ) ) ;
// QObject is also there.
QVERIFY ( qtObject . hasProperty ( QStringLiteral ( " objectName " ) ) ) ;
}
2022-08-25 09:42:23 +00:00
void tst_qqmlengine : : nativeModuleImport ( )
{
QQmlEngine engine ;
QJSValue name ( " TheName " ) ;
QJSValue obj = engine . newObject ( ) ;
obj . setProperty ( " name " , name ) ;
engine . registerModule ( " info.mjs " , obj ) ;
QQmlComponent c ( & engine , testFileUrl ( " nativeModuleImport.qml " ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
QCOMPARE ( o - > property ( " a " ) . toString ( ) , QStringLiteral ( " Hello World " ) ) ;
QCOMPARE ( o - > property ( " b " ) . toString ( ) , QStringLiteral ( " TheName " ) ) ;
QCOMPARE ( o - > property ( " c " ) . toString ( ) , QStringLiteral ( " TheName " ) ) ;
QCOMPARE ( o - > property ( " d " ) . toString ( ) , QStringLiteral ( " TheName " ) ) ;
QCOMPARE ( o - > property ( " e " ) . toString ( ) , QStringLiteral ( " TheName " ) ) ;
}
Allow limited extensions to globals
We can allow
a, overriding data members of globals, such as Error.name
b, adding members that don't clash with any internals
c, any manipulation of toString(), toLocaleString(), valueOf(),
and constructor
To that effect, add a "Locked" flag to our internal classes. If that is
set, disallow changing prototypes and when defining a property, check if
it shadows any non-configurable property. Furthermore, make all
non-primitive properties that are not meant to be overridden
non-configurable and non-writable.
constructor, toString(), toLocaleString() and valueOf() are exempt
because they are explicitly meant to be overridden by users. Therefore,
we let that happen and refrain from optimizing them or triggering their
implicit invocation in optimized code.
[ChangeLog][QtQml][Important Behavior Changes] The JavaScript global
objects are not frozen anymore in a QML engine. Instead, they are
selectively locked. You can extend the objects with new members as long
as you don't shadow any existing methods, and you can change or override
data members. This also means that most methods of Object.prototype,
which was previously exempt from the freezing, cannot be changed
anymore. You can, however, change or override constructor, toString(),
toLocaleString() and valueOf() on any prototype. Those are clearly meant
to be overridden by user code.
Fixes: QTBUG-101298
Task-number: QTBUG-84341
Change-Id: Id77db971f76c8f48b18e7a93607da5f947ecfc3e
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2022-08-29 10:00:52 +00:00
void tst_qqmlengine : : lockedRootObject ( )
{
QQmlEngine engine ;
QQmlComponent c ( & engine , testFileUrl ( " lockedRootObject.qml " ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
QTest : : ignoreMessage (
QtWarningMsg , " You cannot shadow the locked property 'hasOwnProperty' in QML. " ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
QCOMPARE ( o - > property ( " myErrorName " ) . toString ( ) , QStringLiteral ( " MyError1 " ) ) ;
QCOMPARE ( o - > property ( " errorName " ) . toString ( ) , QStringLiteral ( " MyError2 " ) ) ;
QCOMPARE ( o - > property ( " mathMax " ) . toInt ( ) , 4 ) ;
QCOMPARE ( o - > property ( " extendGlobal " ) . toInt ( ) , 32 ) ;
QCOMPARE ( o - > property ( " prototypeTrick " ) . toString ( ) , QStringLiteral ( " SyntaxError " ) ) ;
QCOMPARE ( o - > property ( " shadowMethod1 " ) . toString ( ) , QStringLiteral ( " not a TypeError " ) ) ;
QCOMPARE ( o - > property ( " shadowMethod2 " ) . toBool ( ) , false ) ;
QCOMPARE ( o - > property ( " changeObjectProto1 " ) . toString ( ) , QStringLiteral ( " not an Object " ) ) ;
QCOMPARE ( o - > property ( " changeObjectProto2 " ) . toBool ( ) , false ) ;
QCOMPARE ( o - > property ( " defineProperty1 " ) . toString ( ) , QStringLiteral ( " not a URIError " ) ) ;
QCOMPARE ( o - > property ( " defineProperty2 " ) . toBool ( ) , false ) ;
}
2023-03-22 12:01:24 +00:00
void tst_qqmlengine : : crossReferencingSingletonsDeletion ( )
{
QQmlEngine engine ;
engine . addImportPath ( testFileUrl ( " crossReferencingSingletonsDeletion " ) . url ( ) ) ;
QQmlComponent c ( & engine , testFileUrl ( " crossReferencingSingletonsDeletion/Module/Main.qml " ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
std : : unique_ptr < QObject > o { c . create ( ) } ;
QVERIFY ( o ) ;
QCOMPARE ( o - > property ( " s " ) . toString ( ) , " SingletonA " ) ;
}
2023-09-28 08:36:46 +00:00
void tst_qqmlengine : : bindingInstallUseAfterFree ( )
{
QQmlEngine engine ;
QQmlComponent c ( & engine , testFileUrl ( " bindingInstallUseAfterFree.qml " ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
std : : unique_ptr < QObject > o { c . create ( ) } ;
QVERIFY ( o ) ;
}
2024-04-08 13:28:12 +00:00
void tst_qqmlengine : : variantListQJsonConversion ( )
{
QQmlEngine engine ;
QQmlComponent c ( & engine , testFileUrl ( " variantListQJsonConversion.qml " ) ) ;
QVERIFY2 ( c . isReady ( ) , qPrintable ( c . errorString ( ) ) ) ;
QTest : : ignoreMessage ( QtMsgType : : QtDebugMsg , R " ([ " cpp " , " variant " , " list " ]) " ) ;
QTest : : ignoreMessage ( QtMsgType : : QtDebugMsg , R " ({ " test " :[ " cpp " , " variant " , " list " ]}) " ) ;
QTest : : ignoreMessage ( QtMsgType : : QtDebugMsg ,
R " ([{ " objectName " : " o0 " },{ " objectName " : " o1 " },{ " objectName " : " o2 " }]) " ) ;
QScopedPointer < QObject > o ( c . create ( ) ) ;
QVERIFY ( o ) ;
}
2012-02-16 04:43:03 +00:00
QTEST_MAIN ( tst_qqmlengine )
2011-04-27 10:05:43 +00:00
2012-02-16 04:43:03 +00:00
# include "tst_qqmlengine.moc"