2011-04-27 10:05:43 +00:00
/****************************************************************************
* *
2016-01-19 11:23:05 +00:00
* * Copyright ( C ) 2016 The Qt Company Ltd .
* * Contact : https : //www.qt.io/licensing/
2011-04-27 10:05:43 +00:00
* *
* * This file is part of the test suite of the Qt Toolkit .
* *
2016-01-19 11:23:05 +00:00
* * $ QT_BEGIN_LICENSE : GPL - EXCEPT $
2012-09-20 05:21:40 +00:00
* * Commercial License Usage
* * Licensees holding valid commercial Qt licenses may use this file in
* * accordance with the commercial license agreement provided with the
* * Software or , alternatively , in accordance with the terms contained in
2015-01-28 11:55:39 +00:00
* * a written agreement between you and The Qt Company . For licensing terms
2016-01-19 11:23:05 +00:00
* * and conditions see https : //www.qt.io/terms-conditions. For further
* * information use the contact form at https : //www.qt.io/contact-us.
2012-09-20 05:21:40 +00:00
* *
2016-01-19 11:23:05 +00:00
* * GNU General Public License Usage
* * Alternatively , this file may be used under the terms of the GNU
* * General Public License version 3 as published by the Free Software
* * Foundation with exceptions as appearing in the file LICENSE . GPL3 - EXCEPT
* * included in the packaging of this file . Please review the following
* * information to ensure the GNU General Public License requirements will
* * be met : https : //www.gnu.org/licenses/gpl-3.0.html.
2011-04-27 10:05:43 +00:00
* *
* * $ QT_END_LICENSE $
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-05-03 22:32:45 +00:00
# include "../../shared/util.h"
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>
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>
2013-12-03 16:41:04 +00:00
# include <QQmlAbstractUrlInterceptor>
2011-04-27 10:05:43 +00:00
2012-05-03 22:32:45 +00:00
class tst_qqmlengine : public QQmlDataTest
2011-04-27 10:05:43 +00:00
{
Q_OBJECT
public :
2012-02-16 04:43:03 +00:00
tst_qqmlengine ( ) { }
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 ( ) ;
void networkAccessManager ( ) ;
2013-02-28 17:13:23 +00:00
void synchronousNetworkAccessManager ( ) ;
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 ( ) ;
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 ( ) ;
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
} ;
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
}
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
QNetworkAccessManager * create ( QObject * parent ) {
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
{
2012-02-16 04:43:03 +00:00
QQmlEngine * engine = new 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
delete engine ;
// Test factory created manager
2012-02-16 04:43:03 +00:00
engine = 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
delete engine ;
}
2013-02-28 17:13:23 +00:00
class ImmediateReply : public QNetworkReply {
Q_OBJECT
public :
ImmediateReply ( ) {
setFinished ( true ) ;
}
virtual qint64 readData ( char * , qint64 ) {
return 0 ;
}
virtual void abort ( ) { }
} ;
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
}
2018-02-21 09:41:54 +00:00
QNetworkReply * createRequest ( Operation , const QNetworkRequest & , QIODevice * outgoingData = nullptr ) {
2013-02-28 17:13:23 +00:00
Q_UNUSED ( outgoingData ) ;
return new ImmediateReply ;
}
} ;
class ImmediateFactory : public QQmlNetworkAccessManagerFactory {
public :
QNetworkAccessManager * create ( QObject * ) {
return new ImmediateManager ;
}
} ;
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 ( ) ) ;
}
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
{
2012-02-16 04:43:03 +00:00
QQmlEngine * engine = new 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
2018-02-21 09:41:54 +00:00
delete engine ; engine = nullptr ;
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
2012-04-19 03:50:39 +00:00
QString dataLocation = QStandardPaths : : writableLocation ( QStandardPaths : : DataLocation ) ;
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 ( ) ) ;
engine . setOfflineStoragePath ( QDir : : homePath ( ) ) ;
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 ;
QString dataLocation = QStandardPaths : : writableLocation ( QStandardPaths : : DataLocation ) ;
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 ) ;
2011-04-27 10:05:43 +00:00
QObject * obj = component . create ( ) ;
2018-02-21 09:41:54 +00:00
QVERIFY ( obj ! = nullptr ) ;
2011-04-27 10:05:43 +00:00
QCOMPARE ( obj - > property ( " test " ) . toInt ( ) , 10 ) ;
delete obj ;
}
// 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 ) ;
2011-04-27 10:05:43 +00:00
QObject * obj = component . create ( ) ;
2018-02-21 09:41:54 +00:00
QVERIFY ( obj ! = nullptr ) ;
2011-04-27 10:05:43 +00:00
QCOMPARE ( obj - > property ( " test " ) . toInt ( ) , 10 ) ;
delete obj ;
}
// Clear cache
engine . clearComponentCache ( ) ;
// Test cache refresh
{
2017-05-31 13:54:15 +00:00
QQmlComponent component ( & engine , fileUrl ) ;
2011-04-27 10:05:43 +00:00
QObject * obj = component . create ( ) ;
2018-02-21 09:41:54 +00:00
QVERIFY ( obj ! = nullptr ) ;
2011-04-27 10:05:43 +00:00
QCOMPARE ( obj - > property ( " test " ) . toInt ( ) , 11 ) ;
delete obj ;
}
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.
engine - > collectGarbage ( ) ;
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 :
virtual void timerEvent ( QTimerEvent * )
{
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.
foreach ( const QString & test , ( QStringList ( ) < < " EmptyComponent "
< < " VMEComponent "
< < " EmptyExtendEmptyComponent "
< < " VMEExtendEmptyComponent "
< < " EmptyExtendVMEComponent "
< < " VMEExtendVMEComponent "
< < " EmptyAggregateEmptyComponent "
< < " VMEAggregateEmptyComponent "
< < " EmptyAggregateVMEComponent "
< < " VMEAggregateVMEComponent "
< < " EmptyPropertyEmptyComponent "
< < " VMEPropertyEmptyComponent "
< < " EmptyPropertyVMEComponent "
< < " VMEPropertyVMEComponent "
< < " VMETransientEmptyComponent "
< < " VMETransientVMEComponent " ) ) {
// 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 " ;
}
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
2012-12-18 10:41:06 +00:00
QCOMPARE ( messageHandler . messages ( ) . count ( ) , 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
}
QTRY_VERIFY ( spy . count ( ) ) ;
}
{
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
}
QTRY_VERIFY ( spy . count ( ) ) ;
}
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 ) ;
foreach ( const QString & w , expectedWarnings )
QTest : : ignoreMessage ( QtWarningMsg , qPrintable ( w ) ) ;
QQmlEngine e ;
QQmlComponent c ( & e , testFile ) ;
if ( expectedError . isEmpty ( ) ) {
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 ) { }
2013-03-01 01:03:43 +00:00
virtual QUrl intercept ( const QUrl & url , QQmlAbstractUrlInterceptor : : DataType d )
{
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 ( )
{
2017-08-30 12:39:28 +00:00
# ifdef SKIP_GCCORRUPTION_TEST
QSKIP ( " Test too heavy for qemu " ) ;
# endif
2017-04-21 10:29:22 +00:00
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 ) ;
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 ) ;
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
class ObjectCaller : public QObject
{
Q_OBJECT
signals :
void doubleReply ( const double a ) ;
} ;
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 CppSingleton : public QObject {
Q_OBJECT
public :
CppSingleton ( ) { }
static QObject * create ( QQmlEngine * qmlEngine , QJSEngine * jsEngine )
{
Q_UNUSED ( qmlEngine ) ;
Q_UNUSED ( jsEngine ) ;
return new CppSingleton ( ) ;
}
} ;
class JsSingleton : public QObject {
Q_OBJECT
public :
JsSingleton ( ) { }
static QJSValue create ( QQmlEngine * qmlEngine , QJSEngine * jsEngine )
{
Q_UNUSED ( qmlEngine ) ;
QJSValue value = jsEngine - > newQObject ( new JsSingleton ( ) ) ;
return value ;
}
} ;
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 " ) ;
}
{
// 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
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
2020-01-27 12:55:14 +00:00
auto dayfly = new Dayfly { } ;
2019-10-04 14:20:29 +00:00
auto id = qmlRegisterSingletonInstance ( " Vanity " , 1 , 0 , " Dayfly " , dayfly ) ;
delete dayfly ;
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
}
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"