diff options
author | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2022-10-12 10:01:31 +0300 |
---|---|---|
committer | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2022-10-20 09:11:17 +0300 |
commit | c7fb5612215f1f54f1de0af3eafbb6ddaa623c08 (patch) | |
tree | de39c2662479ae62588e787e8f4f58a18f70913b /tests | |
parent | 499a7a0add7780b4af929716479a18015973a0e9 (diff) |
Add auto-tests for metadata cache
Task-number: QTIFW-2621
Change-Id: Ibdc69d8f64f318505df157091438f79fe55190e8
Reviewed-by: Katja Marttila <katja.marttila@qt.io>
Diffstat (limited to 'tests')
9 files changed, 411 insertions, 1 deletions
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 @@ +<Updates> + <ApplicationName>{AnyApplication}</ApplicationName> + <ApplicationVersion>1.0.0</ApplicationVersion> + <Checksum>true</Checksum> + <RepositoryUpdate> + <Repository action="remove" url="../repository"/> + </RepositoryUpdate> + <PackageUpdate> + <Name>C</Name> + <DisplayName>C</DisplayName> + <Description>Example component C</Description> + <Version>1.0.0-1</Version> + <ReleaseDate>2015-01-01</ReleaseDate> + <Default>true</Default> + <UpdateFile CompressedSize="222" OS="Any" UncompressedSize="72"/> + <DownloadableArchives>content.7z</DownloadableArchives> + <SHA1>5b3939da1af492382c68388fc796837e4c36b876</SHA1> + </PackageUpdate> +</Updates> 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 @@ +<Updates> + <ApplicationName>{AnyApplication}</ApplicationName> + <ApplicationVersion>1.0.0</ApplicationVersion> + <Checksum>false</Checksum> + <PackageUpdate> + <Name>A</Name> + <DisplayName>A</DisplayName> + <Description>Example component A</Description> + <Version>1.0.2-1</Version> + <ReleaseDate>2015-01-01</ReleaseDate> + <Default>true</Default> + <SHA1>f46c677db8bc779d70d0c72fae264a321caea6f8</SHA1> + <Licenses> + <License name="Example license" file="example-license.txt"/> + </Licenses> + </PackageUpdate> +</Updates> 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 @@ +<RCC> + <qresource prefix="/"> + <file>data/local-temp-repository/Updates.xml</file> + <file>data/local-temp-repository/A/example-license.txt</file> + <file>data/existing-cache/manifest.json</file> + <file>data/existing-cache/placeholder_sha1/repository.txt</file> + <file>data/existing-cache/placeholder_sha1/Updates.xml</file> + </qresource> +</RCC> 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 <errors.h> +#include <fileutils.h> +#include <genericdatacache.h> +#include <metadata.h> +#include <repository.h> + +#include <QCryptographicHash> +#include <QDir> +#include <QFileInfo> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QObject> +#include <QTest> + +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<Metadata> 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<Metadata> 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<Metadata> 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<Metadata> 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<QString>("type"); + QTest::addColumn<QString>("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<Metadata> 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<Metadata> cache; + QVERIFY(!cache.initialize()); + QCOMPARE(cache.errorString(), "Cannot initialize cache with empty path."); + } + + void testRemoveItemFromCache() + { + copyExistingCacheFromResourceTree(); + + GenericDataCache<Metadata> 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<Metadata> 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<Metadata> 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<Metadata> 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<Metadata> 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<Metadata> 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" |