diff options
author | Eike Ziller <eike.ziller@qt.io> | 2022-04-08 11:58:43 +0200 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2022-04-21 06:56:47 +0000 |
commit | 98dda165e4007d3eeda9896b21163d79693c94c0 (patch) | |
tree | 9d0b8acee3a4d68144eb7e365f8c4990416d08da | |
parent | f5b77d87da1466e2176144741d5985cb54fe5169 (diff) | |
download | qt-creator-98dda165e4007d3eeda9896b21163d79693c94c0.tar.gz |
UpdateInfo: Add some auto test
Change-Id: If97121bda98e1b09f093d0bcc8f60efb1aa18235
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
-rw-r--r-- | src/plugins/updateinfo/CMakeLists.txt | 8 | ||||
-rw-r--r-- | src/plugins/updateinfo/updateinfo.qbs | 5 | ||||
-rw-r--r-- | src/plugins/updateinfo/updateinfoplugin.cpp | 119 | ||||
-rw-r--r-- | src/plugins/updateinfo/updateinfotools.h | 163 | ||||
-rw-r--r-- | tests/auto/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/auto.qbs | 1 | ||||
-rw-r--r-- | tests/auto/updateinfo/CMakeLists.txt | 5 | ||||
-rw-r--r-- | tests/auto/updateinfo/tst_updateinfo.cpp | 78 | ||||
-rw-r--r-- | tests/auto/updateinfo/updateinfo.qbs | 7 |
9 files changed, 271 insertions, 116 deletions
diff --git a/src/plugins/updateinfo/CMakeLists.txt b/src/plugins/updateinfo/CMakeLists.txt index fe43e0be09..fadca2cd6a 100644 --- a/src/plugins/updateinfo/CMakeLists.txt +++ b/src/plugins/updateinfo/CMakeLists.txt @@ -3,6 +3,10 @@ add_qtc_plugin(UpdateInfo PLUGIN_DEPENDS Core PLUGIN_JSON_IN UPDATEINFO_EXPERIMENTAL_STR=true SOURCES - settingspage.cpp settingspage.h settingspage.ui - updateinfoplugin.cpp updateinfoplugin.h + settingspage.cpp + settingspage.h + settingspage.ui + updateinfoplugin.cpp + updateinfoplugin.h + updateinfotools.h ) diff --git a/src/plugins/updateinfo/updateinfo.qbs b/src/plugins/updateinfo/updateinfo.qbs index 5253039dbc..2dbae1933a 100644 --- a/src/plugins/updateinfo/updateinfo.qbs +++ b/src/plugins/updateinfo/updateinfo.qbs @@ -12,10 +12,11 @@ QtcPlugin { pluginJsonReplacements: ({"UPDATEINFO_EXPERIMENTAL_STR": (enable ? "false": "true")}) files: [ - "updateinfoplugin.cpp", - "updateinfoplugin.h", "settingspage.cpp", "settingspage.h", "settingspage.ui", + "updateinfoplugin.cpp", + "updateinfoplugin.h", + "updateinfotools.h", ] } diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp index e2271ad396..743802f370 100644 --- a/src/plugins/updateinfo/updateinfoplugin.cpp +++ b/src/plugins/updateinfo/updateinfoplugin.cpp @@ -23,9 +23,11 @@ ** ****************************************************************************/ -#include "settingspage.h" #include "updateinfoplugin.h" +#include "settingspage.h" +#include "updateinfotools.h" + #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/coreconstants.h> @@ -181,107 +183,6 @@ void UpdateInfoPlugin::collectCheckForUpdatesOutput(const QString &contents) d->m_collectedOutput += contents; } -struct Update -{ - QString name; - QString version; -}; - -static QList<Update> availableUpdates(const QDomDocument &document) -{ - if (document.isNull() || !document.firstChildElement().hasChildNodes()) - return {}; - QList<Update> result; - const QDomNodeList updates = document.firstChildElement().elementsByTagName("update"); - for (int i = 0; i < updates.size(); ++i) { - const QDomNode node = updates.item(i); - if (node.isElement()) { - const QDomElement element = node.toElement(); - if (element.hasAttribute("name")) - result.append({element.attribute("name"), element.attribute("version")}); - } - } - return result; -} - -struct QtPackage -{ - QString displayName; - QVersionNumber version; - bool installed; - bool isPrerelease = false; -}; - -static QList<QtPackage> availableQtPackages(const QDomDocument &document) -{ - if (document.isNull() || !document.firstChildElement().hasChildNodes()) - return {}; - QList<QtPackage> result; - const QDomNodeList packages = document.firstChildElement().elementsByTagName("package"); - for (int i = 0; i < packages.size(); ++i) { - const QDomNode node = packages.item(i); - if (node.isElement()) { - const QDomElement element = node.toElement(); - if (element.hasAttribute("displayname") && element.hasAttribute("name") - && element.hasAttribute("version")) { - QtPackage package{element.attribute("displayname"), - QVersionNumber::fromString(element.attribute("version")), - element.hasAttribute("installedVersion")}; - // Heuristic: Prerelease if the name is not "Qt x.y.z" - // (prereleases are named "Qt x.y.z-alpha" etc) - package.isPrerelease = package.displayName - != QString("Qt %1").arg(package.version.toString()); - result.append(package); - } - } - } - std::sort(result.begin(), result.end(), [](const QtPackage &p1, const QtPackage &p2) { - return p1.version > p2.version; - }); - return result; -} - -// Expects packages to be sorted, high version first. -static Utils::optional<QtPackage> highestInstalledQt(const QList<QtPackage> &packages) -{ - const auto highestInstalledIt = std::find_if(packages.cbegin(), - packages.cend(), - [](const QtPackage &p) { return p.installed; }); - if (highestInstalledIt == packages.cend()) // Qt not installed - return {}; - return *highestInstalledIt; -} - -// Expects packages to be sorted, high version first. -static Utils::optional<QtPackage> qtToNagAbout(const QList<QtPackage> &allPackages, - QVersionNumber *highestSeen) -{ - // Filter out any Qt prereleases - const QList<QtPackage> packages = Utils::filtered(allPackages, [](const QtPackage &p) { - return !p.isPrerelease; - }); - if (packages.isEmpty()) - return {}; - const QtPackage highest = packages.constFirst(); - qCDebug(log) << "Highest available (non-prerelease) Qt:" << highest.version; - qCDebug(log) << "Highest previously seen (non-prerelease) Qt:" << *highestSeen; - // if the highestSeen version is null, we don't know if the Qt version is new, and better don't nag - const bool isNew = !highestSeen->isNull() && highest.version > *highestSeen; - if (highestSeen->isNull() || isNew) - *highestSeen = highest.version; - if (!isNew) - return {}; - const Utils::optional<QtPackage> highestInstalled = highestInstalledQt(packages); - qCDebug(log) << "Highest installed Qt:" - << qPrintable(highestInstalled ? highestInstalled->version.toString() - : QString("none")); - if (!highestInstalled) // don't nag if no Qt is installed at all - return {}; - if (highestInstalled->version == highest.version) - return {}; - return highest; -} - static void showUpdateInfo(const QList<Update> &updates, const std::function<void()> &startUpdater) { Utils::InfoBarEntry info(InstallUpdates, @@ -334,20 +235,14 @@ void UpdateInfoPlugin::checkForUpdatesFinished() { setLastCheckDate(QDate::currentDate()); - QDomDocument document; - // since the output can contain two toplevel items from the two separate MaintenanceTool runs, - // surround with a toplevel element - const QString xml = d->m_collectedOutput.isEmpty() - ? QString() - : ("<doc>" + d->m_collectedOutput + "</doc>"); qCDebug(log) << "--- MaintenanceTool output (combined):"; - qCDebug(log) << qPrintable(xml); - document.setContent(xml); + qCDebug(log) << qPrintable(d->m_collectedOutput); + std::unique_ptr<QDomDocument> document = documentForResponse(d->m_collectedOutput); stopCheckForUpdates(); - const QList<Update> updates = availableUpdates(document); - const QList<QtPackage> qtPackages = availableQtPackages(document); + const QList<Update> updates = availableUpdates(*document); + const QList<QtPackage> qtPackages = availableQtPackages(*document); if (log().isDebugEnabled()) { qCDebug(log) << "--- Available updates:"; for (const Update &u : updates) diff --git a/src/plugins/updateinfo/updateinfotools.h b/src/plugins/updateinfo/updateinfotools.h new file mode 100644 index 0000000000..66ad2eb3ac --- /dev/null +++ b/src/plugins/updateinfo/updateinfotools.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2022 the Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include <utils/algorithm.h> +#include <utils/optional.h> + +#include <QDomDocument> +#include <QList> +#include <QLoggingCategory> +#include <QVersionNumber> + +#include <memory> + +Q_DECLARE_LOGGING_CATEGORY(log) + +std::unique_ptr<QDomDocument> documentForResponse(const QString &response) +{ + // since the output can contain two toplevel items from the two separate MaintenanceTool runs, + // surround with a toplevel element + const QString xml = response.isEmpty() ? QString() : ("<doc>" + response + "</doc>"); + std::unique_ptr<QDomDocument> doc(new QDomDocument); + doc->setContent(xml); + return doc; +} + +struct Update +{ + QString name; + QString version; + + bool operator==(const Update &other) const + { + return other.name == name && other.version == version; + }; +}; + +QList<Update> availableUpdates(const QDomDocument &document) +{ + if (document.isNull() || !document.firstChildElement().hasChildNodes()) + return {}; + QList<Update> result; + const QDomNodeList updates = document.firstChildElement().elementsByTagName("update"); + for (int i = 0; i < updates.size(); ++i) { + const QDomNode node = updates.item(i); + if (node.isElement()) { + const QDomElement element = node.toElement(); + if (element.hasAttribute("name")) + result.append({element.attribute("name"), element.attribute("version")}); + } + } + return result; +} + +struct QtPackage +{ + QString displayName; + QVersionNumber version; + bool installed; + bool isPrerelease = false; + + bool operator==(const QtPackage &other) const + { + return other.installed == installed && other.isPrerelease == isPrerelease + && other.version == version && other.displayName == displayName; + } +}; + +QList<QtPackage> availableQtPackages(const QDomDocument &document) +{ + if (document.isNull() || !document.firstChildElement().hasChildNodes()) + return {}; + QList<QtPackage> result; + const QDomNodeList packages = document.firstChildElement().elementsByTagName("package"); + for (int i = 0; i < packages.size(); ++i) { + const QDomNode node = packages.item(i); + if (node.isElement()) { + const QDomElement element = node.toElement(); + if (element.hasAttribute("displayname") && element.hasAttribute("name") + && element.hasAttribute("version")) { + QtPackage package{element.attribute("displayname"), + QVersionNumber::fromString(element.attribute("version")), + element.hasAttribute("installedVersion")}; + // Heuristic: Prerelease if the name is not "Qt x.y.z" + // (prereleases are named "Qt x.y.z-alpha" etc) + package.isPrerelease = package.displayName + != QString("Qt %1").arg(package.version.toString()); + result.append(package); + } + } + } + std::sort(result.begin(), result.end(), [](const QtPackage &p1, const QtPackage &p2) { + return p1.version > p2.version; + }); + return result; +} + +// Expects packages to be sorted, high version first. +Utils::optional<QtPackage> highestInstalledQt(const QList<QtPackage> &packages) +{ + const auto highestInstalledIt = std::find_if(packages.cbegin(), + packages.cend(), + [](const QtPackage &p) { return p.installed; }); + if (highestInstalledIt == packages.cend()) // Qt not installed + return {}; + return *highestInstalledIt; +} + +// Expects packages to be sorted, high version first. +Utils::optional<QtPackage> qtToNagAbout(const QList<QtPackage> &allPackages, + QVersionNumber *highestSeen) +{ + // Filter out any Qt prereleases + const QList<QtPackage> packages = Utils::filtered(allPackages, [](const QtPackage &p) { + return !p.isPrerelease; + }); + if (packages.isEmpty()) + return {}; + const QtPackage highest = packages.constFirst(); + qCDebug(log) << "Highest available (non-prerelease) Qt:" << highest.version; + qCDebug(log) << "Highest previously seen (non-prerelease) Qt:" << *highestSeen; + // if the highestSeen version is null, we don't know if the Qt version is new, and better don't nag + const bool isNew = !highestSeen->isNull() && highest.version > *highestSeen; + if (highestSeen->isNull() || isNew) + *highestSeen = highest.version; + if (!isNew) + return {}; + const Utils::optional<QtPackage> highestInstalled = highestInstalledQt(packages); + qCDebug(log) << "Highest installed Qt:" + << qPrintable(highestInstalled ? highestInstalled->version.toString() + : QString("none")); + if (!highestInstalled) // don't nag if no Qt is installed at all + return {}; + if (highestInstalled->version == highest.version) + return {}; + return highest; +} + +Q_DECLARE_METATYPE(Update) +Q_DECLARE_METATYPE(QtPackage) diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index dcf51ff0a2..bc58a5b953 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -21,5 +21,6 @@ add_subdirectory(ssh) add_subdirectory(toolchaincache) add_subdirectory(tracing) add_subdirectory(treeviewfind) +add_subdirectory(updateinfo) add_subdirectory(utils) add_subdirectory(valgrind) diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs index c82d1df094..6526653ab8 100644 --- a/tests/auto/auto.qbs +++ b/tests/auto/auto.qbs @@ -24,6 +24,7 @@ Project { "toolchaincache/toolchaincache.qbs", "tracing/tracing.qbs", "treeviewfind/treeviewfind.qbs", + "updateinfo/updateinfo.qbs", "utils/utils.qbs", "valgrind/valgrind.qbs", ].concat(project.additionalAutotests) diff --git a/tests/auto/updateinfo/CMakeLists.txt b/tests/auto/updateinfo/CMakeLists.txt new file mode 100644 index 0000000000..e44adb8ff8 --- /dev/null +++ b/tests/auto/updateinfo/CMakeLists.txt @@ -0,0 +1,5 @@ +add_qtc_test(tst_updateinfo + INCLUDES ${PROJECT_SOURCE_DIR}/src/plugins + DEPENDS Utils Qt5::Xml + SOURCES tst_updateinfo.cpp +) diff --git a/tests/auto/updateinfo/tst_updateinfo.cpp b/tests/auto/updateinfo/tst_updateinfo.cpp new file mode 100644 index 0000000000..55e61a16c2 --- /dev/null +++ b/tests/auto/updateinfo/tst_updateinfo.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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. +** +****************************************************************************/ + +#include <QtTest> + +#include <updateinfo/updateinfotools.h> + +Q_LOGGING_CATEGORY(log, "qtc.updateinfo", QtWarningMsg) + +class tst_UpdateInfo : public QObject +{ + Q_OBJECT + +private slots: + void updates_data(); + void updates(); +}; + +void tst_UpdateInfo::updates_data() +{ + QTest::addColumn<QString>("xml"); + QTest::addColumn<QList<Update>>("xupdates"); + QTest::addColumn<QList<QtPackage>>("xpackages"); + + QTest::newRow("updates and packages") + << R"raw(<updates> + <update name="Qt Design Studio 3.2.0" version="3.2.0-0-202203291247" size="3113234690" id="qt.tools.qtdesignstudio"/> + </updates> + <availablepackages> + <package name="qt.qt6.621" displayname="Qt 6.2.1" version="6.2.1-0-202110220854"/> + <package name="qt.qt5.5152" displayname="Qt 5.15.2" version="5.15.2-0-202011130607" installedVersion="5.15.2-0-202011130607"/> + <package name="qt.qt6.610" displayname="Qt 6.1.0-beta1" version="6.1.0-0-202105040922"/> + </availablepackages>)raw" + << QList<Update>({{"Qt Design Studio 3.2.0", "3.2.0-0-202203291247"}}) + << QList<QtPackage>( + {{"Qt 6.2.1", QVersionNumber::fromString("6.2.1-0-202110220854"), false, false}, + {"Qt 6.1.0-beta1", QVersionNumber::fromString("6.1.0-0-202105040922"), false, true}, + {"Qt 5.15.2", QVersionNumber::fromString("5.15.2-0-202011130607"), true, false}}); +} + +void tst_UpdateInfo::updates() +{ + QFETCH(QString, xml); + QFETCH(QList<Update>, xupdates); + QFETCH(QList<QtPackage>, xpackages); + + std::unique_ptr<QDomDocument> doc = documentForResponse(xml); + const QList<Update> updates = availableUpdates(*doc); + QCOMPARE(updates, xupdates); + const QList<QtPackage> packages = availableQtPackages(*doc); + QCOMPARE(packages, xpackages); +} + +QTEST_GUILESS_MAIN(tst_UpdateInfo) + +#include "tst_updateinfo.moc" diff --git a/tests/auto/updateinfo/updateinfo.qbs b/tests/auto/updateinfo/updateinfo.qbs new file mode 100644 index 0000000000..9aa07dd28f --- /dev/null +++ b/tests/auto/updateinfo/updateinfo.qbs @@ -0,0 +1,7 @@ +QtcAutotest { + name: "UpdateInfo autotest" + Depends { name: "Qt.xml" } + Depends { name: "Utils" } + files: "tst_updateinfo.cpp" + cpp.includePaths: base.concat(["../../../src/plugins"]) +} |