diff options
author | Katja Marttila <katja.marttila@qt.io> | 2018-02-28 09:54:22 +0200 |
---|---|---|
committer | Katja Marttila <katja.marttila@qt.io> | 2018-04-05 06:54:21 +0000 |
commit | d2ae9c16c3427c272d41e9a471f705ad8947a97a (patch) | |
tree | 4ed6b3cafd28b11f02ef4f4faa6164bcfbd85b63 | |
parent | 6664ca85f09d6ae195ac30f83a60d53c2355da0f (diff) |
Add attribute to mark parts of install tree unstable
This commit adds new AllowUnstableComponents configuration. Setting
AllowUnstablecomponents to true in config.xml will
* allow installing other components when there are errors in scripts
* allow installing other components when there are missing dependencies
* will mark the 'broken' components uninstallable in treeview
Task-number: QTIFW-930
Change-Id: I8d28cf9c4b0401e0bb76795e87d581f39b64f128
Reviewed-by: Iikka Eklund <iikka.eklund@qt.io>
32 files changed, 744 insertions, 12 deletions
diff --git a/examples/unstablecomponent/README b/examples/unstablecomponent/README new file mode 100644 index 000000000..6c081844a --- /dev/null +++ b/examples/unstablecomponent/README @@ -0,0 +1,5 @@ +Shows how to setup an installer that allows install even if there are broken components + +Generate installer with: + +binarycreator -c config/config.xml -p packages installer diff --git a/examples/unstablecomponent/config/config.xml b/examples/unstablecomponent/config/config.xml new file mode 100644 index 000000000..bd9ac49ea --- /dev/null +++ b/examples/unstablecomponent/config/config.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Installer> + <Name>Unstable components Example</Name> + <Version>1.0.0</Version> + <Title>Unstable components Example</Title> + <Publisher>Qt-Project</Publisher> + <StartMenuDir>Qt IFW Examples</StartMenuDir> + <TargetDir>@HomeDir@/IfwExamples/unstablecomponent</TargetDir> + <AllowUnstableComponents>true</AllowUnstableComponents> +</Installer> diff --git a/examples/unstablecomponent/packages/componentB/meta/package.xml b/examples/unstablecomponent/packages/componentB/meta/package.xml new file mode 100644 index 000000000..acf9a3a28 --- /dev/null +++ b/examples/unstablecomponent/packages/componentB/meta/package.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Component B</DisplayName> + <Description>This component does not depend on any other component.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>90</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentDependsonComponentMissingDependency/meta/package.xml b/examples/unstablecomponent/packages/componentDependsonComponentMissingDependency/meta/package.xml new file mode 100644 index 000000000..42d7cbb30 --- /dev/null +++ b/examples/unstablecomponent/packages/componentDependsonComponentMissingDependency/meta/package.xml @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>ComponentDependsonComponentMissingDependency</DisplayName> + <Description>This component depends on component which is depending on component with missing dependency. User is not able to select this component for install.</Description> + <Dependencies>componentMissingDependency</Dependencies> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>70</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/installscript.js b/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/installscript.js new file mode 100644 index 000000000..7ad2f4db3 --- /dev/null +++ b/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/installscript.js @@ -0,0 +1,33 @@ +/************************************************************************** +** +** Copyright (C) 2018 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$ +** +**************************************************************************/ + +function Component() +{ + component.addDependency("componentInvalidScipt"); +} + diff --git a/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/package.xml b/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/package.xml new file mode 100644 index 000000000..4df66da40 --- /dev/null +++ b/examples/unstablecomponent/packages/componentDependsonComponentwithInvalidScript/meta/package.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Component depends on ComponentWithInvalidScript)</DisplayName> + <Description>Depends on component with invalid script. User is not able to select this component for install.</Description> + <Default>true</Default> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <Script>installscript.js</Script> + <SortingPriority>30</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentE/meta/package.xml b/examples/unstablecomponent/packages/componentE/meta/package.xml new file mode 100644 index 000000000..a19c4f7a9 --- /dev/null +++ b/examples/unstablecomponent/packages/componentE/meta/package.xml @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Component E (forced)</DisplayName> + <Description>This is a forced component that is always installed.</Description> + <ForcedInstallation>true</ForcedInstallation> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>60</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml new file mode 100644 index 000000000..717eb723f --- /dev/null +++ b/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Subsubcomponent 1</DisplayName> + <Description>This component does not depend on any other component.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>100</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent2/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent2/meta/package.xml new file mode 100644 index 000000000..29bc65ffa --- /dev/null +++ b/examples/unstablecomponent/packages/componentF.subcomponent1.subsubcomponent2/meta/package.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Subsubcomponent 2</DisplayName> + <Description>This component does not depend on any other component.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>50</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentF.subcomponent1/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent1/meta/package.xml new file mode 100644 index 000000000..7111a9031 --- /dev/null +++ b/examples/unstablecomponent/packages/componentF.subcomponent1/meta/package.xml @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Subcomponent 1</DisplayName> + <Description>This component contains 2 leaf components.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>100</SortingPriority> + <Dependencies>componentA</Dependencies> +</Package> diff --git a/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent1/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent1/meta/package.xml new file mode 100644 index 000000000..717eb723f --- /dev/null +++ b/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent1/meta/package.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Subsubcomponent 1</DisplayName> + <Description>This component does not depend on any other component.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>100</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent2/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent2/meta/package.xml new file mode 100644 index 000000000..29bc65ffa --- /dev/null +++ b/examples/unstablecomponent/packages/componentF.subcomponent2.subsubcomponent2/meta/package.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Subsubcomponent 2</DisplayName> + <Description>This component does not depend on any other component.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>50</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentF.subcomponent2/meta/package.xml b/examples/unstablecomponent/packages/componentF.subcomponent2/meta/package.xml new file mode 100644 index 000000000..131f4799e --- /dev/null +++ b/examples/unstablecomponent/packages/componentF.subcomponent2/meta/package.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Subcomponent 2</DisplayName> + <Description>This component contains 2 leaf components.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>50</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentF/meta/package.xml b/examples/unstablecomponent/packages/componentF/meta/package.xml new file mode 100644 index 000000000..7f24c4f35 --- /dev/null +++ b/examples/unstablecomponent/packages/componentF/meta/package.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>Component F</DisplayName> + <Description>This component contains 2 subcomponents.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>40</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentMissingDependency/meta/package.xml b/examples/unstablecomponent/packages/componentMissingDependency/meta/package.xml new file mode 100644 index 000000000..52cb99c1a --- /dev/null +++ b/examples/unstablecomponent/packages/componentMissingDependency/meta/package.xml @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>ComponentMissingDependency</DisplayName> + <Description>This component depends on component which does not exists. User is not able to select this component for install.</Description> + <Dependencies>componentDoesnotExists</Dependencies> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>80</SortingPriority> +</Package> diff --git a/examples/unstablecomponent/packages/componentWithInvalidScipt/data/installcontent.txt b/examples/unstablecomponent/packages/componentWithInvalidScipt/data/installcontent.txt new file mode 100644 index 000000000..f40001983 --- /dev/null +++ b/examples/unstablecomponent/packages/componentWithInvalidScipt/data/installcontent.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/installscript.js b/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/installscript.js new file mode 100644 index 000000000..4ca6a8816 --- /dev/null +++ b/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/installscript.js @@ -0,0 +1,33 @@ +/************************************************************************** +** +** Copyright (C) 2018 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$ +** +**************************************************************************/ + +function Component() +{ + component.invalidScriptCall() +} + diff --git a/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/package.xml b/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/package.xml new file mode 100644 index 000000000..7bf69ccc4 --- /dev/null +++ b/examples/unstablecomponent/packages/componentWithInvalidScipt/meta/package.xml @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<Package> + <DisplayName>ComponentWithInvalidScript</DisplayName> + <Description>This component has an invalid script. User is not able to select this component for install.</Description> + <Version>1.0.0</Version> + <ReleaseDate>2018-02-02</ReleaseDate> + <SortingPriority>100</SortingPriority> + <Script>installscript.js</Script> +</Package> diff --git a/examples/unstablecomponent/unstablecomponent.pro b/examples/unstablecomponent/unstablecomponent.pro new file mode 100644 index 000000000..415df49d5 --- /dev/null +++ b/examples/unstablecomponent/unstablecomponent.pro @@ -0,0 +1,13 @@ +TEMPLATE = aux + +INSTALLER = installer + +INPUT = $$PWD/config/config.xml $$PWD/packages +example.input = INPUT +example.output = $$INSTALLER +example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} +example.CONFIG += target_predeps no_link combine + +QMAKE_EXTRA_COMPILERS += example + +OTHER_FILES = README diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index 9be2357cd..65dfbbc8d 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.cpp @@ -67,6 +67,7 @@ static const QLatin1String scCurrentState("CurrentState"); static const QLatin1String scForcedInstallation("ForcedInstallation"); static const QLatin1String scCheckable("Checkable"); static const QLatin1String scExpandedByDefault("ExpandedByDefault"); +static const QLatin1String scUnstable("Unstable"); /*! \inmodule QtInstallerFramework @@ -511,9 +512,29 @@ void Component::loadComponentScript(const QString &fileName) { // introduce the component object as javascript value and call the name to check that it // was successful - d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName, - QString::fromLatin1("var component = installer.componentByName('%1'); component.name;") - .arg(name())); + try { + d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName, + QString::fromLatin1("var component = installer.componentByName('%1'); component.name;") + .arg(name())); + if (packageManagerCore()->settings().allowUnstableComponents()) { + // Check if component has dependency to a broken component. Dependencies to broken + // components are checked if error is thrown but if dependency to a broken + // component is added in script, the script might not be loaded yet + foreach (QString dependency, dependencies()) { + Component *dependencyComponent = packageManagerCore()->componentByName + (PackageManagerCore::checkableName(dependency)); + if (dependencyComponent && dependencyComponent->isUnstable()) + setUnstable(); + } + } + } catch (const Error &error) { + if (packageManagerCore()->settings().allowUnstableComponents()) { + setUnstable(); + qWarning() << error.message(); + } else { + throw error; + } + } emit loaded(); languageChanged(); @@ -1008,6 +1029,13 @@ Operation *Component::createOperation(const QString &operationName, const QStrin return operation; } +void Component::markComponentUnstable() +{ + setValue(scDefault, scFalse); + setCheckState(Qt::Unchecked); + setValue(scUnstable, scTrue); +} + namespace { inline bool convert(QQmlV4Function *func, QStringList *toArgs) @@ -1339,6 +1367,31 @@ bool Component::isUninstalled() const return scUninstalled == d->m_vars.value(scCurrentState); } +bool Component::isUnstable() const +{ + return scTrue == d->m_vars.value(scUnstable); +} + +void Component::setUnstable() +{ + QList<Component*> dependencies = d->m_core->dependees(this); + // Mark this component unstable + markComponentUnstable(); + + // Marks all components unstable that depend on the unstable component + foreach (Component *dependency, dependencies) { + dependency->markComponentUnstable(); + foreach (Component *descendant, dependency->descendantComponents()) { + descendant->markComponentUnstable(); + } + } + + // Marks all child components unstable + foreach (Component *descendant, this->descendantComponents()) { + descendant->markComponentUnstable(); + } +} + /*! Returns whether the user wants to uninstall the component. @@ -1424,13 +1477,21 @@ void Component::updateModelData(const QString &key, const QString &data) const QString &updateInfo = d->m_vars.value(scUpdateText); if (!d->m_core->isUpdater() || updateInfo.isEmpty()) { - const QString tooltipText + QString tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription)); + if (isUnstable()) { + tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. " + "This component can not be installed."); + } setData(tooltipText, Qt::ToolTipRole); } else { - const QString tooltipText + QString tooltipText = d->m_vars.value(scDescription) + QLatin1String("<br><br>") + tr("Update Info: ") + updateInfo; + if (isUnstable()) { + tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. " + "This component can not be updated."); + } setData(tooltipText, Qt::ToolTipRole); } diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h index b11fd4cef..574117b6d 100644 --- a/src/libs/installer/component.h +++ b/src/libs/installer/component.h @@ -62,6 +62,7 @@ class INSTALLER_EXPORT Component : public QObject, public ComponentModelHelper Q_PROPERTY(bool default READ isDefault) Q_PROPERTY(bool installed READ isInstalled) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + Q_PROPERTY(bool unstable READ isUnstable) public: explicit Component(PackageManagerCore *core); @@ -182,6 +183,8 @@ public: Q_INVOKABLE bool componentChangeRequested(); + bool isUnstable() const; + void setUnstable(); bool isVirtual() const; bool isSelected() const; @@ -212,6 +215,7 @@ private: const QString ¶meter8 = QString(), const QString ¶meter9 = QString(), const QString ¶meter10 = QString()); Operation *createOperation(const QString &operationName, const QStringList ¶meters); + void markComponentUnstable(); private: QString validatorCallbackName; diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h index 60cbd8075..b9f50b557 100644 --- a/src/libs/installer/component_p.h +++ b/src/libs/installer/component_p.h @@ -63,6 +63,7 @@ public: bool m_autoCreateOperations; bool m_operationsCreatedSuccessfully; bool m_updateIsAvailable; + bool m_unstable; QString m_componentName; QUrl m_repositoryUrl; diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp index 0f8a47c38..078afc50a 100644 --- a/src/libs/installer/componentmodel.cpp +++ b/src/libs/installer/componentmodel.cpp @@ -222,14 +222,15 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const return component->data(Qt::UserRole + index.column()); } if (role == Qt::CheckStateRole) { - if (!component->isCheckable()) - return QVariant(); - if (!component->autoDependencies().isEmpty()) + if (!component->isCheckable() || !component->autoDependencies().isEmpty() || component->isUnstable()) return QVariant(); } if (role == ComponentModelHelper::ExpandedByDefault) { return component->isExpandedByDefault(); } + if (component->isUnstable() && role == Qt::ForegroundRole) { + return QVariant(QColor(Qt::lightGray)); + } return component->data(role); } return QVariant(); diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h index 7f44acbe7..ddfa8c555 100644 --- a/src/libs/installer/constants.h +++ b/src/libs/installer/constants.h @@ -89,6 +89,7 @@ static const QLatin1String scUrlQueryString("UrlQueryString"); static const QLatin1String scProductUUID("ProductUUID"); static const QLatin1String scAllUsers("AllUsers"); static const QLatin1String scSupportsModify("SupportsModify"); +static const QLatin1String scAllowUnstableComponents("AllowUnstableComponents"); const char scRelocatable[] = "@RELOCATABLE_PATH@"; diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp index d44450eb2..4e0d7fdfa 100644 --- a/src/libs/installer/installercalculator.cpp +++ b/src/libs/installer/installercalculator.cpp @@ -30,6 +30,7 @@ #include "component.h" #include "packagemanagercore.h" +#include "settings.h" #include <QDebug> @@ -94,7 +95,8 @@ QString InstallerCalculator::componentsToInstallError() const void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version) { - if (!component->isInstalled(version) || component->updateRequested()) { + if (!component->isUnstable() && + (!component->isInstalled(version) || component->updateRequested())) { m_orderedComponentsToInstall.append(component); m_toInstallComponentIds.insert(component->name()); } @@ -169,7 +171,12 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q component->name()); qWarning().noquote() << errorMessage; m_componentsToInstallError.append(errorMessage); - return false; + if (component->packageManagerCore()->settings().allowUnstableComponents()) { + component->setUnstable(); + return true; + } else { + return false; + } } //Check if component requires higher version than what might be already installed bool isUpdateRequired = false; diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp index 79d4bcddc..3d5d4b858 100644 --- a/src/libs/installer/settings.cpp +++ b/src/libs/installer/settings.cpp @@ -256,7 +256,7 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix, << scWizardDefaultWidth << scWizardDefaultHeight << scRepositorySettingsPageVisible << scTargetConfigurationFile << scRemoteRepositories << scTranslations << scUrlQueryString << QLatin1String(scControlScript) - << scCreateLocalRepository << scInstallActionColumnVisible << scSupportsModify; + << scCreateLocalRepository << scInstallActionColumnVisible << scSupportsModify << scAllowUnstableComponents; Settings s; s.d->m_data.insert(scPrefix, prefix); @@ -325,7 +325,8 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix, s.d->m_data.insert(scCreateLocalRepository, false); if (!s.d->m_data.contains(scInstallActionColumnVisible)) s.d->m_data.insert(scInstallActionColumnVisible, false); - + if (!s.d->m_data.contains(scAllowUnstableComponents)) + s.d->m_data.insert(scAllowUnstableComponents, false); return s; } @@ -743,3 +744,13 @@ bool Settings::supportsModify() const { return d->m_data.value(scSupportsModify, true).toBool(); } + +bool Settings::allowUnstableComponents() const +{ + return d->m_data.value(scAllowUnstableComponents, true).toBool(); +} + +void Settings::setAllowUnstableComponents(bool allow) +{ + d->m_data.insert(scAllowUnstableComponents, allow); +} diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h index 3dc1c99c3..45c4fbcc3 100644 --- a/src/libs/installer/settings.h +++ b/src/libs/installer/settings.h @@ -154,6 +154,9 @@ public: bool supportsModify() const; + bool allowUnstableComponents() const; + void setAllowUnstableComponents(bool allow); + private: class Private; QSharedDataPointer<Private> d; diff --git a/tests/auto/installer/brokeninstaller/brokeninstaller.pro b/tests/auto/installer/brokeninstaller/brokeninstaller.pro new file mode 100644 index 000000000..e48733ac6 --- /dev/null +++ b/tests/auto/installer/brokeninstaller/brokeninstaller.pro @@ -0,0 +1,8 @@ +include(../../qttest.pri) + +QT -= gui +QT += network xml qml + +SOURCES += tst_brokeninstaller.cpp + +RESOURCES += components.qrc diff --git a/tests/auto/installer/brokeninstaller/components.qrc b/tests/auto/installer/brokeninstaller/components.qrc new file mode 100644 index 000000000..1431393a6 --- /dev/null +++ b/tests/auto/installer/brokeninstaller/components.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>data/updates.xml</file> + <file>data/broken_script.qs</file> + </qresource> +</RCC> diff --git a/tests/auto/installer/brokeninstaller/data/broken_script.qs b/tests/auto/installer/brokeninstaller/data/broken_script.qs new file mode 100644 index 000000000..a1574f46d --- /dev/null +++ b/tests/auto/installer/brokeninstaller/data/broken_script.qs @@ -0,0 +1,31 @@ +/************************************************************************** +** +** Copyright (C) 2018 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$ +** +**************************************************************************/ +function Component() +{ + installer.invalidScriptCall() +} diff --git a/tests/auto/installer/brokeninstaller/data/updates.xml b/tests/auto/installer/brokeninstaller/data/updates.xml new file mode 100644 index 000000000..a20275120 --- /dev/null +++ b/tests/auto/installer/brokeninstaller/data/updates.xml @@ -0,0 +1,163 @@ +<Updates> + <ApplicationName>Your application</ApplicationName> + <ApplicationVersion>1.2.3</ApplicationVersion> + <Checksum>true</Checksum> + <PackageUpdate> + <Name>componentinvalidscript</Name> + <DisplayName>Invalid script component</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Default>true</Default> + <SortingPriority>1</SortingPriority> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentb</Name> + <DisplayName>The second root component</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Default>true</Default> + <Script>installscript.qs</Script> + <SortingPriority>1</SortingPriority> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentc</Name> + <DisplayName>The third root component</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Default>true</Default> + <Script>installscript.qs</Script> + <SortingPriority>1</SortingPriority> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentc.subnode</Name> + <DisplayName>A subcomponent for the third root, dependency to component with invalid script</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Default>true</Default> + <Script>installscript.qs</Script> + <SortingPriority>1</SortingPriority> + <Dependencies>componentinvalidscript</Dependencies> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentc.subnode.sub</Name> + <DisplayName>A subcomponent for the third roots subcomponent</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Script>installscript.qs</Script> + <Default>true</Default> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentmissingdependency</Name> + <DisplayName>A component that has missing sha</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Default>true</Default> + <Script>installscript.qs</Script> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentd</Name> + <DisplayName>Component that depends on component which is missing dependency</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Script>installscript.qs</Script> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentmissingcontent</Name> + <DisplayName>A component which is missing 7z</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Default>true</Default> + <Script>installscript.qs</Script> + <SortingPriority>0</SortingPriority> + <DownloadableArchives>content.7z</DownloadableArchives> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentmissingcontent.sub</Name> + <DisplayName>A subcomponent of component which is missing 7z</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Checkable>true</Checkable> + <Default>true</Default> + <Script>installscript.qs</Script> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> + <PackageUpdate> + <Name>componentE</Name> + <DisplayName>A component which is dependent on subcomponent of missing 7z component</DisplayName> + <Description>Install this example.</Description> + <Version>0.1.0-1</Version> + <ReleaseDate>2010-09-21</ReleaseDate> + <Script>installscript.qs</Script> + <Checkable>true</Checkable> + <Default>true</Default> + <Dependencies>componentmissingcontent.sub</Dependencies> + <UpdateFile UncompressedSize="61" + CompressedSize="61"/> + <Licenses> + <License name="Beer Public License Agreement" + file="license.txt"/> + </Licenses> + </PackageUpdate> +</Updates> diff --git a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp new file mode 100644 index 000000000..95f4ac77d --- /dev/null +++ b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp @@ -0,0 +1,228 @@ +/************************************************************************** +** +** Copyright (C) 2018 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 "component.h" +#include "componentmodel.h" +#include "updatesinfo_p.h" +#include "packagemanagercore.h" +#include "settings.h" + +#include <QTest> +#include <QtCore/QLocale> +#include <QRegularExpression> + +using namespace KDUpdater; +using namespace QInstaller; + +#define EXPECTED_COUNTS_COMPONENTS_VISIBLE 10 +#define EXPECTED_COUNT_ROOT_COMPONENTS 7 +#define EXPECTED_COUNT_INVALID_SCRIPT 7 +#define EXPECTED_COUNT_MISSING_DEPENDENCY 8 + +static const char componentInvalidScript[] = "componentinvalidscript"; +static const char componentB[] = "componentb"; + +static const char componentC[] = "componentc"; +static const char componentCSubnodeDependsOnInvalidScriptComponent[] = "componentc.subnode"; +static const char componentCSubnodeDependsOnInvalidScriptComponentSub[] = "componentc.subnode.sub"; + +static const char componentMissingDependency[] = "componentmissingdependency"; +static const char componentDependsOnMissingDependency[] = "componentd"; + +static const char componentMissingContent[] = "componentmissingcontent"; +static const char componentMissingContentSub[] = "componentmissingcontent.sub"; +static const char componentDependsOnMissingContentSub[] = "componentE"; + +static const QMap<QString, QString> rootComponentDisplayNames = { + {"", QLatin1String("The root component")}, + {"ru_ru", QString::fromUtf8("Корневая компонента")}, + {"de_de", QString::fromUtf8("Wurzel Komponente")} +}; + +using namespace QInstaller; + +class tst_BrokenInstaller : public QObject +{ + Q_OBJECT + +public: + enum Option { + NoFlags = 0x00, + VirtualsVisible = 0x01, + NoForcedInstallation = 0x02 + }; + Q_DECLARE_FLAGS(Options, Option); + +private slots: + void initTestCase() + { + + m_checkedComponentsWithBrokenScript << componentB << componentMissingDependency << componentDependsOnMissingDependency << componentMissingContent + << componentMissingContentSub << componentDependsOnMissingContentSub; + m_uncheckedComponentsWithBrokenScript << componentInvalidScript << componentCSubnodeDependsOnInvalidScriptComponent + << componentCSubnodeDependsOnInvalidScriptComponentSub << componentC; + + m_checkedComponentsWithMissingDependency << componentB << componentMissingContent << componentMissingContentSub << componentDependsOnMissingContentSub + << componentInvalidScript << componentCSubnodeDependsOnInvalidScriptComponent + << componentCSubnodeDependsOnInvalidScriptComponentSub << componentC; + m_uncheckedComponentsWithMissingDependency << componentMissingDependency << componentDependsOnMissingDependency; + + } + + void testNameToIndexAndIndexToName() + { + PackageManagerCore core; + QList<Component*> rootComponents = loadComponents(core); + + ComponentModel *model = new ComponentModel(1, &core); + model->setRootComponents(rootComponents); + // all names should be resolvable + QStringList all; + all << m_checkedComponentsWithBrokenScript << m_uncheckedComponentsWithBrokenScript << m_partiallyCheckedComponentsWithBrokenScript; + foreach (const QString &name, all) { + QVERIFY(model->indexFromComponentName(name).isValid()); + QVERIFY(model->componentFromIndex(model->indexFromComponentName(name)) != 0); + QCOMPARE(model->componentFromIndex(model->indexFromComponentName(name))->name(), name); + } + } + + void testInvalidScript() + { + PackageManagerCore core; + QList<Component*> components = loadComponents(core); + + core.settings().setAllowUnstableComponents(true); + ComponentModel *model = new ComponentModel(1, &core); + Component *invalidScriptComponent = core.componentByName("componentinvalidscript"); + + // Using regexp to ignore the warning message as the message contains + // object address which we don't know + const QString debugMessage = QString("Exception while loading the component script"); + const QRegularExpression re(debugMessage); + QTest::ignoreMessage(QtWarningMsg, re); + invalidScriptComponent->loadComponentScript(":///data/broken_script.qs"); + + model->setRootComponents(components); + testModelState(model, m_checkedComponentsWithBrokenScript, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithBrokenScript); + } + + void testMissingDependency() + { + PackageManagerCore core; + QList<Component*> components = loadComponents(core); + + core.settings().setAllowUnstableComponents(true); + ComponentModel *model = new ComponentModel(1, &core); + + Component *invalidComponent = core.componentByName("componentmissingdependency"); + invalidComponent->addDependency("missingcomponent"); + + Component *componentDependingOnMissingDependency = core.componentByName("componentd"); + componentDependingOnMissingDependency->addDependency("componentmissingdependency"); + + core.componentsToInstallNeedsRecalculation(); + model->setRootComponents(components); + + testModelState(model, m_checkedComponentsWithMissingDependency, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithMissingDependency); + } + +private: + + QList<Component*> loadComponents(PackageManagerCore &core) + { + UpdatesInfo updatesInfo; + updatesInfo.setFileName(":///data/updates.xml"); + const QList<UpdateInfo> updateInfos = updatesInfo.updatesInfo(); + + QList <Component*> components; + UpdateInfo info; + for (int i = 0; i < updateInfos.count(); i++) { + info = updateInfos.at(i); + Component *component = new Component(&core); + + // we need at least these to be able to test the model + component->setValue("Name", info.data.value("Name").toString()); + component->setValue("Virtual", info.data.value("Virtual").toString()); + component->setValue("DisplayName", info.data.value("DisplayName").toString()); + component->setValue("Checkable", info.data.value("Checkable").toString()); + component->setValue("Dependencies", info.data.value("Dependencies").toString()); + component->setCheckState(Qt::Checked); + QString forced = info.data.value("ForcedInstallation", scFalse).toString().toLower(); + if (core.noForceInstallation()) + forced = scFalse; + component->setValue("ForcedInstallation", forced); + if (forced == scTrue) { + component->setEnabled(false); + component->setCheckable(false); + } + //If name does not contain dot it is root component + qDebug()<<"component name "<<component->name(); + if (!component->name().contains('.')) { + core.appendRootComponent(component); + } else { //Contains dot, is previous components child component + components.at(i-1)->appendComponent(component); + } + components.append(component); + } + return components; + } + + void testModelState(ComponentModel *model, const QStringList &checked, const QStringList &partially, + const QStringList &unchecked) const + { + QCOMPARE(model->checked().count(), checked.count()); + QCOMPARE(model->partially().count(), partially.count()); + QCOMPARE(model->unchecked().count(), unchecked.count()); + + // these components should have checked state + foreach (Component *const component, model->checked()) + QVERIFY(checked.contains(component->name())); + + // these components should not have partially checked state + foreach (Component *const component, model->partially()) + QVERIFY(partially.contains(component->name())); + + // these components should not have checked state + foreach (Component *const component, model->unchecked()) + QVERIFY(unchecked.contains(component->name())); + } + +private: + QStringList m_checkedComponentsWithBrokenScript; + QStringList m_uncheckedComponentsWithBrokenScript; + QStringList m_partiallyCheckedComponentsWithBrokenScript; + + QStringList m_checkedComponentsWithMissingDependency; + QStringList m_uncheckedComponentsWithMissingDependency; + QStringList m_partiallyCheckedComponentsWithMissingDependency; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(tst_BrokenInstaller::Options) + +QTEST_MAIN(tst_BrokenInstaller) + +#include "tst_brokeninstaller.moc" |