From c7fb5612215f1f54f1de0af3eafbb6ddaa623c08 Mon Sep 17 00:00:00 2001 From: Arttu Tarkiainen Date: Wed, 12 Oct 2022 10:01:31 +0300 Subject: Add auto-tests for metadata cache Task-number: QTIFW-2621 Change-Id: Ibdc69d8f64f318505df157091438f79fe55190e8 Reviewed-by: Katja Marttila --- tests/auto/installer/installer.pro | 3 +- .../data/existing-cache/manifest.json | 7 + .../existing-cache/placeholder_sha1/Updates.xml | 19 ++ .../existing-cache/placeholder_sha1/repository.txt | 1 + .../local-temp-repository/A/example-license.txt | 1 + .../data/local-temp-repository/Updates.xml | 17 + .../auto/installer/metadatacache/metadatacache.pro | 8 + tests/auto/installer/metadatacache/settings.qrc | 9 + .../installer/metadatacache/tst_metadatacache.cpp | 347 +++++++++++++++++++++ 9 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 tests/auto/installer/metadatacache/data/existing-cache/manifest.json create mode 100644 tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/Updates.xml create mode 100644 tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/repository.txt create mode 100644 tests/auto/installer/metadatacache/data/local-temp-repository/A/example-license.txt create mode 100644 tests/auto/installer/metadatacache/data/local-temp-repository/Updates.xml create mode 100644 tests/auto/installer/metadatacache/metadatacache.pro create mode 100644 tests/auto/installer/metadatacache/settings.qrc create mode 100644 tests/auto/installer/metadatacache/tst_metadatacache.cpp (limited to 'tests/auto') diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro index 5d93f2132..9325e71c5 100644 --- a/tests/auto/installer/installer.pro +++ b/tests/auto/installer/installer.pro @@ -41,7 +41,8 @@ SUBDIRS += \ treename \ createoffline \ contentshaupdate \ - componentreplace + componentreplace \ + metadatacache CONFIG(libarchive) { SUBDIRS += libarchivearchive diff --git a/tests/auto/installer/metadatacache/data/existing-cache/manifest.json b/tests/auto/installer/metadatacache/data/existing-cache/manifest.json new file mode 100644 index 000000000..0ef247e8e --- /dev/null +++ b/tests/auto/installer/metadatacache/data/existing-cache/manifest.json @@ -0,0 +1,7 @@ +{ + "items": [ + "placeholder_sha1" + ], + "type": "Metadata", + "version": "1.0.0" +} diff --git a/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/Updates.xml b/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/Updates.xml new file mode 100644 index 000000000..b0b30c9d4 --- /dev/null +++ b/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/Updates.xml @@ -0,0 +1,19 @@ + + {AnyApplication} + 1.0.0 + true + + + + + C + C + Example component C + 1.0.0-1 + 2015-01-01 + true + + content.7z + 5b3939da1af492382c68388fc796837e4c36b876 + + diff --git a/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/repository.txt b/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/repository.txt new file mode 100644 index 000000000..7224edd52 --- /dev/null +++ b/tests/auto/installer/metadatacache/data/existing-cache/placeholder_sha1/repository.txt @@ -0,0 +1 @@ +/example-repository diff --git a/tests/auto/installer/metadatacache/data/local-temp-repository/A/example-license.txt b/tests/auto/installer/metadatacache/data/local-temp-repository/A/example-license.txt new file mode 100644 index 000000000..1ffdcc8df --- /dev/null +++ b/tests/auto/installer/metadatacache/data/local-temp-repository/A/example-license.txt @@ -0,0 +1 @@ +Example license file diff --git a/tests/auto/installer/metadatacache/data/local-temp-repository/Updates.xml b/tests/auto/installer/metadatacache/data/local-temp-repository/Updates.xml new file mode 100644 index 000000000..03e54572d --- /dev/null +++ b/tests/auto/installer/metadatacache/data/local-temp-repository/Updates.xml @@ -0,0 +1,17 @@ + + {AnyApplication} + 1.0.0 + false + + A + A + Example component A + 1.0.2-1 + 2015-01-01 + true + f46c677db8bc779d70d0c72fae264a321caea6f8 + + + + + diff --git a/tests/auto/installer/metadatacache/metadatacache.pro b/tests/auto/installer/metadatacache/metadatacache.pro new file mode 100644 index 000000000..df1542dc7 --- /dev/null +++ b/tests/auto/installer/metadatacache/metadatacache.pro @@ -0,0 +1,8 @@ +include(../../qttest.pri) + +QT += qml + +SOURCES += tst_metadatacache.cpp + +RESOURCES += \ + settings.qrc diff --git a/tests/auto/installer/metadatacache/settings.qrc b/tests/auto/installer/metadatacache/settings.qrc new file mode 100644 index 000000000..96383fdcb --- /dev/null +++ b/tests/auto/installer/metadatacache/settings.qrc @@ -0,0 +1,9 @@ + + + data/local-temp-repository/Updates.xml + data/local-temp-repository/A/example-license.txt + data/existing-cache/manifest.json + data/existing-cache/placeholder_sha1/repository.txt + data/existing-cache/placeholder_sha1/Updates.xml + + diff --git a/tests/auto/installer/metadatacache/tst_metadatacache.cpp b/tests/auto/installer/metadatacache/tst_metadatacache.cpp new file mode 100644 index 000000000..46e4d1dfe --- /dev/null +++ b/tests/auto/installer/metadatacache/tst_metadatacache.cpp @@ -0,0 +1,347 @@ +/************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $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 "../shared/packagemanager.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace QInstaller; + +static const QByteArray scPlaceholderSha1("placeholder_sha1"); + +class tst_metadatacache : public QObject +{ + Q_OBJECT + +private: + void copyExistingCacheFromResourceTree() + { + try { + QInstaller::copyDirectoryContents(":/data/existing-cache/", m_cachePath); + + // We need to modify the test data here because the checksums of + // files may differ on Windows and Unix platforms. + QVERIFY(QDir().rename(m_cachePath + QDir::separator() + scPlaceholderSha1, + m_cachePath + QDir::separator() + m_oldMetadataItemChecksum)); + + QFile manifestFile(m_cachePath + QDir::separator() + "manifest.json"); + // The file lost the write bit after copying from resource + QInstaller::setDefaultFilePermissions(&manifestFile, QInstaller::NonExecutable); + QVERIFY2(manifestFile.open(QIODevice::ReadWrite), qPrintable(manifestFile.errorString())); + + const QByteArray manifestData = manifestFile.readAll(); + QJsonDocument manifestJsonDoc(QJsonDocument::fromJson(manifestData)); + QJsonObject docJsonObject = manifestJsonDoc.object(); + + QJsonArray itemsJsonArray; + itemsJsonArray.append(QJsonValue(QLatin1String(m_oldMetadataItemChecksum))); + + docJsonObject.insert(QLatin1String("items"), itemsJsonArray); + manifestJsonDoc.setObject(docJsonObject); + + manifestFile.seek(0); + QVERIFY(manifestFile.write(manifestJsonDoc.toJson()) != -1); + + } catch (const Error &e) { + QVERIFY2(false, QString::fromLatin1("Error while copying item to path %1: %2") + .arg(m_cachePath, e.message()).toLatin1()); + } + } + + QByteArray checksumFromUpdateFile(const QString &directory) + { + QFile updateFile(directory + QDir::separator() + QLatin1String("Updates.xml")); + if (!updateFile.open(QIODevice::ReadOnly)) + return QByteArray(); + + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(&updateFile); + return hash.result().toHex(); + } + +private slots: + void init() + { + m_cachePath = generateTemporaryFileName(); + } + + void cleanup() + { + if (QFileInfo::exists(m_cachePath)) + QInstaller::removeDirectory(m_cachePath, true); + } + + void initTestCase() + { + m_newMetadataItemChecksum = checksumFromUpdateFile(":/data/local-temp-repository"); + m_oldMetadataItemChecksum = checksumFromUpdateFile(":/data/existing-cache/" + + QLatin1String(scPlaceholderSha1)); + + QVERIFY(!m_newMetadataItemChecksum.isEmpty()); + QVERIFY(!m_oldMetadataItemChecksum.isEmpty()); + + qInstallMessageHandler(silentTestMessageHandler); + } + + void testRegisterItemToEmptyCache() + { + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + Metadata *metadata = new Metadata(":/data/local-temp-repository/"); + + QVERIFY(cache.registerItem(metadata)); + metadata = cache.itemByChecksum(m_newMetadataItemChecksum); + QVERIFY(metadata); + QVERIFY(metadata->isValid()); + + QVERIFY(cache.clear()); + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testRegisterItemToExistingCache() + { + copyExistingCacheFromResourceTree(); + + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + Metadata *metadata = new Metadata(":/data/local-temp-repository/"); + + QVERIFY(cache.registerItem(metadata)); + metadata = cache.itemByChecksum(m_newMetadataItemChecksum); + QVERIFY(metadata); + QVERIFY(metadata->isValid()); + + QVERIFY(cache.clear()); + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testRegisterItemFails() + { + // 1. Test fail due to invalidated cache + GenericDataCache cache; + Metadata *metadata = new Metadata(":/data/local-temp-repository/"); + + QVERIFY(!cache.registerItem(metadata)); + QCOMPARE(cache.errorString(), "Cannot register item to invalidated cache."); + + delete metadata; + metadata = nullptr; + + // 2. Test fail due to null metadata + cache.setPath(m_cachePath); + cache.setType("Metadata"); + cache.setVersion("1.0.0"); + QVERIFY(cache.initialize()); + + QVERIFY(!cache.registerItem(metadata)); + QCOMPARE(cache.errorString(), "Cannot register null item."); + + // 3. Test fail due to invalid metadata + metadata = new Metadata; + QVERIFY(!cache.registerItem(metadata)); + QCOMPARE(cache.errorString(), "Cannot register invalid item with checksum "); + + // 4. Test fail due to duplicate metadata item + metadata->setPath(":/data/local-temp-repository/"); + QVERIFY(cache.registerItem(metadata)); + QVERIFY(cache.itemByChecksum(m_newMetadataItemChecksum)->isValid()); + QVERIFY(!cache.registerItem(metadata)); + QCOMPARE(cache.errorString(), QString::fromLatin1("Cannot register item with checksum " + "%1. An item with the same checksum already exists in cache.") + .arg(QString::fromLatin1(m_newMetadataItemChecksum)).toLatin1()); + + QVERIFY(cache.clear()); + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testInitializeExistingCache() + { + copyExistingCacheFromResourceTree(); + + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + Metadata *metadata = cache.itemByChecksum(m_oldMetadataItemChecksum); + QVERIFY(metadata); + QVERIFY(metadata->isValid()); + + QVERIFY(cache.clear()); + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testInitializeForeignCache_data() + { + QTest::addColumn("type"); + QTest::addColumn("version"); + + QTest::newRow("Type mismatch") << "MyCacheableType" << "1.0.0"; + QTest::newRow("Version mismatch") << "Metadata" << "0.9.1"; + } + + void testInitializeForeignCache() + { + QFETCH(QString, type); + QFETCH(QString, version); + + copyExistingCacheFromResourceTree(); + + GenericDataCache cache(m_cachePath, type, version); + QVERIFY(cache.isValid()); + QVERIFY(!cache.itemByChecksum(m_oldMetadataItemChecksum)); + + QVERIFY(cache.clear()); + // The 'foreign' entry prevents removing the directory + QVERIFY(QFileInfo::exists(m_cachePath)); + } + + void testInitializeCacheFails() + { + GenericDataCache cache; + QVERIFY(!cache.initialize()); + QCOMPARE(cache.errorString(), "Cannot initialize cache with empty path."); + } + + void testRemoveItemFromCache() + { + copyExistingCacheFromResourceTree(); + + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + Metadata *metadata = cache.itemByChecksum(m_oldMetadataItemChecksum); + QVERIFY(metadata); + QVERIFY(metadata->isValid()); + QVERIFY(cache.removeItem(m_oldMetadataItemChecksum)); + + QVERIFY(cache.clear()); + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testRemoveItemFails() + { + copyExistingCacheFromResourceTree(); + + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + QVERIFY(!cache.removeItem("12345")); + QCOMPARE(cache.errorString(), "Cannot remove item specified by checksum 12345: no such item exists."); + + QVERIFY(cache.clear()); + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testRetrieveItemFromCache() + { + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + Metadata *metadata = new Metadata(":/data/local-temp-repository/"); + + QVERIFY(cache.registerItem(metadata)); + metadata = cache.itemByChecksum(m_newMetadataItemChecksum); + QVERIFY(metadata); + QVERIFY(metadata->isValid()); + + metadata = cache.itemByPath(metadata->path()); + QVERIFY(metadata); + QVERIFY(metadata->isValid()); + + QVERIFY(cache.clear()); + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testRetrieveItemFails() + { + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + Metadata *metadata = new Metadata(":/data/local-temp-repository/"); + const QString metadataPath = metadata->path(); + + QVERIFY(cache.registerItem(metadata)); + QVERIFY(cache.clear()); + + QVERIFY(!cache.itemByChecksum(m_newMetadataItemChecksum)); + QVERIFY(!cache.itemByPath(metadataPath)); + QCOMPARE(cache.errorString(), "Cannot retrieve item from invalidated cache."); + + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testItemObsoletesOther() + { + copyExistingCacheFromResourceTree(); + + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + Metadata *metadata = new Metadata(":/data/local-temp-repository/"); + + QVERIFY(cache.registerItem(metadata)); + metadata->setRepository(Repository(QUrl("file:///example-repository"), true)); + metadata->setPersistentRepositoryPath(QUrl("file:///example-repository")); + QVERIFY(metadata->isActive()); + + metadata = cache.itemByChecksum(m_newMetadataItemChecksum); + QVERIFY(metadata); + QVERIFY(metadata->isValid()); + + metadata = cache.itemByChecksum(m_oldMetadataItemChecksum); + QVERIFY(metadata); + QVERIFY(metadata->isValid()); + + Metadata *obsolete = cache.obsoleteItems().first(); + QVERIFY(!obsolete->isActive()); + QCOMPARE(obsolete->checksum(), m_oldMetadataItemChecksum); + + QVERIFY(cache.clear()); + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + + void testClearCacheFails() + { + GenericDataCache cache(m_cachePath, "Metadata", "1.0.0"); + Metadata *metadata = new Metadata(":/data/local-temp-repository/"); + + QVERIFY(cache.registerItem(metadata)); + QVERIFY(cache.clear()); + QVERIFY(!cache.clear()); + QCOMPARE(cache.errorString(), "Cannot clear invalidated cache."); + + QVERIFY(!QFileInfo::exists(m_cachePath)); + } + +private: + QString m_cachePath; + QByteArray m_newMetadataItemChecksum; + QByteArray m_oldMetadataItemChecksum; +}; + +QTEST_MAIN(tst_metadatacache) + +#include "tst_metadatacache.moc" -- cgit v1.2.3