diff options
20 files changed, 173 insertions, 69 deletions
diff --git a/.qmake.conf b/.qmake.conf index 560bbf5ce..95fc3f4da 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,2 +1,2 @@ -VERSION=4.4.1 +VERSION=4.4.2 CONFIG=prepare_docs qt_docs_targets $$CONFIG @@ -1,3 +1,12 @@ +4.4.2 +- Fix uninstallation of needed virtual components +- Attach to squish only when the port is separately given (QTIFW-2746) +- Windows: fix installation error with concurrent Extract operations (QTIFW-2752) +- Uninstaller remove target directory if it is empty (QTIFW-884) +- Uninstaller remove maintenancetool's data files (QTIFW-884) +- Do not convert newline characters in license files (QTIFW-903) +- Set encoding to UTF-8 when writing license file (QTIFW-1436) + 4.4.1 - Fix bug when all requested packages are not installed (QTIFW-2708) diff --git a/coin/instructions/make_instructions.yaml b/coin/instructions/make_instructions.yaml index c3e2452ae..357445fbb 100644 --- a/coin/instructions/make_instructions.yaml +++ b/coin/instructions/make_instructions.yaml @@ -77,13 +77,13 @@ instructions: - type: ChangeDirectory directory: "{{.InstallRoot}}/{{.AgentWorkingDir}}" - type: ExecuteCommand - command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-linux-x64-4.4.1.run" + command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-linux-x64-4.4.2.run" maxTimeInSeconds: 36000 maxTimeBetweenOutput: 3600 userMessageOnFailure: "Failed to create ifw installer." - type: Rename - sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-x64-4.4.1.run" - targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-4.4.1.run" + sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-linux-x64-4.4.2.run" + targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-linux-x64-4.4.2.run" userMessageOnFailure: "Failed to copy installer." enable_if: condition: and @@ -97,7 +97,7 @@ instructions: - type: ChangeDirectory directory: "{{.InstallRoot}}/{{.AgentWorkingDir}}" - type: ExecuteCommand - command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-macOS-x64-4.4.1.app" + command: "python3 {{.SourceDir}}/coin/create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir {{.SourceDir}}/IfwInstaller --target-name QtInstallerFramework-macOS-x64-4.4.2.app" maxTimeInSeconds: 36000 maxTimeBetweenOutput: 3600 userMessageOnFailure: "Failed to create ifw installer." @@ -105,18 +105,18 @@ instructions: variableName: QT_CODESIGN_IDENTITY_KEY variableValue: "A5GTH44LYL" - type: ExecuteCommand - command: "{{.AgentWorkingDir}}/qtsdk/qtsdk/packaging-tools/sign_installer.py mac --file={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.1.app" + command: "{{.AgentWorkingDir}}/qtsdk/qtsdk/packaging-tools/sign_installer.py mac --file={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.2.app" maxTimeInSeconds: 36000 maxTimeBetweenOutput: 3600 userMessageOnFailure: "Failed to sign the ifw installer" - type: ExecuteCommand - command: "{{.AgentWorkingDir}}/qtsdk/qtsdk/packaging-tools/notarize.py --dmg={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.1.dmg" + command: "{{.AgentWorkingDir}}/qtsdk/qtsdk/packaging-tools/notarize.py --dmg={{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.2.dmg" maxTimeInSeconds: 36000 maxTimeBetweenOutput: 3600 userMessageOnFailure: "Failed to notarize the ifw installer" - type: Rename - sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.1.dmg" - targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x64-4.4.1.dmg" + sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-macOS-x64-4.4.2.dmg" + targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-macOS-x64-4.4.2.dmg" userMessageOnFailure: "Failed to copy installer." enable_if: condition: and @@ -130,7 +130,7 @@ instructions: - type: ChangeDirectory directory: "{{.SourceDir}}" - type: ExecuteCommand - command: "{{.Env.PYTHON3_PATH}}\\python {{.SourceDir}}\\coin\\create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir C:\\{{.SourceDir}}\\IfwInstaller --target-name QtInstallerFramework-windows-x64-4.4.1" + command: "{{.Env.PYTHON3_PATH}}\\python {{.SourceDir}}\\coin\\create_ifw_installer.py --src-dir {{.SourceDir}} --bld-dir {{.SourceDir}} --target-dir C:\\{{.SourceDir}}\\IfwInstaller --target-name QtInstallerFramework-windows-x64-4.4.2" maxTimeInSeconds: 1200 maxTimeBetweenOutput: 1200 userMessageOnFailure: "Failed to create ifw installer." @@ -151,8 +151,8 @@ instructions: equals_value: Windows - type: Rename - sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-windows-x64-4.4.1.exe" - targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x64-4.4.1.exe" + sourcePath: "{{.SourceDir}}/IfwInstaller/QtInstallerFramework-windows-x64-4.4.2.exe" + targetPath: "{{.InstallRoot}}/{{.AgentWorkingDir}}/QtInstallerFramework-windows-x64-4.4.2.exe" userMessageOnFailure: "Failed to copy installer." enable_if: condition: property diff --git a/dist/packages/org.qtproject.ifw.binaries/meta/package.xml b/dist/packages/org.qtproject.ifw.binaries/meta/package.xml index 73ed953f3..ca19108b7 100644 --- a/dist/packages/org.qtproject.ifw.binaries/meta/package.xml +++ b/dist/packages/org.qtproject.ifw.binaries/meta/package.xml @@ -2,7 +2,7 @@ <Package> <DisplayName>Qt Installer Framework Binaries</DisplayName> <Description>Installs the binaries, examples and help files.</Description> - <Version>4.4.1</Version> - <ReleaseDate>2022-06-17</ReleaseDate> + <Version>4.4.2</Version> + <ReleaseDate>2022-09-12</ReleaseDate> <Default>True</Default> </Package> diff --git a/dist/packages/org.qtproject.ifw/meta/package.xml b/dist/packages/org.qtproject.ifw/meta/package.xml index d10ae0348..3922d58f8 100644 --- a/dist/packages/org.qtproject.ifw/meta/package.xml +++ b/dist/packages/org.qtproject.ifw/meta/package.xml @@ -2,8 +2,8 @@ <Package> <DisplayName>Qt Installer Framework</DisplayName> <Description>Installs the Qt Installer Framework.</Description> - <Version>4.4.1</Version> - <ReleaseDate>2022-06-17</ReleaseDate> + <Version>4.4.2</Version> + <ReleaseDate>2022-09-12</ReleaseDate> <Licenses> <License name="The Qt Company GPL Exception 1.0" file="LICENSE.GPL3-EXCEPT" /> <License name="Third Party Code Licenses" file="3RDPARTY" /> diff --git a/installerfw.pri b/installerfw.pri index 036ee4cc3..f09d78973 100644 --- a/installerfw.pri +++ b/installerfw.pri @@ -3,9 +3,9 @@ } IFW_PRI_INCLUDED = 1 -IFW_VERSION_STR = 4.4.1 -IFW_VERSION = 0x040401 -IFW_VERSION_WIN32 = 4,4,1,0 +IFW_VERSION_STR = 4.4.2 +IFW_VERSION = 0x040402 +IFW_VERSION_WIN32 = 4,4,2,0 IFW_VERSION_STR_WIN32 = $$IFW_VERSION_STR\0 diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp index a04891303..b40556f6c 100644 --- a/src/libs/installer/commandlineparser.cpp +++ b/src/libs/installer/commandlineparser.cpp @@ -220,8 +220,8 @@ CommandLineParser::CommandLineParser() QLatin1String("socketname, key"))); addOption(QCommandLineOption(QStringList() << CommandLineOptions::scSquishPortShort << CommandLineOptions::scSquishPortLong, - QLatin1String("Give a port where Squish can connect to. If no port is given, default port 11233 is " - "used. Note: To enable Squish support you first need to build IFW with SQUISH_PATH " + QLatin1String("Give a port where Squish can connect to. If no port is given, attach to squish " + "not done. Note: To enable Squish support you first need to build IFW with SQUISH_PATH " "parameter where SQUISH_PATH is pointing to your Squish installation folder: " "<path_to_qt>/bin/qmake -r SQUISH_PATH=<pat_to_squish>"), QLatin1String("port number"))); diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index c7dfa65da..37abcea8a 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.cpp @@ -1082,10 +1082,12 @@ OperationList Component::operations(const Operation::OperationGroups &mask) cons d->m_operations.append(d->m_licenseOperation); } } - OperationList operations = d->m_operations; - QtConcurrent::blockingFilter(operations, [&](const Operation *op) { - return mask.testFlag(op->group()); - }); + OperationList operations; + std::copy_if(d->m_operations.begin(), d->m_operations.end(), std::back_inserter(operations), + [&](const Operation *op) { + return mask.testFlag(op->group()); + } + ); return operations; } diff --git a/src/libs/installer/directoryguard.cpp b/src/libs/installer/directoryguard.cpp index 9c97130a4..014d213d7 100644 --- a/src/libs/installer/directoryguard.cpp +++ b/src/libs/installer/directoryguard.cpp @@ -28,6 +28,7 @@ #include "directoryguard.h" +#include "fileutils.h" #include "globals.h" #include "errors.h" @@ -92,8 +93,7 @@ QStringList DirectoryGuard::tryCreate() toCreate = QDir(p); } - QDir dir(m_path); - m_created = dir.mkpath(m_path); + m_created = QInstaller::createDirectoryWithParents(m_path); if (!m_created) { throw Error(QCoreApplication::translate("DirectoryGuard", "Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path))); diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp index 5ff2d9a1a..986b9d8c8 100644 --- a/src/libs/installer/extractarchiveoperation.cpp +++ b/src/libs/installer/extractarchiveoperation.cpp @@ -193,20 +193,7 @@ bool ExtractArchiveOperation::performOperation() QFileInfo targetDirectoryInfo(fileDirectory); - // We need to create the directory structure step-by-step to avoid a rare race condition - // on Windows. QDir::mkpath() doesn't check if the leading directories were created elsewhere - // (like from within another thread) after the initial check that the given path requires - // creating also parent directories. - // - // On Unix patforms this case is handled by QFileSystemEngine though. - QDir resourcesDir(resourcesPath); - if (!resourcesDir.exists()) - resourcesDir.mkdir(resourcesPath); - - QDir resourceFileDir(targetDirectoryInfo.absolutePath()); - if (!resourceFileDir.exists()) - resourceFileDir.mkdir(targetDirectoryInfo.absolutePath()); - + QInstaller::createDirectoryWithParents(targetDirectoryInfo.absolutePath()); setDefaultFilePermissions(resourcesPath, DefaultFilePermissions::Executable); setDefaultFilePermissions(targetDirectoryInfo.absolutePath(), DefaultFilePermissions::Executable); @@ -259,6 +246,9 @@ bool ExtractArchiveOperation::undoOperation() if (!useStringListType) deleteDataFile(m_relocatedDataFileName); + // Remove the installerResources directory if it is empty. + QDir(targetDir).rmdir(QLatin1String("installerResources")); + return true; } diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp index 5a89073fc..11ae397fd 100644 --- a/src/libs/installer/fileutils.cpp +++ b/src/libs/installer/fileutils.cpp @@ -449,6 +449,52 @@ void QInstaller::mkpath(const QString &path) /*! \internal + Creates directory \a path including all parent directories. Return \c true on + success, \c false otherwise. + + On Windows \c QDir::mkpath() doesn't check if the leading directories were created + elsewhere (i.e. in another thread) after the initial check that the given path + requires creating also parent directories, and returns \c false. + + On Unix platforms this case is handled different by QFileSystemEngine though, + which checks for \c EEXIST error code in case any of the recursive directories + could not be created. + + Compared to \c QInstaller::mkpath() and \c QDir::mkpath() this function checks if + each parent directory to-be-created were created elsewhere. +*/ +bool QInstaller::createDirectoryWithParents(const QString &path) +{ + if (path.isEmpty()) + return false; + + QFileInfo dirInfo(path); + if (dirInfo.exists() && dirInfo.isDir()) + return true; + + // bail out if we are at the root directory + if (dirInfo.isRoot()) + return false; + + QDir dir(path); + if (dir.mkdir(path)) + return true; + + // mkdir failed, try to create the parent directory + if (!createDirectoryWithParents(dirInfo.absolutePath())) + return false; + + // now try again + if (dir.mkdir(path)) + return true; + + // directory may be have also been created elsewhere + return (dirInfo.exists() && dirInfo.isDir()); +} + +/*! + \internal + Generates and returns a temporary file name. The name can start with a template \a templ. */ diff --git a/src/libs/installer/fileutils.h b/src/libs/installer/fileutils.h index 8b79ce052..1114cd60c 100644 --- a/src/libs/installer/fileutils.h +++ b/src/libs/installer/fileutils.h @@ -89,6 +89,7 @@ private: void INSTALLER_EXPORT mkdir(const QString &path); void INSTALLER_EXPORT mkpath(const QString &path); + bool INSTALLER_EXPORT createDirectoryWithParents(const QString &path); #ifdef Q_OS_MACOS void INSTALLER_EXPORT mkalias(const QString &path, const QString &alias); #endif diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp index e321872ee..9f8293545 100644 --- a/src/libs/installer/installercalculator.cpp +++ b/src/libs/installer/installercalculator.cpp @@ -107,9 +107,7 @@ bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &co // user explicitly selects it to install. Increase the references to // know when the component should be removed from install const QStringList dependenciesList = component->currentDependencies(); - QSet<QString> allDependencies(dependenciesList.begin(), dependenciesList.end()); - - foreach (const QString &dependencyComponentName, allDependencies) + for (const QString &dependencyComponentName : dependenciesList) calculateComponentDependencyReferences(dependencyComponentName, component); continue; } @@ -184,9 +182,8 @@ void InstallerCalculator::realAppendToInstallComponents(Component *component, co bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version, bool revertFromInstall) { const QStringList dependenciesList = component->currentDependencies(); - const QSet<QString> allDependencies(dependenciesList.begin(), dependenciesList.end()); QString requiredDependencyVersion = version; - foreach (const QString &dependencyComponentName, allDependencies) { + for (const QString &dependencyComponentName : dependenciesList) { // PackageManagerCore::componentByName returns 0 if dependencyComponentName contains a // version which is not available Component *dependencyComponent = @@ -345,8 +342,7 @@ void InstallerCalculator::calculateComponentDependencyReferences(const QString d m_referenceCount.insert(dependencyComponentName, value); const QStringList dependenciesList = dependencyComponent->currentDependencies(); - QSet<QString> allDependencies(dependenciesList.begin(), dependenciesList.end()); - for (const QString &depComponentName : allDependencies) { + for (const QString &depComponentName : dependenciesList) { Component *dependencyComponent = PackageManagerCore::componentByName(depComponentName, m_allComponents); calculateComponentDependencyReferences(depComponentName, dependencyComponent); diff --git a/src/libs/installer/licenseoperation.cpp b/src/libs/installer/licenseoperation.cpp index a5a61c71f..dc863c1da 100644 --- a/src/libs/installer/licenseoperation.cpp +++ b/src/libs/installer/licenseoperation.cpp @@ -80,13 +80,14 @@ bool LicenseOperation::performOperation() for (QVariantMap::const_iterator it = licenses.constBegin(); it != licenses.constEnd(); ++it) { QFile file(targetDir + QLatin1Char('/') + it.key()); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { setError(UserDefinedError); setErrorString(tr("Can not write license file \"%1\".").arg(QDir::toNativeSeparators(file.fileName()))); return false; } QTextStream stream(&file); + stream.setCodec("UTF-8"); stream << it.value().toString(); } diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 66630b98d..d90dfdec2 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -2311,7 +2311,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo runner.setType(Operation::Perform); const QHash<Operation *, bool> results = runner.run(); - const OperationList performedOperations = backupResults.keys(); + const OperationList performedOperations = results.keys(); QString error; for (auto &operation : performedOperations) { @@ -2508,6 +2508,14 @@ void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::Che void PackageManagerCorePrivate::deleteMaintenanceTool() { + QDir resourcePath(QFileInfo(maintenanceToolName()).dir()); + resourcePath.remove(QLatin1String("installer.dat")); + QDir installDir(targetDir()); + installDir.remove(m_data.settings().maintenanceToolName() + QLatin1String(".dat")); + installDir.remove(QLatin1String("network.xml")); + installDir.remove(m_data.settings().maintenanceToolIniFile()); + QInstaller::VerboseWriter::instance()->setFileName(QString()); + installDir.remove(m_core->value(QLatin1String("LogFileName"), QLatin1String("InstallationLog.txt"))); #ifdef Q_OS_WIN // Since Windows does not support that the maintenance tool deletes itself we have to go with a rather dirty // hack. What we do is to create a batchfile that will try to remove the maintenance tool once per second. Then @@ -2520,6 +2528,7 @@ void PackageManagerCorePrivate::deleteMaintenanceTool() if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) throw Error(tr("Cannot prepare removal")); + const bool removeTargetDir = QVariant(m_core->value(scRemoveTargetDir)).toBool(); QTextStream batch(&f); batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n"; batch << "file = WScript.Arguments.Item(0)\n"; @@ -2531,10 +2540,12 @@ void PackageManagerCorePrivate::deleteMaintenanceTool() batch << " fso.DeleteFile(file)\n"; batch << " WScript.Sleep(1000)\n"; batch << "wend\n"; -// batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n"; + if (!removeTargetDir) + batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n"; batch << " Set folder = Nothing\n"; batch << " fso.DeleteFolder folderpath, true\n"; -// batch << "end if\n"; + if (!removeTargetDir) + batch << "end if\n"; batch << "fso.DeleteFile(WScript.ScriptFullName)\n"; f.close(); @@ -2542,11 +2553,7 @@ void PackageManagerCorePrivate::deleteMaintenanceTool() QStringList arguments; arguments << QLatin1String("//Nologo") << batchfile; // execute the batchfile arguments << QDir::toNativeSeparators(QFileInfo(installerBinaryPath()).absoluteFilePath()); - if (!m_performedOperationsOld.isEmpty()) { - const Operation *const op = m_performedOperationsOld.first(); - if (op->name() == QLatin1String("Mkdir")) // the target directory name - arguments << QDir::toNativeSeparators(QFileInfo(op->arguments().first()).absoluteFilePath()); - } + arguments << targetDir(); if (!QProcessWrapper::startDetached(QLatin1String("cscript"), arguments, QDir::rootPath())) throw Error(tr("Cannot start removal")); diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp index 3de9f5b60..d54b5c2ab 100644 --- a/src/libs/installer/uninstallercalculator.cpp +++ b/src/libs/installer/uninstallercalculator.cpp @@ -205,7 +205,8 @@ void UninstallerCalculator::appendVirtualComponentsToUninstall(const bool revers // Check if installed or about to be updated -packages are dependant on the package const QList<Component*> installDependants = m_core->installDependants(virtualComponent); for (Component *dependant : installDependants) { - if (dependant->isInstalled() && !m_componentsToUninstall.contains(dependant)) { + if ((dependant->isInstalled() && !m_componentsToUninstall.contains(dependant)) + || m_core->orderedComponentsToInstall().contains(dependant)) { required = true; break; } diff --git a/src/sdk/installerbase.cpp b/src/sdk/installerbase.cpp index 32df0b550..dcbf15228 100644 --- a/src/sdk/installerbase.cpp +++ b/src/sdk/installerbase.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -98,17 +98,15 @@ int InstallerBase::run() return status; #ifdef ENABLE_SQUISH - int squishPort = 11233; if (m_parser.isSet(CommandLineOptions::scSquishPortLong)) { - squishPort = m_parser.value(CommandLineOptions::scSquishPortLong).toInt(); - } - if (squishPort != 0) { - if (Squish::allowAttaching(squishPort)) - qCDebug(QInstaller::lcDeveloperBuild) << "Attaching to squish port " << squishPort << " succeeded"; - else - qCDebug(QInstaller::lcDeveloperBuild) << "Attaching to squish failed."; - } else { - qCWarning(QInstaller::lcDeveloperBuild) << "Invalid squish port number: " << squishPort; + const int maxSquishPortNumber = 65535; + int squishPort = m_parser.value(CommandLineOptions::scSquishPortLong).toInt(); + if (squishPort <= 0 || squishPort > maxSquishPortNumber) { + qWarning().noquote() << "Invalid Squish port:" << squishPort; + } else { + Squish::allowAttaching(squishPort); + qCDebug(QInstaller::lcDeveloperBuild) << "Attaching to squish port" << squishPort << "succeeded"; + } } #endif const int result = QCoreApplication::instance()->exec(); diff --git a/tests/auto/installer/extractarchiveoperationtest/data.qrc b/tests/auto/installer/extractarchiveoperationtest/data.qrc index 974c0c6f7..a8cc1a892 100644 --- a/tests/auto/installer/extractarchiveoperationtest/data.qrc +++ b/tests/auto/installer/extractarchiveoperationtest/data.qrc @@ -2,6 +2,7 @@ <qresource prefix="/"> <file>data/valid.7z</file> <file>data/invalid.7z</file> + <file>data/subdirs.7z</file> <file>data/xmloperationrepository/Updates.xml</file> <file>data/xmloperationrepository/A/1.0.0content.7z</file> <file>data/xmloperationrepository/A/1.0.0content1.tar.gz</file> diff --git a/tests/auto/installer/extractarchiveoperationtest/data/subdirs.7z b/tests/auto/installer/extractarchiveoperationtest/data/subdirs.7z Binary files differnew file mode 100644 index 000000000..7f93fe106 --- /dev/null +++ b/tests/auto/installer/extractarchiveoperationtest/data/subdirs.7z diff --git a/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp b/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp index 8ceaa76c7..89492bd94 100644 --- a/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp +++ b/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp @@ -28,6 +28,7 @@ #include "../shared/packagemanager.h" +#include "concurrentoperationrunner.h" #include "init.h" #include "extractarchiveoperation.h" @@ -86,6 +87,57 @@ private slots: QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::UserDefinedError); } + void testConcurrentExtractWithCompetingData() + { + // Suppress warnings about already deleted installerResources file + qInstallMessageHandler(silentTestMessageHandler); + + const QString testDirectory = generateTemporaryFileName() + + "/subdir1/subdir2/subdir3/subdir4/subdir5/"; + + QStringList created7zList; + + OperationList operations; + for (int i = 0; i < 100; ++i) { + ExtractArchiveOperation *op = new ExtractArchiveOperation(nullptr); + // We add the same data multiple times, and extract to same directory. + // Can't open the same archive multiple times however so it needs to + // be copied to unique files. + const QString new7zPath = generateTemporaryFileName() + ".7z"; + QFile old7z(":///data/subdirs.7z"); + QVERIFY(old7z.copy(new7zPath)); + + op->setArguments(QStringList() << new7zPath << testDirectory); + operations.append(op); + } + ConcurrentOperationRunner runner(&operations, Operation::Backup); + + const QHash<Operation *, bool> backupResults = runner.run(); + const OperationList backupOperations = backupResults.keys(); + + for (auto *operation : backupOperations) + QVERIFY2((backupResults.value(operation) && operation->error() == Operation::NoError), + operation->errorString().toLatin1()); + + runner.setType(Operation::Perform); + const QHash<Operation *, bool> results = runner.run(); + const OperationList performedOperations = results.keys(); + + for (auto *operation : performedOperations) + QVERIFY2((results.value(operation) && operation->error() == Operation::NoError), + operation->errorString().toLatin1()); + + for (auto *operation : operations) + QVERIFY(operation->undoOperation()); + + qDeleteAll(operations); + + for (const QString &archive : created7zList) + QFile::remove(archive); + + QDir().rmdir(testDirectory); + } + void testExtractArchiveFromXML() { m_testDirectory = QInstaller::generateTemporaryFileName(); |