aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qmldiskcache
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2016-10-16 17:54:59 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2016-10-18 13:26:50 +0000
commit2c2c92d999ae8584840e44785fbece9bf2cfac62 (patch)
tree6c5239e3b3189be0e48ea3bd62df7b8c8bb126c2 /tests/auto/qml/qmldiskcache
parent15630c45e7383cf23ff201af5330c36e32014cdd (diff)
Fix excessive invalidation of QML disk caches
We use an MD5 checksum over the meta-object data to verify that the types a QML file depends on haven't changed since the cache was generated. However when the dependent types are QML types, then the meta-object data contains dynamically generated type names such as QMLTYPE_1234, which is non-deterministic. To address this, we resort to the checksum over the meta-object data only for C++ types (if those change it's likely an incompatible change) and for QML types use the fact that all the information about the QML declared types comes from the QML file only, which means we can in that case simply use a checksum over the QV4::CompiledData memory chunk. In addition we need to ensure that the generated CompiledData memory chunk is deterministic by avoiding any uninitialized bytes (memset) and using a map instead of a hash for the mapping of object index to object id. Task-number: QTBUG-55926 Change-Id: I27c840b1960ad36b486198e504b70989c22a3972 Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
Diffstat (limited to 'tests/auto/qml/qmldiskcache')
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp100
1 files changed, 100 insertions, 0 deletions
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 92b38c6ad8..e2c0055ea1 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -56,6 +56,7 @@ private slots:
void fileSelectors();
void localAliases();
void cacheResources();
+ void stableOrderOfDependentCompositeTypes();
};
// A wrapper around QQmlComponent to ensure the temporary reference counts
@@ -566,6 +567,105 @@ void tst_qmldiskcache::cacheResources()
}
}
+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\nQtObject { 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\nQtObject { 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\nQtObject {\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\nQtObject { 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()));
+ }
+}
+
QTEST_MAIN(tst_qmldiskcache)
#include "tst_qmldiskcache.moc"