2016-06-06 09:46:01 +00:00
/****************************************************************************
* *
* * Copyright ( C ) 2016 The Qt Company Ltd .
* * Contact : https : //www.qt.io/licensing/
* *
* * This file is part of the test suite of the Qt Toolkit .
* *
* * $ QT_BEGIN_LICENSE : GPL - EXCEPT $
* * 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
* * a written agreement between you and The Qt Company . For licensing terms
* * and conditions see https : //www.qt.io/terms-conditions. For further
* * information use the contact form at https : //www.qt.io/contact-us.
* *
* * 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.
* *
* * $ QT_END_LICENSE $
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <qtest.h>
# include <private/qv4compileddata_p.h>
2016-07-21 14:30:57 +00:00
# include <private/qv4compiler_p.h>
# include <private/qv4jsir_p.h>
# include <private/qv4isel_p.h>
# include <private/qv8engine_p.h>
# include <private/qv4engine_p.h>
2016-06-06 09:46:01 +00:00
# include <QQmlComponent>
# include <QQmlEngine>
2016-08-01 13:57:53 +00:00
# include <QQmlFileSelector>
2016-07-19 08:13:26 +00:00
# include <QThread>
2016-08-07 12:53:57 +00:00
# include <QCryptographicHash>
# include <QStandardPaths>
# include <QDirIterator>
2016-06-06 09:46:01 +00:00
class tst_qmldiskcache : public QObject
{
Q_OBJECT
private slots :
void initTestCase ( ) ;
void regenerateAfterChange ( ) ;
2016-07-07 11:11:08 +00:00
void registerImportForImplicitComponent ( ) ;
2016-07-21 14:30:57 +00:00
void basicVersionChecks ( ) ;
2016-07-28 15:40:36 +00:00
void recompileAfterChange ( ) ;
2017-01-02 09:46:38 +00:00
void recompileAfterDirectoryChange ( ) ;
2016-08-01 13:57:53 +00:00
void fileSelectors ( ) ;
2016-08-05 19:10:17 +00:00
void localAliases ( ) ;
2016-08-07 12:53:57 +00:00
void cacheResources ( ) ;
2016-10-16 15:54:59 +00:00
void stableOrderOfDependentCompositeTypes ( ) ;
2016-06-06 09:46:01 +00:00
} ;
2016-07-28 15:40:36 +00:00
// A wrapper around QQmlComponent to ensure the temporary reference counts
// on the type data as a result of the main thread <> loader thread communication
// are dropped. 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. A plain sendPostedEvents() however is insufficient because
// we can't be sure that the event is posted after the constructor finished.
class CleanlyLoadingComponent : public QQmlComponent
{
public :
CleanlyLoadingComponent ( QQmlEngine * engine , const QUrl & url )
: QQmlComponent ( engine , url , QQmlComponent : : Asynchronous )
{ waitForLoad ( ) ; }
CleanlyLoadingComponent ( QQmlEngine * engine , const QString & fileName )
: QQmlComponent ( engine , fileName , QQmlComponent : : Asynchronous )
{ waitForLoad ( ) ; }
void waitForLoad ( )
{
QTRY_VERIFY ( status ( ) = = QQmlComponent : : Ready | | status ( ) = = QQmlComponent : : Error ) ;
}
} ;
static void waitForFileSystem ( )
{
// 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 ) ;
}
2016-06-06 09:46:01 +00:00
struct TestCompiler
{
TestCompiler ( QQmlEngine * engine )
: engine ( engine )
, tempDir ( )
, currentMapping ( nullptr )
{
2017-01-02 09:46:38 +00:00
init ( tempDir . path ( ) ) ;
}
void init ( const QString & baseDirectory )
{
closeMapping ( ) ;
testFilePath = baseDirectory + QStringLiteral ( " /test.qml " ) ;
cacheFilePath = baseDirectory + QStringLiteral ( " /test.qmlc " ) ;
mappedFile . setFileName ( cacheFilePath ) ;
2016-06-06 09:46:01 +00:00
}
bool compile ( const QByteArray & contents )
{
2016-07-21 14:30:57 +00:00
closeMapping ( ) ;
engine - > clearComponentCache ( ) ;
2016-06-06 09:46:01 +00:00
2016-07-28 15:40:36 +00:00
waitForFileSystem ( ) ;
2016-06-06 09:46:01 +00:00
{
QFile f ( testFilePath ) ;
if ( ! f . open ( QIODevice : : WriteOnly | QIODevice : : Truncate ) ) {
lastErrorString = f . errorString ( ) ;
return false ;
}
if ( f . write ( contents ) ! = contents . size ( ) ) {
lastErrorString = f . errorString ( ) ;
return false ;
}
}
2016-07-28 15:40:36 +00:00
CleanlyLoadingComponent component ( engine , testFilePath ) ;
2016-06-06 09:46:01 +00:00
if ( ! component . isReady ( ) ) {
lastErrorString = component . errorString ( ) ;
return false ;
}
return true ;
}
const QV4 : : CompiledData : : Unit * mapUnit ( )
{
if ( ! mappedFile . open ( QIODevice : : ReadOnly ) ) {
lastErrorString = mappedFile . errorString ( ) ;
return nullptr ;
}
currentMapping = mappedFile . map ( /*offset*/ 0 , mappedFile . size ( ) ) ;
if ( ! currentMapping ) {
lastErrorString = mappedFile . errorString ( ) ;
return nullptr ;
}
QV4 : : CompiledData : : Unit * unitPtr ;
memcpy ( & unitPtr , & currentMapping , sizeof ( unitPtr ) ) ;
return unitPtr ;
}
2016-07-21 14:30:57 +00:00
typedef void ( * HeaderTweakFunction ) ( QV4 : : CompiledData : : Unit * header ) ;
bool tweakHeader ( HeaderTweakFunction function )
{
closeMapping ( ) ;
QFile f ( cacheFilePath ) ;
if ( ! f . open ( QIODevice : : ReadWrite ) )
return false ;
QV4 : : CompiledData : : Unit header ;
if ( f . read ( reinterpret_cast < char * > ( & header ) , sizeof ( header ) ) ! = sizeof ( header ) )
return false ;
function ( & header ) ;
f . seek ( 0 ) ;
return f . write ( reinterpret_cast < const char * > ( & header ) , sizeof ( header ) ) = = sizeof ( header ) ;
}
bool verify ( )
{
QV4 : : ExecutionEngine * v4 = QV8Engine : : getV4 ( engine ) ;
QQmlRefPointer < QV4 : : CompiledData : : CompilationUnit > unit = v4 - > iselFactory - > createUnitForLoading ( ) ;
2017-03-16 08:44:50 +00:00
return unit - > loadFromDisk ( QUrl : : fromLocalFile ( testFilePath ) , QFileInfo ( testFilePath ) . lastModified ( ) , v4 - > iselFactory . data ( ) , & lastErrorString ) ;
2016-07-21 14:30:57 +00:00
}
void closeMapping ( )
{
if ( currentMapping ) {
mappedFile . unmap ( currentMapping ) ;
currentMapping = nullptr ;
}
mappedFile . close ( ) ;
}
void clearCache ( )
{
closeMapping ( ) ;
QFile : : remove ( cacheFilePath ) ;
}
2016-06-06 09:46:01 +00:00
QQmlEngine * engine ;
const QTemporaryDir tempDir ;
2017-01-02 09:46:38 +00:00
QString testFilePath ;
QString cacheFilePath ;
2016-06-06 09:46:01 +00:00
QString lastErrorString ;
QFile mappedFile ;
uchar * currentMapping ;
} ;
void tst_qmldiskcache : : initTestCase ( )
{
2016-08-03 11:50:44 +00:00
qputenv ( " QML_FORCE_DISK_CACHE " , " 1 " ) ;
2016-06-06 09:46:01 +00:00
}
void tst_qmldiskcache : : regenerateAfterChange ( )
{
QQmlEngine engine ;
TestCompiler testCompiler ( & engine ) ;
QVERIFY ( testCompiler . tempDir . isValid ( ) ) ;
const QByteArray contents = QByteArrayLiteral ( " import QtQml 2.0 \n "
" QtObject { \n "
" property string blah: Qt.platform; \n "
" } " ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
{
const QV4 : : CompiledData : : Unit * testUnit = testCompiler . mapUnit ( ) ;
QVERIFY2 ( testUnit , qPrintable ( testCompiler . lastErrorString ) ) ;
2016-07-12 12:19:09 +00:00
QCOMPARE ( quint32 ( testUnit - > nObjects ) , quint32 ( 1 ) ) ;
2016-06-06 09:46:01 +00:00
const QV4 : : CompiledData : : Object * obj = testUnit - > objectAt ( 0 ) ;
2016-07-12 12:19:09 +00:00
QCOMPARE ( quint32 ( obj - > nBindings ) , quint32 ( 1 ) ) ;
QCOMPARE ( quint32 ( obj - > bindingTable ( ) - > type ) , quint32 ( QV4 : : CompiledData : : Binding : : Type_Script ) ) ;
QCOMPARE ( quint32 ( obj - > bindingTable ( ) - > value . compiledScriptIndex ) , quint32 ( 1 ) ) ;
2016-06-06 09:46:01 +00:00
2016-07-12 12:19:09 +00:00
QCOMPARE ( quint32 ( testUnit - > functionTableSize ) , quint32 ( 2 ) ) ;
2016-06-06 09:46:01 +00:00
const QV4 : : CompiledData : : Function * bindingFunction = testUnit - > functionAt ( 1 ) ;
QVERIFY ( bindingFunction - > codeOffset > testUnit - > unitSize ) ;
}
{
const QByteArray newContents = QByteArrayLiteral ( " import QtQml 2.0 \n "
" QtObject { \n "
" property string blah: Qt.platform; \n "
" property int secondProperty: 42; \n "
" } " ) ;
QVERIFY2 ( testCompiler . compile ( newContents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
const QV4 : : CompiledData : : Unit * testUnit = testCompiler . mapUnit ( ) ;
QVERIFY2 ( testUnit , qPrintable ( testCompiler . lastErrorString ) ) ;
2016-07-12 12:19:09 +00:00
QCOMPARE ( quint32 ( testUnit - > nObjects ) , quint32 ( 1 ) ) ;
2016-06-06 09:46:01 +00:00
const QV4 : : CompiledData : : Object * obj = testUnit - > objectAt ( 0 ) ;
2016-07-12 12:19:09 +00:00
QCOMPARE ( quint32 ( obj - > nBindings ) , quint32 ( 2 ) ) ;
QCOMPARE ( quint32 ( obj - > bindingTable ( ) - > type ) , quint32 ( QV4 : : CompiledData : : Binding : : Type_Number ) ) ;
QCOMPARE ( obj - > bindingTable ( ) - > valueAsNumber ( ) , double ( 42 ) ) ;
2016-06-06 09:46:01 +00:00
2016-07-12 12:19:09 +00:00
QCOMPARE ( quint32 ( testUnit - > functionTableSize ) , quint32 ( 2 ) ) ;
2016-06-06 09:46:01 +00:00
const QV4 : : CompiledData : : Function * bindingFunction = testUnit - > functionAt ( 1 ) ;
QVERIFY ( bindingFunction - > codeOffset > testUnit - > unitSize ) ;
}
}
2016-07-07 11:11:08 +00:00
void tst_qmldiskcache : : registerImportForImplicitComponent ( )
{
QQmlEngine engine ;
TestCompiler testCompiler ( & engine ) ;
QVERIFY ( testCompiler . tempDir . isValid ( ) ) ;
const QByteArray contents = QByteArrayLiteral ( " import QtQuick 2.0 \n "
" Loader { \n "
" sourceComponent: Item {} \n "
" } " ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
{
const QV4 : : CompiledData : : Unit * testUnit = testCompiler . mapUnit ( ) ;
QVERIFY2 ( testUnit , qPrintable ( testCompiler . lastErrorString ) ) ;
QCOMPARE ( quint32 ( testUnit - > nImports ) , quint32 ( 2 ) ) ;
QCOMPARE ( testUnit - > stringAt ( testUnit - > importAt ( 0 ) - > uriIndex ) , QStringLiteral ( " QtQuick " ) ) ;
QQmlType * componentType = QQmlMetaType : : qmlType ( & QQmlComponent : : staticMetaObject ) ;
QCOMPARE ( testUnit - > stringAt ( testUnit - > importAt ( 1 ) - > uriIndex ) , QString ( componentType - > module ( ) ) ) ;
QCOMPARE ( testUnit - > stringAt ( testUnit - > importAt ( 1 ) - > qualifierIndex ) , QStringLiteral ( " QmlInternals " ) ) ;
QCOMPARE ( quint32 ( testUnit - > nObjects ) , quint32 ( 3 ) ) ;
const QV4 : : CompiledData : : Object * obj = testUnit - > objectAt ( 0 ) ;
QCOMPARE ( quint32 ( obj - > nBindings ) , quint32 ( 1 ) ) ;
QCOMPARE ( quint32 ( obj - > bindingTable ( ) - > type ) , quint32 ( QV4 : : CompiledData : : Binding : : Type_Object ) ) ;
const QV4 : : CompiledData : : Object * implicitComponent = testUnit - > objectAt ( obj - > bindingTable ( ) - > value . objectIndex ) ;
QCOMPARE ( testUnit - > stringAt ( implicitComponent - > inheritedTypeNameIndex ) , QStringLiteral ( " QmlInternals. " ) + componentType - > elementName ( ) ) ;
}
}
2016-07-21 14:30:57 +00:00
void tst_qmldiskcache : : basicVersionChecks ( )
{
QQmlEngine engine ;
TestCompiler testCompiler ( & engine ) ;
QVERIFY ( testCompiler . tempDir . isValid ( ) ) ;
const QByteArray contents = QByteArrayLiteral ( " import QtQml 2.0 \n "
" QtObject { \n "
" property string blah: Qt.platform; \n "
" } " ) ;
{
testCompiler . clearCache ( ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
QVERIFY2 ( testCompiler . verify ( ) , qPrintable ( testCompiler . lastErrorString ) ) ;
}
{
testCompiler . clearCache ( ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
testCompiler . tweakHeader ( [ ] ( QV4 : : CompiledData : : Unit * header ) {
header - > qtVersion = 0 ;
} ) ;
QVERIFY ( ! testCompiler . verify ( ) ) ;
QCOMPARE ( testCompiler . lastErrorString , QString : : fromUtf8 ( " Qt version mismatch. Found 0 expected %1 " ) . arg ( QT_VERSION , 0 , 16 ) ) ;
testCompiler . clearCache ( ) ;
}
{
testCompiler . clearCache ( ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
testCompiler . tweakHeader ( [ ] ( QV4 : : CompiledData : : Unit * header ) {
header - > version = 0 ;
} ) ;
QVERIFY ( ! testCompiler . verify ( ) ) ;
QCOMPARE ( testCompiler . lastErrorString , QString : : fromUtf8 ( " V4 data structure version mismatch. Found 0 expected %1 " ) . arg ( QV4_DATA_STRUCTURE_VERSION , 0 , 16 ) ) ;
}
{
testCompiler . clearCache ( ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
testCompiler . tweakHeader ( [ ] ( QV4 : : CompiledData : : Unit * header ) {
header - > architectureIndex = 0 ;
} ) ;
QVERIFY ( ! testCompiler . verify ( ) ) ;
QCOMPARE ( testCompiler . lastErrorString , QString : : fromUtf8 ( " Architecture mismatch. Found expected %1 " ) . arg ( QSysInfo : : buildAbi ( ) ) ) ;
}
{
testCompiler . clearCache ( ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
testCompiler . tweakHeader ( [ ] ( QV4 : : CompiledData : : Unit * header ) {
header - > codeGeneratorIndex = 0 ;
} ) ;
QVERIFY ( ! testCompiler . verify ( ) ) ;
QCOMPARE ( testCompiler . lastErrorString , QString : : fromUtf8 ( " Code generator mismatch. Found code generated by but expected %1 " ) . arg ( QV8Engine : : getV4 ( & engine ) - > iselFactory - > codeGeneratorName ) ) ;
}
}
2016-07-28 15:40:36 +00:00
class TypeVersion1 : public QObject
{
Q_OBJECT
Q_PROPERTY ( int value READ value WRITE setValue NOTIFY valueChanged ) ;
public :
int m_value = 0 ;
int value ( ) const { return m_value ; }
void setValue ( int v ) { m_value = v ; emit valueChanged ( ) ; }
signals :
void valueChanged ( ) ;
} ;
// Same as TypeVersion1 except the property type changed!
class TypeVersion2 : public QObject
{
Q_OBJECT
Q_PROPERTY ( QString value READ value WRITE setValue NOTIFY valueChanged ) ;
public :
QString m_value ;
QString value ( ) const { return m_value ; }
void setValue ( QString v ) { m_value = v ; emit valueChanged ( ) ; }
signals :
void valueChanged ( ) ;
} ;
void tst_qmldiskcache : : recompileAfterChange ( )
{
QQmlEngine engine ;
TestCompiler testCompiler ( & engine ) ;
QVERIFY ( testCompiler . tempDir . isValid ( ) ) ;
const QByteArray contents = QByteArrayLiteral ( " import TypeTest 1.0 \n "
" TypeThatWillChange { \n "
" } " ) ;
qmlRegisterType < TypeVersion1 > ( " TypeTest " , 1 , 0 , " TypeThatWillChange " ) ;
{
testCompiler . clearCache ( ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
QVERIFY2 ( testCompiler . verify ( ) , qPrintable ( testCompiler . lastErrorString ) ) ;
}
QDateTime initialCacheTimeStamp = QFileInfo ( testCompiler . cacheFilePath ) . lastModified ( ) ;
{
CleanlyLoadingComponent component ( & engine , testCompiler . testFilePath ) ;
QScopedPointer < TypeVersion1 > obj ( qobject_cast < TypeVersion1 * > ( component . create ( ) ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( QFileInfo ( testCompiler . cacheFilePath ) . lastModified ( ) , initialCacheTimeStamp ) ;
}
engine . clearComponentCache ( ) ;
{
CleanlyLoadingComponent component ( & engine , testCompiler . testFilePath ) ;
QScopedPointer < TypeVersion1 > obj ( qobject_cast < TypeVersion1 * > ( component . create ( ) ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( QFileInfo ( testCompiler . cacheFilePath ) . lastModified ( ) , initialCacheTimeStamp ) ;
}
engine . clearComponentCache ( ) ;
qmlClearTypeRegistrations ( ) ;
qmlRegisterType < TypeVersion2 > ( " TypeTest " , 1 , 0 , " TypeThatWillChange " ) ;
waitForFileSystem ( ) ;
{
CleanlyLoadingComponent component ( & engine , testCompiler . testFilePath ) ;
QScopedPointer < TypeVersion2 > obj ( qobject_cast < TypeVersion2 * > ( component . create ( ) ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QVERIFY ( QFileInfo ( testCompiler . cacheFilePath ) . lastModified ( ) > initialCacheTimeStamp ) ;
}
}
2017-01-02 09:46:38 +00:00
void tst_qmldiskcache : : recompileAfterDirectoryChange ( )
{
QQmlEngine engine ;
TestCompiler testCompiler ( & engine ) ;
QVERIFY ( testCompiler . tempDir . isValid ( ) ) ;
QVERIFY ( QDir ( testCompiler . tempDir . path ( ) ) . mkdir ( " source1 " ) ) ;
testCompiler . init ( testCompiler . tempDir . path ( ) + QLatin1String ( " /source1 " ) ) ;
{
const QByteArray contents = QByteArrayLiteral ( " import QtQml 2.0 \n "
" QtObject { \n "
" property int blah: 42; \n "
" } " ) ;
testCompiler . clearCache ( ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
QVERIFY2 ( testCompiler . verify ( ) , qPrintable ( testCompiler . lastErrorString ) ) ;
testCompiler . closeMapping ( ) ;
}
const QDateTime initialCacheTimeStamp = QFileInfo ( testCompiler . cacheFilePath ) . lastModified ( ) ;
QDir ( testCompiler . tempDir . path ( ) ) . rename ( QStringLiteral ( " source1 " ) , QStringLiteral ( " source2 " ) ) ;
waitForFileSystem ( ) ;
testCompiler . init ( testCompiler . tempDir . path ( ) + QLatin1String ( " /source2 " ) ) ;
{
CleanlyLoadingComponent component ( & engine , testCompiler . testFilePath ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " blah " ) . toInt ( ) , 42 ) ;
}
QFile cacheFile ( testCompiler . cacheFilePath ) ;
QVERIFY2 ( cacheFile . exists ( ) , qPrintable ( cacheFile . fileName ( ) ) ) ;
QVERIFY ( QFileInfo ( testCompiler . cacheFilePath ) . lastModified ( ) > initialCacheTimeStamp ) ;
}
2016-08-01 13:57:53 +00:00
void tst_qmldiskcache : : fileSelectors ( )
{
QQmlEngine engine ;
QTemporaryDir tempDir ;
QVERIFY ( tempDir . isValid ( ) ) ;
const QString testFilePath = tempDir . path ( ) + " /test.qml " ;
{
QFile f ( testFilePath ) ;
QVERIFY2 ( f . open ( QIODevice : : WriteOnly ) , qPrintable ( f . errorString ( ) ) ) ;
f . write ( QByteArrayLiteral ( " import QtQml 2.0 \n QtObject { property int value: 42 } " ) ) ;
}
const QString selector = QStringLiteral ( " testSelector " ) ;
const QString selectorPath = tempDir . path ( ) + " /+ " + selector ;
const QString selectedTestFilePath = selectorPath + " /test.qml " ;
{
QVERIFY ( QDir : : root ( ) . mkpath ( selectorPath ) ) ;
QFile f ( selectorPath + " /test.qml " ) ;
QVERIFY2 ( f . open ( QIODevice : : WriteOnly ) , qPrintable ( f . errorString ( ) ) ) ;
f . write ( QByteArrayLiteral ( " import QtQml 2.0 \n QtObject { property int value: 100 } " ) ) ;
}
{
QQmlComponent component ( & engine , testFilePath ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " value " ) . toInt ( ) , 42 ) ;
QFile cacheFile ( testFilePath + " c " ) ;
QVERIFY2 ( cacheFile . exists ( ) , qPrintable ( cacheFile . fileName ( ) ) ) ;
}
QQmlFileSelector qmlSelector ( & engine ) ;
qmlSelector . setExtraSelectors ( QStringList ( ) < < selector ) ;
engine . clearComponentCache ( ) ;
{
QQmlComponent component ( & engine , testFilePath ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " value " ) . toInt ( ) , 100 ) ;
QFile cacheFile ( selectedTestFilePath + " c " ) ;
QVERIFY2 ( cacheFile . exists ( ) , qPrintable ( cacheFile . fileName ( ) ) ) ;
}
}
2016-08-05 19:10:17 +00:00
void tst_qmldiskcache : : localAliases ( )
{
QQmlEngine engine ;
TestCompiler testCompiler ( & engine ) ;
QVERIFY ( testCompiler . tempDir . isValid ( ) ) ;
const QByteArray contents = QByteArrayLiteral ( " import QtQml 2.0 \n "
" QtObject { \n "
" id: root \n "
" property int prop: 100 \n "
" property alias dummy1: root.prop \n "
" property alias dummy2: root.prop \n "
" property alias dummy3: root.prop \n "
" property alias dummy4: root.prop \n "
" property alias dummy5: root.prop \n "
" property alias foo: root.prop \n "
" property alias bar: root.foo \n "
" } " ) ;
{
testCompiler . clearCache ( ) ;
QVERIFY2 ( testCompiler . compile ( contents ) , qPrintable ( testCompiler . lastErrorString ) ) ;
QVERIFY2 ( testCompiler . verify ( ) , qPrintable ( testCompiler . lastErrorString ) ) ;
}
{
CleanlyLoadingComponent component ( & engine , testCompiler . testFilePath ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " bar " ) . toInt ( ) , 100 ) ;
}
engine . clearComponentCache ( ) ;
{
CleanlyLoadingComponent component ( & engine , testCompiler . testFilePath ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " bar " ) . toInt ( ) , 100 ) ;
}
}
2016-08-07 12:53:57 +00:00
void tst_qmldiskcache : : cacheResources ( )
{
const QString cacheDirectory = QStandardPaths : : writableLocation ( QStandardPaths : : CacheLocation ) ;
QVERIFY ( QDir : : root ( ) . mkpath ( cacheDirectory ) ) ;
const QString qmlCacheDirectory = cacheDirectory + QLatin1String ( " /qmlcache/ " ) ;
QVERIFY ( QDir ( qmlCacheDirectory ) . removeRecursively ( ) ) ;
QVERIFY ( QDir : : root ( ) . mkpath ( qmlCacheDirectory ) ) ;
QVERIFY ( QDir ( qmlCacheDirectory ) . entryList ( QDir : : NoDotAndDotDot ) . isEmpty ( ) ) ;
QQmlEngine engine ;
{
CleanlyLoadingComponent component ( & engine , QUrl ( " qrc:/test.qml " ) ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " value " ) . toInt ( ) , 20 ) ;
}
2016-09-12 14:48:59 +00:00
const QStringList entries = QDir ( qmlCacheDirectory ) . entryList ( QDir : : NoDotAndDotDot | QDir : : Files ) ;
QCOMPARE ( entries . count ( ) , 1 ) ;
2016-11-22 14:53:07 +00:00
QDateTime cacheFileTimeStamp ;
2016-09-12 14:48:59 +00:00
{
QFile cacheFile ( qmlCacheDirectory + QLatin1Char ( ' / ' ) + entries . constFirst ( ) ) ;
QVERIFY2 ( cacheFile . open ( QIODevice : : ReadOnly ) , qPrintable ( cacheFile . errorString ( ) ) ) ;
QV4 : : CompiledData : : Unit unit ;
QVERIFY ( cacheFile . read ( reinterpret_cast < char * > ( & unit ) , sizeof ( unit ) ) = = sizeof ( unit ) ) ;
2016-11-22 14:35:57 +00:00
2016-11-22 14:53:07 +00:00
cacheFileTimeStamp = QFileInfo ( cacheFile . fileName ( ) ) . lastModified ( ) ;
2016-11-22 14:35:57 +00:00
QDateTime referenceTimeStamp = QFileInfo ( " :/test.qml " ) . lastModified ( ) ;
if ( ! referenceTimeStamp . isValid ( ) )
referenceTimeStamp = QFileInfo ( QCoreApplication : : applicationFilePath ( ) ) . lastModified ( ) ;
QCOMPARE ( qint64 ( unit . sourceTimeStamp ) , referenceTimeStamp . toMSecsSinceEpoch ( ) ) ;
2016-09-12 14:48:59 +00:00
}
2016-11-22 14:53:07 +00:00
waitForFileSystem ( ) ;
{
CleanlyLoadingComponent component ( & engine , QUrl ( " qrc:///test.qml " ) ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " value " ) . toInt ( ) , 20 ) ;
}
{
const QStringList entries = QDir ( qmlCacheDirectory ) . entryList ( QDir : : NoDotAndDotDot | QDir : : Files ) ;
QCOMPARE ( entries . count ( ) , 1 ) ;
QCOMPARE ( QFileInfo ( qmlCacheDirectory + QLatin1Char ( ' / ' ) + entries . constFirst ( ) ) . lastModified ( ) . toMSecsSinceEpoch ( ) , cacheFileTimeStamp . toMSecsSinceEpoch ( ) ) ;
}
2016-08-07 12:53:57 +00:00
}
2016-10-16 15:54:59 +00:00
void tst_qmldiskcache : : stableOrderOfDependentCompositeTypes ( )
{
QQmlEngine engine ;
QTemporaryDir tempDir ;
QVERIFY ( tempDir . isValid ( ) ) ;
{
const QString depFilePath = tempDir . path ( ) + " /FirstDependentType.qml " ;
QFile f ( depFilePath ) ;
QVERIFY2 ( f . open ( QIODevice : : WriteOnly ) , qPrintable ( f . errorString ( ) ) ) ;
f . write ( QByteArrayLiteral ( " import QtQml 2.0 \n QtObject { property int value: 42 } " ) ) ;
}
{
const QString depFilePath = tempDir . path ( ) + " /SecondDependentType.qml " ;
QFile f ( depFilePath ) ;
QVERIFY2 ( f . open ( QIODevice : : WriteOnly ) , qPrintable ( f . errorString ( ) ) ) ;
f . write ( QByteArrayLiteral ( " import QtQml 2.0 \n QtObject { property int value: 100 } " ) ) ;
}
const QString testFilePath = tempDir . path ( ) + " /main.qml " ;
{
QFile f ( testFilePath ) ;
QVERIFY2 ( f . open ( QIODevice : : WriteOnly ) , qPrintable ( f . errorString ( ) ) ) ;
f . write ( QByteArrayLiteral ( " import QtQml 2.0 \n QtObject { \n "
" property QtObject dep1: FirstDependentType{} \n "
" property QtObject dep2: SecondDependentType{} \n "
" property int value: dep1.value + dep2.value \n "
" } " ) ) ;
}
QByteArray firstDependentTypeClassName ;
QByteArray secondDependentTypeClassName ;
{
CleanlyLoadingComponent component ( & engine , QUrl : : fromLocalFile ( testFilePath ) ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " value " ) . toInt ( ) , 142 ) ;
firstDependentTypeClassName = qvariant_cast < QObject * > ( obj - > property ( " dep1 " ) ) - > metaObject ( ) - > className ( ) ;
secondDependentTypeClassName = qvariant_cast < QObject * > ( obj - > property ( " dep2 " ) ) - > metaObject ( ) - > className ( ) ;
}
QVERIFY ( firstDependentTypeClassName ! = secondDependentTypeClassName ) ;
QVERIFY2 ( firstDependentTypeClassName . contains ( " QMLTYPE " ) , firstDependentTypeClassName . constData ( ) ) ;
QVERIFY2 ( secondDependentTypeClassName . contains ( " QMLTYPE " ) , secondDependentTypeClassName . constData ( ) ) ;
const QString testFileCachePath = testFilePath + QLatin1Char ( ' c ' ) ;
QVERIFY ( QFile : : exists ( testFileCachePath ) ) ;
QDateTime initialCacheTimeStamp = QFileInfo ( testFileCachePath ) . lastModified ( ) ;
engine . clearComponentCache ( ) ;
waitForFileSystem ( ) ;
// Creating the test component a second time should load it from the cache (same time stamp),
// despite the class names of the dependent composite types differing.
{
CleanlyLoadingComponent component ( & engine , QUrl : : fromLocalFile ( testFilePath ) ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " value " ) . toInt ( ) , 142 ) ;
QVERIFY ( qvariant_cast < QObject * > ( obj - > property ( " dep1 " ) ) - > metaObject ( ) - > className ( ) ! = firstDependentTypeClassName ) ;
QVERIFY ( qvariant_cast < QObject * > ( obj - > property ( " dep2 " ) ) - > metaObject ( ) - > className ( ) ! = secondDependentTypeClassName ) ;
}
{
QVERIFY ( QFile : : exists ( testFileCachePath ) ) ;
QDateTime newCacheTimeStamp = QFileInfo ( testFileCachePath ) . lastModified ( ) ;
QCOMPARE ( newCacheTimeStamp , initialCacheTimeStamp ) ;
}
// Now change the first dependent QML type and see if we correctly re-generate the
// caches.
engine . clearComponentCache ( ) ;
waitForFileSystem ( ) ;
{
const QString depFilePath = tempDir . path ( ) + " /FirstDependentType.qml " ;
QFile f ( depFilePath ) ;
QVERIFY2 ( f . open ( QIODevice : : WriteOnly ) , qPrintable ( f . errorString ( ) ) ) ;
f . write ( QByteArrayLiteral ( " import QtQml 2.0 \n QtObject { property int value: 40 } " ) ) ;
}
{
CleanlyLoadingComponent component ( & engine , QUrl : : fromLocalFile ( testFilePath ) ) ;
QScopedPointer < QObject > obj ( component . create ( ) ) ;
QVERIFY ( ! obj . isNull ( ) ) ;
QCOMPARE ( obj - > property ( " value " ) . toInt ( ) , 140 ) ;
}
{
QVERIFY ( QFile : : exists ( testFileCachePath ) ) ;
QDateTime newCacheTimeStamp = QFileInfo ( testFileCachePath ) . lastModified ( ) ;
QVERIFY2 ( newCacheTimeStamp > initialCacheTimeStamp , qPrintable ( newCacheTimeStamp . toString ( ) ) ) ;
}
}
2016-06-06 09:46:01 +00:00
QTEST_MAIN ( tst_qmldiskcache )
# include "tst_qmldiskcache.moc"