diff options
author | Robert Griebl <robert.griebl@qt.io> | 2021-05-03 16:42:46 +0200 |
---|---|---|
committer | Robert Griebl <robert.griebl@qt.io> | 2021-06-18 13:06:27 +0200 |
commit | 26e09279f3cd12324657011c640972e297928f53 (patch) | |
tree | 17839d952b17fcc6338105a85604f543dc95547b /tests/auto | |
parent | 12b4ee7d85b1197cd4b6024e710430a747f76838 (diff) | |
download | qtapplicationmanager-26e09279f3cd12324657011c640972e297928f53.tar.gz |
cmake: Nearly full cmake build
Had to rename a lot of things to conform with the standard Qt module
layout that the cmake system expects:
- all non-manual tests were moved to a sub-dir named auto/
- the benchmark was moved to tests/
- the 3rdparty folder was moved into src/
Other changes:
- libyaml was updated to 2.2.5 while fixing a weird build issue that
led to crashes on 64bit systems.
- fixed build issues with the new 8.1 MingW compiler.
- added support for QT_NO_OPENGL builds.
The remaining issues are:
- examples still don't build with qmake due to a potential bug in
module.pri generation.
- tests do run, but the test data is not generated yet dynamically.
- qml-only tests are not built and run yet.
- qml-only examples are not built yet.
Fixes: AUTOSUITE-1632
Change-Id: Ic5fe0148e738b05835c73bed78e624b55861b75e
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Dominik Holland <dominik.holland@qt.io>
Diffstat (limited to 'tests/auto')
258 files changed, 11854 insertions, 0 deletions
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt new file mode 100644 index 00000000..1f6dcca8 --- /dev/null +++ b/tests/auto/CMakeLists.txt @@ -0,0 +1,23 @@ + +add_subdirectory(application) +add_subdirectory(applicationinfo) +add_subdirectory(applicationinstaller) +add_subdirectory(configuration) +add_subdirectory(cryptography) +add_subdirectory(debugwrapper) +add_subdirectory(installationreport) +add_subdirectory(main) +add_subdirectory(packagecreator) +add_subdirectory(packageextractor) +add_subdirectory(packager-tool) +#add_subdirectory(qml) +add_subdirectory(runtime) +add_subdirectory(signature) +add_subdirectory(utilities) +add_subdirectory(yaml) + +if(LINUX) + add_subdirectory(systemreader) + add_subdirectory(processreader) + add_subdirectory(sudo) +endif() diff --git a/tests/auto/application/CMakeLists.txt b/tests/auto/application/CMakeLists.txt new file mode 100644 index 00000000..942bc0c3 --- /dev/null +++ b/tests/auto/application/CMakeLists.txt @@ -0,0 +1,30 @@ + +qt_internal_add_test(tst_application + SOURCES + ../error-checking.h + tst_application.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManManagerPrivate +) + +# Resources: +set(tst_application_resource_files + "info.yaml" +) + +qt_internal_add_resource(tst_application "tst_application" + PREFIX + "/" + FILES + ${tst_application_resource_files} +) + +qt_internal_extend_target(tst_application CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/application/application.pro b/tests/auto/application/application.pro new file mode 100644 index 00000000..2bdaf9c5 --- /dev/null +++ b/tests/auto/application/application.pro @@ -0,0 +1,13 @@ +TARGET = tst_application + +include($$PWD/../tests.pri) + +QT *= \ + appman_common-private \ + appman_application-private \ + appman_manager-private \ + +SOURCES += tst_application.cpp + +OTHER_FILES += info.yaml +RESOURCES += tst_application.qrc diff --git a/tests/auto/application/icon.png b/tests/auto/application/icon.png Binary files differnew file mode 100644 index 00000000..909c66db --- /dev/null +++ b/tests/auto/application/icon.png diff --git a/tests/auto/application/info.yaml b/tests/auto/application/info.yaml new file mode 100644 index 00000000..08329bb8 --- /dev/null +++ b/tests/auto/application/info.yaml @@ -0,0 +1,10 @@ +formatType: am-package +formatVersion: 1 +--- +id: pkg.test +name: { en: "Test Package" } +icon: icon.png +applications: +- id: app.test + runtime: qml + code: info.yaml # just to make the test simpler diff --git a/tests/auto/application/tst_application.cpp b/tests/auto/application/tst_application.cpp new file mode 100644 index 00000000..05b155c5 --- /dev/null +++ b/tests/auto/application/tst_application.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> + +#include "global.h" +#include "application.h" +#include "applicationinfo.h" +#include "packageinfo.h" +#include "package.h" +#include "abstractruntime.h" + +QT_USE_NAMESPACE_AM + +class TestRuntime : public AbstractRuntime +{ + Q_OBJECT + +public: + explicit TestRuntime(AbstractContainer *container, Application *app, AbstractRuntimeManager *manager) + : AbstractRuntime(container, app, manager) + { } + + void setSlowAnimations(bool) override {} + + qint64 applicationProcessId() const override + { + return m_state == Am::Running ? 1 : 0; + } + +public slots: + bool start() override + { + m_state = Am::Running; + return true; + } + + void stop(bool forceKill) override + { + Q_UNUSED(forceKill); + m_state = Am::NotRunning; + } +}; + +class TestRuntimeManager : public AbstractRuntimeManager +{ + Q_OBJECT + +public: + TestRuntimeManager(const QString &id, QObject *parent) + : AbstractRuntimeManager(id, parent) + { } + + static QString defaultIdentifier() { return qSL("foo"); } + + bool inProcess() const override + { + return !AbstractRuntimeManager::inProcess(); + } + + TestRuntime *create(AbstractContainer *container, Application *app) override + { + return new TestRuntime(container, app, this); + } +}; + +class tst_Application : public QObject +{ + Q_OBJECT + +public: + tst_Application(){} + +private slots: + void runtimeDestroyed(); +}; + +// Checks that when the runtime of an application is destroyed +// the application no longer holds a reference to it +void tst_Application::runtimeDestroyed() +{ + auto pi = PackageInfo::fromManifest(qL1S(":/info.yaml")); + auto pkg = new Package(pi); + auto ai = new ApplicationInfo(pi); + auto app = new Application(ai, pkg); + + auto runtimeManager = new TestRuntimeManager(qSL("foo"), qApp); + auto runtime = runtimeManager->create(nullptr, app); + + app->setCurrentRuntime(runtime); + + QCOMPARE(app->currentRuntime(), runtime); + + delete runtime; + + QCOMPARE(app->currentRuntime(), nullptr); + + delete app; + delete runtimeManager; + delete ai; + delete pkg; + delete pi; +} + +QTEST_APPLESS_MAIN(tst_Application) + +#include "tst_application.moc" diff --git a/tests/auto/application/tst_application.qrc b/tests/auto/application/tst_application.qrc new file mode 100644 index 00000000..a24bcd9a --- /dev/null +++ b/tests/auto/application/tst_application.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>info.yaml</file> +</qresource> +</RCC> diff --git a/tests/auto/applicationinfo/CMakeLists.txt b/tests/auto/applicationinfo/CMakeLists.txt new file mode 100644 index 00000000..5cc7ca36 --- /dev/null +++ b/tests/auto/applicationinfo/CMakeLists.txt @@ -0,0 +1,21 @@ + +qt_internal_add_test(tst_applicationinfo + SOURCES + ../error-checking.h + tst_applicationinfo.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManManagerPrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_applicationinfo CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/applicationinfo/applicationinfo.pro b/tests/auto/applicationinfo/applicationinfo.pro new file mode 100644 index 00000000..9fcc7ffe --- /dev/null +++ b/tests/auto/applicationinfo/applicationinfo.pro @@ -0,0 +1,10 @@ +TARGET = tst_applicationinfo + +include($$PWD/../tests.pri) + +QT *= \ + appman_common-private \ + appman_application-private \ + appman_manager-private \ + +SOURCES += tst_applicationinfo.cpp diff --git a/tests/auto/applicationinfo/tst_applicationinfo.cpp b/tests/auto/applicationinfo/tst_applicationinfo.cpp new file mode 100644 index 00000000..9dc93292 --- /dev/null +++ b/tests/auto/applicationinfo/tst_applicationinfo.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> + +#include "global.h" +#include "application.h" +#include "applicationinfo.h" +#include "packageinfo.h" +#include "yamlpackagescanner.h" +#include "exception.h" + +QT_USE_NAMESPACE_AM + +class tst_ApplicationInfo : public QObject +{ + Q_OBJECT + +public: + tst_ApplicationInfo(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + void application_data(); + void application(); + void validApplicationId_data(); + void validApplicationId(); + void validIcon_data(); + void validIcon(); + +private: + QVector<PackageInfo *> m_pkgs; +}; + +tst_ApplicationInfo::tst_ApplicationInfo() +{ } + + +void tst_ApplicationInfo::initTestCase() +{ + if (!QDir(qL1S(AM_TESTDATA_DIR "/packages")).exists()) + QSKIP("No test packages available in the data/ directory"); + + YamlPackageScanner scanner; + QDir baseDir(qL1S(AM_TESTDATA_DIR "manifests")); + const QStringList pkgDirNames = baseDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); + for (const QString &pkgDirName : pkgDirNames) { + QDir dir = baseDir.absoluteFilePath(pkgDirName); + try { + PackageInfo *pi = scanner.scan(dir.absoluteFilePath(qSL("info.yaml"))); + QVERIFY(pi); + QCOMPARE(pkgDirName, pi->id()); + m_pkgs << pi; + } catch (const std::exception &e) { + QFAIL(e.what()); + } + } + + QCOMPARE(m_pkgs.size(), 2); +} + +void tst_ApplicationInfo::cleanupTestCase() +{ + qDeleteAll(m_pkgs); +} + +void tst_ApplicationInfo::application_data() +{ + QTest::addColumn<QString>("id"); + + QTest::newRow("normal") << "com.pelagicore.test"; + QTest::newRow("json") << "com.pelagicore.json-legacy"; +} + +void tst_ApplicationInfo::application() +{ + QFETCH(QString, id); + + QString name = QString::fromLatin1(AM_TESTDATA_DIR "manifests/%1/info.yaml").arg(id); + + YamlPackageScanner scanner; + PackageInfo *pkg; + try { + pkg = scanner.scan(name); + QVERIFY(pkg); + } catch (const std::exception &e) { + QFAIL(e.what()); + } + + QCOMPARE(pkg->id(), id); + QCOMPARE(QFileInfo(pkg->icon()).fileName(), qSL("icon.png")); + QCOMPARE(pkg->names().size(), 2); + QCOMPARE(pkg->names().value(qSL("en")), qSL("english")); + QCOMPARE(pkg->names().value(qSL("de")), qSL("deutsch")); + QCOMPARE(pkg->name(qSL("en")), qSL("english")); + QCOMPARE(pkg->isBuiltIn(), false); + QCOMPARE(pkg->categories().size(), 2); + QVERIFY(pkg->categories().startsWith(qSL("bar"))); + QVERIFY(pkg->categories().endsWith(qSL("foo"))); + + ApplicationInfo *app = pkg->applications().size() == 1 ? pkg->applications().first() : nullptr; + QVERIFY(app); + QCOMPARE(QFileInfo(app->codeFilePath()).fileName(), qSL("Test.qml")); + QCOMPARE(app->runtimeName(), qSL("qml")); + QCOMPARE(app->runtimeParameters().size(), 1); + QCOMPARE(app->runtimeParameters().value(qSL("loadDummyData")).toBool(), true); + QCOMPARE(app->capabilities().size(), 2); + QVERIFY(app->capabilities().startsWith(qSL("cameraAccess"))); + QVERIFY(app->capabilities().endsWith(qSL("locationAccess"))); + + // legacy + QCOMPARE(app->supportedMimeTypes().size(), 2); + QVERIFY(app->supportedMimeTypes().startsWith(qSL("text/plain"))); + QVERIFY(app->supportedMimeTypes().endsWith(qSL("x-scheme-handler/mailto"))); + + delete pkg; +} + +void tst_ApplicationInfo::validApplicationId_data() +{ + QTest::addColumn<QString>("appId"); + QTest::addColumn<bool>("valid"); + + // passes + QTest::newRow("normal") << "Test" << true; + QTest::newRow("shortest") << "t" << true; + QTest::newRow("valid-chars") << "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ,.';[]{}!#$%^&()-_=+@" << true; + QTest::newRow("longest-name") << "com.012345678901234567890123456789012345678901234567890123456789012.012345678901234567890123456789012345678901234567890123456789012.0123456789012.test" << true; + + // failures + QTest::newRow("empty") << "" << false; + QTest::newRow("space-only") << " " << false; + QTest::newRow("space-only2") << " " << false; + QTest::newRow("name-too-long") << "com.012345678901234567890123456789012345678901234567890123456789012.012345678901234567890123456789012345678901234567890123456789012.0123456789012.xtest" << false; + QTest::newRow("invalid-char<") << "t<" << false; + QTest::newRow("invalid-char>") << "t>" << false; + QTest::newRow("invalid-char:") << "t:" << false; + QTest::newRow("invalid-char-quote") << "t\"" << false; + QTest::newRow("invalid-char/") << "t/" << false; + QTest::newRow("invalid-char\\") << "t\\" << false; + QTest::newRow("invalid-char|") << "t|" << false; + QTest::newRow("invalid-char?") << "t?" << false; + QTest::newRow("invalid-char*") << "t*" << false; + QTest::newRow("control-char") << "t\t" << false; +} + +void tst_ApplicationInfo::validApplicationId() +{ + QFETCH(QString, appId); + QFETCH(bool, valid); + + QString errorString; + bool result = PackageInfo::isValidApplicationId(appId, &errorString); + + QVERIFY2(valid == result, qPrintable(errorString)); +} + +void tst_ApplicationInfo::validIcon_data() +{ + QTest::addColumn<QString>("icon"); + QTest::addColumn<bool>("isValid"); + + // valid + QTest::newRow("'icon.png' is valid") << "icon.png" << true; + QTest::newRow("'foo.bar' is valid") << "foo.bar" << true; + QTest::newRow("'foo' is valid") << "foo" << true; + + // invalid + QTest::newRow("empty is invalid") << "" << false; + QTest::newRow("'foo/icon.png' is invalid") << "foo/icon.png" << false; + QTest::newRow("'../icon.png' is invalid") << "../icon.png" << false; + QTest::newRow("'icon.png/' is invalid") << "icon.png/" << false; + QTest::newRow("'/foo' is invalid") << "/foo" << false; +} + +void tst_ApplicationInfo::validIcon() +{ + QFETCH(QString, icon); + QFETCH(bool, isValid); + + QString errorString; + bool result = PackageInfo::isValidIcon(icon, &errorString); + + QCOMPARE(result, isValid); +} + +QTEST_APPLESS_MAIN(tst_ApplicationInfo) + +#include "tst_applicationinfo.moc" + diff --git a/tests/auto/applicationinstaller/CMakeLists.txt b/tests/auto/applicationinstaller/CMakeLists.txt new file mode 100644 index 00000000..3da4d358 --- /dev/null +++ b/tests/auto/applicationinstaller/CMakeLists.txt @@ -0,0 +1,25 @@ + +qt_internal_add_test(tst_applicationinstaller + SOURCES + ../error-checking.h + tst_applicationinstaller.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManManagerPrivate + Qt::AppManPackagePrivate +) + +#### Keys ignored in scope 1:.:.:applicationinstaller.pro:<TRUE>: +# COVERAGE_RUNTIME = "sudo" + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_applicationinstaller CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/applicationinstaller/applicationinstaller.pro b/tests/auto/applicationinstaller/applicationinstaller.pro new file mode 100644 index 00000000..b84ffac4 --- /dev/null +++ b/tests/auto/applicationinstaller/applicationinstaller.pro @@ -0,0 +1,13 @@ +TARGET = tst_applicationinstaller + +COVERAGE_RUNTIME = sudo + +include($$PWD/../tests.pri) + +QT *= \ + appman_common-private \ + appman_application-private \ + appman_package-private \ + appman_manager-private \ + +SOURCES += tst_applicationinstaller.cpp diff --git a/tests/auto/applicationinstaller/tst_applicationinstaller.cpp b/tests/auto/applicationinstaller/tst_applicationinstaller.cpp new file mode 100644 index 00000000..15be7898 --- /dev/null +++ b/tests/auto/applicationinstaller/tst_applicationinstaller.cpp @@ -0,0 +1,793 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> + +#include <functional> + +#include "packagemanager.h" +#include "packagedatabase.h" +#include "package.h" +#include "applicationinfo.h" +#include "sudo.h" +#include "utilities.h" +#include "error.h" +#include "private/packageutilities_p.h" +#include "runtimefactory.h" +#include "qmlinprocessruntime.h" +#include "packageutilities.h" + +#include "../error-checking.h" + +QT_USE_NAMESPACE_AM + +static bool startedSudoServer = false; +static QString sudoServerError; + +static int spyTimeout = 5000; // shorthand for specifying QSignalSpy timeouts + +// RAII to reset the global attribute +class AllowInstallations +{ +public: + enum Type { + AllowUnsinged, + RequireDevSigned, + RequireStoreSigned + }; + + AllowInstallations(Type t) + : m_oldUnsigned(PackageManager::instance()->allowInstallationOfUnsignedPackages()) + , m_oldDevMode(PackageManager::instance()->developmentMode()) + { + switch (t) { + case AllowUnsinged: + PackageManager::instance()->setAllowInstallationOfUnsignedPackages(true); + PackageManager::instance()->setDevelopmentMode(false); + break; + case RequireDevSigned: + PackageManager::instance()->setAllowInstallationOfUnsignedPackages(false); + PackageManager::instance()->setDevelopmentMode(true); + break; + case RequireStoreSigned: + PackageManager::instance()->setAllowInstallationOfUnsignedPackages(false); + PackageManager::instance()->setDevelopmentMode(false); + break; + } + } + ~AllowInstallations() + { + PackageManager::instance()->setAllowInstallationOfUnsignedPackages(m_oldUnsigned); + PackageManager::instance()->setDevelopmentMode(m_oldDevMode); + } +private: + bool m_oldUnsigned; + bool m_oldDevMode; +}; + +class tst_PackageManager : public QObject +{ + Q_OBJECT + +public: + tst_PackageManager(QObject *parent = nullptr); + ~tst_PackageManager(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + //TODO: test AI::cleanupBrokenInstallations() before calling cleanup() the first time! + + void packageInstallation_data(); + void packageInstallation(); + + void simulateErrorConditions_data(); + void simulateErrorConditions(); + + void cancelPackageInstallation_data(); + void cancelPackageInstallation(); + + void parallelPackageInstallation(); + + void validateDnsName_data(); + void validateDnsName(); + + void compareVersions_data(); + void compareVersions(); + +public: + enum PathLocation { + Internal0, + Documents0, + + PathLocationCount + }; + +private: + QString pathTo(const QString &sub = QString()) + { + return pathTo(PathLocationCount, sub); + } + + QString pathTo(PathLocation pathLocation, const QString &sub = QString()) + { + QString base; + switch (pathLocation) { + case Internal0: base = qSL("internal"); break; + case Documents0: base = qSL("documents"); break; + default: break; + } + + QDir workDir(m_workDir.path()); + + if (base.isEmpty() && sub.isEmpty()) + base = workDir.absolutePath(); + else if (sub.isEmpty()) + base = workDir.absoluteFilePath(base); + else if (base.isEmpty()) + base = workDir.absoluteFilePath(sub); + else + base = workDir.absoluteFilePath(base + '/' + sub); + + if (QDir(base).exists()) + return base + '/'; + else + return base; + } + + void clearSignalSpies() + { + m_startedSpy->clear(); + m_requestingInstallationAcknowledgeSpy->clear(); + m_blockingUntilInstallationAcknowledgeSpy->clear(); + m_progressSpy->clear(); + m_finishedSpy->clear(); + m_failedSpy->clear(); + } + + static bool isDataTag(const char *tag) + { + return !qstrcmp(tag, QTest::currentDataTag()); + } + +private: + SudoClient *m_sudo = nullptr; + bool m_fakeSudo = false; + + QTemporaryDir m_workDir; + QString m_hardwareId; + PackageManager *m_pm = nullptr; + QSignalSpy *m_startedSpy = nullptr; + QSignalSpy *m_requestingInstallationAcknowledgeSpy = nullptr; + QSignalSpy *m_blockingUntilInstallationAcknowledgeSpy = nullptr; + QSignalSpy *m_progressSpy = nullptr; + QSignalSpy *m_finishedSpy = nullptr; + QSignalSpy *m_failedSpy = nullptr; +}; + + +tst_PackageManager::tst_PackageManager(QObject *parent) + : QObject(parent) +{ } + +tst_PackageManager::~tst_PackageManager() +{ + if (m_workDir.isValid()) { + if (m_sudo) + m_sudo->removeRecursive(m_workDir.path()); + else + recursiveOperation(m_workDir.path(), safeRemove); + } + + delete m_failedSpy; + delete m_finishedSpy; + delete m_progressSpy; + delete m_blockingUntilInstallationAcknowledgeSpy; + delete m_requestingInstallationAcknowledgeSpy; + delete m_startedSpy; + + delete m_pm; +} + +void tst_PackageManager::initTestCase() +{ + if (!QDir(qL1S(AM_TESTDATA_DIR "/packages")).exists()) + QSKIP("No test packages available in the data/ directory"); + + bool verbose = qEnvironmentVariableIsSet("AM_VERBOSE_TEST"); + if (!verbose) + QLoggingCategory::setFilterRules(qSL("am.installer.debug=false")); + qInfo() << "Verbose mode is" << (verbose ? "on" : "off") << "(change by (un)setting $AM_VERBOSE_TEST)"; + + spyTimeout *= timeoutFactor(); + + QVERIFY(PackageUtilities::checkCorrectLocale()); + QVERIFY2(startedSudoServer, qPrintable(sudoServerError)); + m_sudo = SudoClient::instance(); + QVERIFY(m_sudo); + m_fakeSudo = m_sudo->isFallbackImplementation(); + + // create a temporary dir (plus sub-dirs) for everything created by this test run + QVERIFY(m_workDir.isValid()); + + // make sure we have a valid hardware-id + m_hardwareId = qSL("foobar"); + + for (int i = 0; i < PathLocationCount; ++i) + QVERIFY(QDir().mkdir(pathTo(PathLocation(i)))); + + // finally, instantiate the PackageManager and a bunch of signal-spies for its signals + try { + PackageDatabase *pdb = new PackageDatabase(QStringList(), pathTo(Internal0)); + m_pm = PackageManager::createInstance(pdb, pathTo(Documents0)); + m_pm->setHardwareId(m_hardwareId); + m_pm->enableInstaller(); + + // simulate the ApplicationManager stopping blocked applications + connect(&m_pm->internalSignals, &PackageManagerInternalSignals::registerApplication, + this, [this](ApplicationInfo *ai, Package *package) { + connect(package, &Package::blockedChanged, this, [ai, package](bool blocked) { + if (package->info()->applications().contains(ai) && blocked) + package->applicationStoppedDueToBlock(ai->id()); + }); + }); + } catch (const Exception &e) { + QVERIFY2(false, e.what()); + } + + const QVariantMap iloc = m_pm->installationLocation(); + QCOMPARE(iloc.size(), 3); + QCOMPARE(iloc.value(qSL("path")).toString(), pathTo(Internal0)); + QVERIFY(iloc.value(qSL("deviceSize")).toLongLong() > 0); + QVERIFY(iloc.value(qSL("deviceFree")).toLongLong() > 0); + QVERIFY(iloc.value(qSL("deviceFree")).toLongLong() < iloc.value(qSL("deviceSize")).toLongLong()); + + const QVariantMap dloc = m_pm->documentLocation(); + QCOMPARE(dloc.size(), 3); + QCOMPARE(dloc.value(qSL("path")).toString(), pathTo(Documents0)); + QVERIFY(dloc.value(qSL("deviceSize")).toLongLong() > 0); + QVERIFY(dloc.value(qSL("deviceFree")).toLongLong() > 0); + QVERIFY(dloc.value(qSL("deviceFree")).toLongLong() < dloc.value(qSL("deviceSize")).toLongLong()); + + m_startedSpy = new QSignalSpy(m_pm, &PackageManager::taskStarted); + m_requestingInstallationAcknowledgeSpy = new QSignalSpy(m_pm, &PackageManager::taskRequestingInstallationAcknowledge); + m_blockingUntilInstallationAcknowledgeSpy = new QSignalSpy(m_pm, &PackageManager::taskBlockingUntilInstallationAcknowledge); + m_progressSpy = new QSignalSpy(m_pm, &PackageManager::taskProgressChanged); + m_finishedSpy = new QSignalSpy(m_pm, &PackageManager::taskFinished); + m_failedSpy = new QSignalSpy(m_pm, &PackageManager::taskFailed); + + // crypto stuff - we need to load the root CA and developer CA certificates + + QFile devcaFile(qL1S(AM_TESTDATA_DIR "certificates/devca.crt")); + QFile storecaFile(qL1S(AM_TESTDATA_DIR "certificates/store.crt")); + QFile caFile(qL1S(AM_TESTDATA_DIR "certificates/ca.crt")); + QVERIFY2(devcaFile.open(QIODevice::ReadOnly), qPrintable(devcaFile.errorString())); + QVERIFY2(storecaFile.open(QIODevice::ReadOnly), qPrintable(storecaFile.errorString())); + QVERIFY2(caFile.open(QIODevice::ReadOnly), qPrintable(caFile.errorString())); + + QList<QByteArray> chainOfTrust; + chainOfTrust << devcaFile.readAll() << caFile.readAll(); + QVERIFY(!chainOfTrust.at(0).isEmpty()); + QVERIFY(!chainOfTrust.at(1).isEmpty()); + m_pm->setCACertificates(chainOfTrust); + + // we do not require valid store signatures for this test run + + m_pm->setDevelopmentMode(true); + + // make sure we have a valid runtime available. The important part is + // that we have a runtime called "native" - the functionality does not matter. + RuntimeFactory::instance()->registerRuntime(new QmlInProcessRuntimeManager(qSL("native"))); +} + +void tst_PackageManager::cleanupTestCase() +{ + // the real cleanup happens in ~tst_PackageManager, since we also need + // to call this cleanup from the crash handler +} + +void tst_PackageManager::init() +{ + // start with a fresh App1 dir on each test run + + if (!QDir(pathTo(Internal0)).exists()) + QVERIFY(QDir().mkdir(pathTo(Internal0))); +} + +void tst_PackageManager::cleanup() +{ + // this helps with reducing the amount of cleanup work required + // at the end of each test + + try { + m_pm->cleanupBrokenInstallations(); + } catch (const Exception &e) { + QFAIL(e.what()); + } + + clearSignalSpies(); + recursiveOperation(pathTo(Internal0), safeRemove); +} + +void tst_PackageManager::packageInstallation_data() +{ + QTest::addColumn<QString>("packageName"); + QTest::addColumn<QString>("updatePackageName"); + QTest::addColumn<bool>("devSigned"); + QTest::addColumn<bool>("storeSigned"); + QTest::addColumn<bool>("expectedSuccess"); + QTest::addColumn<bool>("updateExpectedSuccess"); + QTest::addColumn<QVariantMap>("extraMetaData"); + QTest::addColumn<QString>("errorString"); // start with ~ to create a RegExp + + QVariantMap nomd { }; // no meta-data + QVariantMap extramd = QVariantMap { + { "extra", QVariantMap { + { "array", QVariantList { 1, 2 } }, + { "foo", "bar" }, + { "foo2","bar2" }, + { "key", "value" } } }, + { "extraSigned", QVariantMap { + { "sfoo", "sbar" }, + { "sfoo2", "sbar2" }, + { "signed-key", "signed-value" }, + { "signed-object", QVariantMap { { "k1", "v1" }, { "k2", "v2" } } } + } } + }; + + QTest::newRow("normal") \ + << "test.appkg" << "test-update.appkg" + << false << false << true << true << nomd<< ""; + QTest::newRow("no-dev-signed") \ + << "test.appkg" << "" + << true << false << false << false << nomd << "cannot install unsigned packages"; + QTest::newRow("dev-signed") \ + << "test-dev-signed.appkg" << "test-update-dev-signed.appkg" + << true << false << true << true << nomd << ""; + QTest::newRow("no-store-signed") \ + << "test.appkg" << "" + << false << true << false << false << nomd << "cannot install unsigned packages"; + QTest::newRow("no-store-but-dev-signed") \ + << "test-dev-signed.appkg" << "" + << false << true << false << false << nomd << "cannot install development packages on consumer devices"; + QTest::newRow("store-signed") \ + << "test-store-signed.appkg" << "" + << false << true << true << false << nomd << ""; + QTest::newRow("extra-metadata") \ + << "test-extra.appkg" << "" + << false << false << true << false << extramd << ""; + QTest::newRow("extra-metadata-dev-signed") \ + << "test-extra-dev-signed.appkg" << "" + << true << false << true << false << extramd << ""; + QTest::newRow("invalid-file-order") \ + << "test-invalid-file-order.appkg" << "" + << false << false << false << false << nomd << "The package icon (as stated in info.yaml) must be the second file in the package. Expected 'icon.png', got 'test'"; + QTest::newRow("invalid-header-format") \ + << "test-invalid-header-formatversion.appkg" << "" + << false << false << false << false << nomd << "metadata has an invalid format specification: wrong header: expected type 'am-package-header', version '2' or type 'am-package-header', version '1', but instead got type 'am-package-header', version '0'"; + QTest::newRow("invalid-header-diskspaceused") \ + << "test-invalid-header-diskspaceused.appkg" << "" + << false << false << false << false << nomd << "metadata has an invalid diskSpaceUsed field (0)"; + QTest::newRow("invalid-header-id") \ + << "test-invalid-header-id.appkg" << "" + << false << false << false << false << nomd << "metadata has an invalid packageId field (:invalid)"; + QTest::newRow("non-matching-header-id") \ + << "test-non-matching-header-id.appkg" << "" + << false << false << false << false << nomd << "the package identifiers in --PACKAGE-HEADER--' and info.yaml do not match"; + QTest::newRow("tampered-extra-signed-header") \ + << "test-tampered-extra-signed-header.appkg" << "" + << false << false << false << false << nomd << "~package digest mismatch.*"; + QTest::newRow("invalid-info.yaml") \ + << "test-invalid-info.appkg" << "" + << false << false << false << false << nomd << "~.*did not find expected key.*"; + QTest::newRow("invalid-info.yaml-id") \ + << "test-invalid-info-id.appkg" << "" + << false << false << false << false << nomd << "~.*the identifier \\(:invalid\\) is not a valid package-id: must consist of printable ASCII characters only, except any of .*"; + QTest::newRow("invalid-footer-signature") \ + << "test-invalid-footer-signature.appkg" << "" + << true << false << false << false << nomd << "could not verify the package's developer signature"; +} + +// this test function is a bit of a kitchen sink, but the basic boiler plate +// code of testing the results of an installation is the biggest part and it +// is always the same. +void tst_PackageManager::packageInstallation() +{ + QFETCH(QString, packageName); + QFETCH(QString, updatePackageName); + QFETCH(bool, devSigned); + QFETCH(bool, storeSigned); + QFETCH(bool, expectedSuccess); + QFETCH(bool, updateExpectedSuccess); + QFETCH(QVariantMap, extraMetaData); + QFETCH(QString, errorString); + + QString installationDir = m_pm->installationLocation().value(qSL("path")).toString(); + QString documentDir = m_pm->documentLocation().value(qSL("path")).toString(); + + AllowInstallations allow(storeSigned ? AllowInstallations::RequireStoreSigned + : (devSigned ? AllowInstallations::RequireDevSigned + : AllowInstallations::AllowUnsinged)); + + int lastPass = (updatePackageName.isEmpty() ? 1 : 2); + // pass 1 is the installation / pass 2 is the update (if needed) + for (int pass = 1; pass <= lastPass; ++pass) { + // this makes the results a bit ugly to look at, but it helps with debugging a lot + if (pass > 1) + qInfo("Pass %d", pass); + + // install (or update) the package + + QUrl url = QUrl::fromLocalFile(AM_TESTDATA_DIR "packages/" + (pass == 1 ? packageName : updatePackageName)); + QString taskId = m_pm->startPackageInstallation(url); + QVERIFY(!taskId.isEmpty()); + m_pm->acknowledgePackageInstallation(taskId); + + // check received signals... + + if (pass == 1 ? !expectedSuccess : !updateExpectedSuccess) { + // ...in case of expected failure + + QVERIFY(m_failedSpy->wait(spyTimeout)); + QCOMPARE(m_failedSpy->first()[0].toString(), taskId); + + AM_CHECK_ERRORSTRING(m_failedSpy->first()[2].toString(), errorString); + } else { + // ...in case of expected success + + QVERIFY(m_finishedSpy->wait(spyTimeout)); + QCOMPARE(m_finishedSpy->first()[0].toString(), taskId); + QVERIFY(!m_progressSpy->isEmpty()); + QCOMPARE(m_progressSpy->last()[0].toString(), taskId); + QCOMPARE(m_progressSpy->last()[1].toDouble(), double(1)); + + // check files + + //TODO: remove system((QString::fromUtf8("find ") + m_workDir.path()).toLocal8Bit().constData()); + + QVERIFY(QFile::exists(installationDir + qSL("/com.pelagicore.test/.installation-report.yaml"))); + QVERIFY(QDir(documentDir + qSL("/com.pelagicore.test")).exists()); + + QString fileCheckPath = installationDir + "/com.pelagicore.test"; + + // now check the installed files + + QStringList files = QDir(fileCheckPath).entryList(QDir::AllEntries | QDir::NoDotAndDotDot); + files.sort(); + QVERIFY2(files == QStringList({ qSL("icon.png"), qSL("info.yaml"), qSL("test"), QString::fromUtf8("t\xc3\xa4st") }), + qPrintable(files.join(qSL(", ")))); + + QFile f(fileCheckPath + "/test"); + QVERIFY(f.open(QFile::ReadOnly)); + QCOMPARE(f.readAll(), QByteArray(pass == 1 ? "test\n" : "test update\n")); + f.close(); + + // check metadata + QCOMPARE(m_requestingInstallationAcknowledgeSpy->count(), 1); + QVariantMap extra = m_requestingInstallationAcknowledgeSpy->first()[2].toMap(); + QVariantMap extraSigned = m_requestingInstallationAcknowledgeSpy->first()[3].toMap(); + if (extraMetaData.value(qSL("extra")).toMap() != extra) { + qDebug() << "Actual: " << extra; + qDebug() << "Expected: " << extraMetaData.value(qSL("extra")).toMap(); + QVERIFY(extraMetaData == extra); + } + if (extraMetaData.value(qSL("extraSigned")).toMap() != extraSigned) { + qDebug() << "Actual: " << extraSigned; + qDebug() << "Expected: " << extraMetaData.value(qSL("extraSigned")).toMap(); + QVERIFY(extraMetaData == extraSigned); + } + + // check if the meta-data was saved to the installation report correctly + QVERIFY2(m_pm->installedPackageExtraMetaData(qSL("com.pelagicore.test")) == extra, + "Extra meta-data was not correctly saved to installation report"); + QVERIFY2(m_pm->installedPackageExtraSignedMetaData(qSL("com.pelagicore.test")) == extraSigned, + "Extra signed meta-data was not correctly saved to installation report"); + } + if (pass == lastPass && expectedSuccess) { + // remove package again + + clearSignalSpies(); + taskId = m_pm->removePackage(qSL("com.pelagicore.test"), false); + QVERIFY(!taskId.isEmpty()); + + // check signals + QVERIFY(m_finishedSpy->wait(spyTimeout)); + QCOMPARE(m_finishedSpy->first()[0].toString(), taskId); + } + clearSignalSpies(); + } + // check that all files are gone + + for (PathLocation pl: { Internal0, Documents0 }) { + QStringList entries = QDir(pathTo(pl)).entryList({ qSL("com.pelagicore.test*") }); + QVERIFY2(entries.isEmpty(), qPrintable(pathTo(pl) + qSL(": ") + entries.join(qSL(", ")))); + } +} + + +Q_DECLARE_METATYPE(std::function<bool()>) +typedef QMultiMap<QString, std::function<bool()>> FunctionMap; +Q_DECLARE_METATYPE(FunctionMap) + +void tst_PackageManager::simulateErrorConditions_data() +{ + QTest::addColumn<bool>("testUpdate"); + QTest::addColumn<QString>("errorString"); + QTest::addColumn<FunctionMap>("functions"); + +#ifdef Q_OS_LINUX + QTest::newRow("applications-dir-read-only") \ + << false << "~could not create installation directory .*" \ + << FunctionMap { { "before-start", [this]() { return chmod(pathTo(Internal0).toLocal8Bit(), 0000) == 0; } }, + { "after-failed", [this]() { return chmod(pathTo(Internal0).toLocal8Bit(), 0777) == 0; } } }; + + QTest::newRow("documents-dir-read-only") \ + << false << "~could not create the document directory .*" \ + << FunctionMap { { "before-start", [this]() { return chmod(pathTo(Documents0).toLocal8Bit(), 0000) == 0; } }, + { "after-failed", [this]() { return chmod(pathTo(Documents0).toLocal8Bit(), 0777) == 0; } } }; +#endif +} + +void tst_PackageManager::simulateErrorConditions() +{ +#ifndef Q_OS_LINUX + QSKIP("Only tested on Linux"); +#endif + + QFETCH(bool, testUpdate); + QFETCH(QString, errorString); + QFETCH(FunctionMap, functions); + + QString taskId; + + if (testUpdate) { + // the check will run when updating a package, so we need to install it first + + taskId = m_pm->startPackageInstallation(QUrl::fromLocalFile(qL1S(AM_TESTDATA_DIR "packages/test-dev-signed.appkg"))); + QVERIFY(!taskId.isEmpty()); + m_pm->acknowledgePackageInstallation(taskId); + QVERIFY(m_finishedSpy->wait(spyTimeout)); + QCOMPARE(m_finishedSpy->first()[0].toString(), taskId); + clearSignalSpies(); + } + + foreach (const auto &f, functions.values(qSL("before-start"))) + QVERIFY(f()); + + taskId = m_pm->startPackageInstallation(QUrl::fromLocalFile(qL1S(AM_TESTDATA_DIR "packages/test-dev-signed.appkg"))); + + foreach (const auto &f, functions.values(qSL("after-start"))) + QVERIFY(f()); + + m_pm->acknowledgePackageInstallation(taskId); + + QVERIFY(m_failedSpy->wait(spyTimeout)); + QCOMPARE(m_failedSpy->first()[0].toString(), taskId); + AM_CHECK_ERRORSTRING(m_failedSpy->first()[2].toString(), errorString); + clearSignalSpies(); + + foreach (const auto &f, functions.values(qSL("after-failed"))) + QVERIFY(f()); + + if (testUpdate) { + taskId = m_pm->removePackage(qSL("com.pelagicore.test"), false); + + QVERIFY(m_finishedSpy->wait(spyTimeout)); + QCOMPARE(m_finishedSpy->first()[0].toString(), taskId); + } +} + + +void tst_PackageManager::cancelPackageInstallation_data() +{ + QTest::addColumn<bool>("expectedResult"); + + // please note that the data tag names are used in the actual test function below! + + QTest::newRow("before-started-signal") << true; + QTest::newRow("after-started-signal") << true; + QTest::newRow("after-blocking-until-installation-acknowledge-signal") << true; + QTest::newRow("after-finished-signal") << false; +} + +void tst_PackageManager::cancelPackageInstallation() +{ + QFETCH(bool, expectedResult); + + QString taskId = m_pm->startPackageInstallation(QUrl::fromLocalFile(qL1S(AM_TESTDATA_DIR "packages/test-dev-signed.appkg"))); + QVERIFY(!taskId.isEmpty()); + + if (isDataTag("before-started-signal")) { + QCOMPARE(m_pm->cancelTask(taskId), expectedResult); + } else if (isDataTag("after-started-signal")) { + QVERIFY(m_startedSpy->wait(spyTimeout)); + QCOMPARE(m_startedSpy->first()[0].toString(), taskId); + QCOMPARE(m_pm->cancelTask(taskId), expectedResult); + } else if (isDataTag("after-blocking-until-installation-acknowledge-signal")) { + QVERIFY(m_blockingUntilInstallationAcknowledgeSpy->wait(spyTimeout)); + QCOMPARE(m_blockingUntilInstallationAcknowledgeSpy->first()[0].toString(), taskId); + QCOMPARE(m_pm->cancelTask(taskId), expectedResult); + } else if (isDataTag("after-finished-signal")) { + m_pm->acknowledgePackageInstallation(taskId); + QVERIFY(m_finishedSpy->wait(spyTimeout)); + QCOMPARE(m_finishedSpy->first()[0].toString(), taskId); + QCOMPARE(m_pm->cancelTask(taskId), expectedResult); + } + + if (expectedResult) { + if (!m_startedSpy->isEmpty()) { + QVERIFY(m_failedSpy->wait(spyTimeout)); + QCOMPARE(m_failedSpy->first()[0].toString(), taskId); + QCOMPARE(m_failedSpy->first()[1].toInt(), int(Error::Canceled)); + } + } else { + clearSignalSpies(); + + taskId = m_pm->removePackage(qSL("com.pelagicore.test"), false); + QVERIFY(!taskId.isEmpty()); + QVERIFY(m_finishedSpy->wait(spyTimeout)); + QCOMPARE(m_finishedSpy->first()[0].toString(), taskId); + } + clearSignalSpies(); +} + +void tst_PackageManager::parallelPackageInstallation() +{ + QString task1Id = m_pm->startPackageInstallation(QUrl::fromLocalFile(qL1S(AM_TESTDATA_DIR "packages/test-dev-signed.appkg"))); + QVERIFY(!task1Id.isEmpty()); + QVERIFY(m_blockingUntilInstallationAcknowledgeSpy->wait(spyTimeout)); + QCOMPARE(m_blockingUntilInstallationAcknowledgeSpy->first()[0].toString(), task1Id); + + QString task2Id = m_pm->startPackageInstallation(QUrl::fromLocalFile(qL1S(AM_TESTDATA_DIR "packages/bigtest-dev-signed.appkg"))); + QVERIFY(!task2Id.isEmpty()); + m_pm->acknowledgePackageInstallation(task2Id); + QVERIFY(m_finishedSpy->wait(spyTimeout)); + QCOMPARE(m_finishedSpy->first()[0].toString(), task2Id); + + clearSignalSpies(); + m_pm->acknowledgePackageInstallation(task1Id); + QVERIFY(m_finishedSpy->wait(spyTimeout)); + QCOMPARE(m_finishedSpy->first()[0].toString(), task1Id); + + clearSignalSpies(); +} + +void tst_PackageManager::validateDnsName_data() +{ + QTest::addColumn<QString>("dnsName"); + QTest::addColumn<int>("minParts"); + QTest::addColumn<bool>("valid"); + + // passes + QTest::newRow("normal") << "com.pelagicore.test" << 3 << true; + QTest::newRow("shortest") << "c.p.t" << 3 << true; + QTest::newRow("valid-chars") << "1-2.c-d.3.z" << 3 << true; + QTest::newRow("longest-part") << "com.012345678901234567890123456789012345678901234567890123456789012.test" << 3 << true; + QTest::newRow("longest-name") << "com.012345678901234567890123456789012345678901234567890123456789012.012345678901234567890123456789012345678901234567890123456789012.0123456789012.test" << 3 << true; + QTest::newRow("max-part-cnt") << "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.0.1.2.3.4.5.6.7.8.9.a.0.12" << 3 << true; + QTest::newRow("one-part-only") << "c" << 1 << true; + + // failures + QTest::newRow("too-few-parts") << "com.pelagicore" << 3 << false; + QTest::newRow("empty-part") << "com..test" << 3 << false; + QTest::newRow("empty") << "" << 3 << false; + QTest::newRow("dot-only") << "." << 3 << false; + QTest::newRow("invalid-char1") << "com.pelagi_core.test" << 3 << false; + QTest::newRow("invalid-char2") << "com.pelagi#core.test" << 3 << false; + QTest::newRow("invalid-char3") << "com.pelagi$core.test" << 3 << false; + QTest::newRow("invalid-char3") << "com.pelagi@core.test" << 3 << false; + QTest::newRow("unicode-char") << QString::fromUtf8("c\xc3\xb6m.pelagicore.test") << 3 << false; + QTest::newRow("upper-case") << "com.Pelagicore.test" << 3 << false; + QTest::newRow("dash-at-start") << "com.-pelagicore.test" << 3 << false; + QTest::newRow("dash-at-end") << "com.pelagicore-.test" << 3 << false; + QTest::newRow("part-too-long") << "com.x012345678901234567890123456789012345678901234567890123456789012.test" << 3 << false; +} + +void tst_PackageManager::validateDnsName() +{ + QFETCH(QString, dnsName); + QFETCH(int, minParts); + QFETCH(bool, valid); + + QString errorString; + bool result = m_pm->validateDnsName(dnsName, minParts); + + QVERIFY2(valid == result, qPrintable(errorString)); +} + +void tst_PackageManager::compareVersions_data() +{ + QTest::addColumn<QString>("version1"); + QTest::addColumn<QString>("version2"); + QTest::addColumn<int>("result"); + + + QTest::newRow("1") << "" << "" << 0; + QTest::newRow("2") << "0" << "0" << 0; + QTest::newRow("3") << "foo" << "foo" << 0; + QTest::newRow("4") << "1foo" << "1foo" << 0; + QTest::newRow("5") << "foo1" << "foo1" << 0; + QTest::newRow("6") << "13.403.51-alpha2+git" << "13.403.51-alpha2+git" << 0; + QTest::newRow("7") << "1" << "2" << -1; + QTest::newRow("8") << "2" << "1" << 1; + QTest::newRow("9") << "1.0" << "2.0" << -1; + QTest::newRow("10") << "1.99" << "2.0" << -1; + QTest::newRow("11") << "1.9" << "11" << -1; + QTest::newRow("12") << "9" << "10" << -1; + QTest::newRow("12") << "9a" << "10" << -1; + QTest::newRow("13") << "9-a" << "10" << -1; + QTest::newRow("14") << "13.403.51-alpha2+gi" << "13.403.51-alpha2+git" << -1; + QTest::newRow("15") << "13.403.51-alpha1+git" << "13.403.51-alpha2+git" << -1; + QTest::newRow("16") << "13.403.51-alpha2+git" << "13.403.51-beta1+git" << -1; + QTest::newRow("17") << "13.403.51-alpha2+git" << "13.403.52" << -1; + QTest::newRow("18") << "13.403.51-alpha2+git" << "13.403.52-alpha2+git" << -1; + QTest::newRow("19") << "13.403.51-alpha2+git" << "13.404" << -1; + QTest::newRow("20") << "13.402" << "13.403.51-alpha2+git" << -1; + QTest::newRow("21") << "12.403.51-alpha2+git" << "13.403.51-alpha2+git" << -1; +} + +void tst_PackageManager::compareVersions() +{ + QFETCH(QString, version1); + QFETCH(QString, version2); + QFETCH(int, result); + + int cmp = m_pm->compareVersions(version1, version2); + QCOMPARE(cmp, result); + + if (result) { + cmp = m_pm->compareVersions(version2, version1); + QCOMPARE(cmp, -result); + } +} + +static tst_PackageManager *tstPackageManager = nullptr; + +int main(int argc, char **argv) +{ + PackageUtilities::ensureCorrectLocale(); + + try { + Sudo::forkServer(Sudo::DropPrivilegesPermanently); + startedSudoServer = true; + } catch (...) { } + + QCoreApplication a(argc, argv); + tstPackageManager = new tst_PackageManager(&a); + + return QTest::qExec(tstPackageManager, argc, argv); +} + +#include "tst_applicationinstaller.moc" diff --git a/tests/auto/configuration/CMakeLists.txt b/tests/auto/configuration/CMakeLists.txt new file mode 100644 index 00000000..58eeed5f --- /dev/null +++ b/tests/auto/configuration/CMakeLists.txt @@ -0,0 +1,31 @@ + +qt_internal_add_test(tst_configuration + SOURCES + ../error-checking.h + tst_configuration.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManCommonPrivate + Qt::AppManMainPrivate +) + +# Resources: +set(qmake_immediate_resource_files + "data/config1.yaml" + "data/config2.yaml" + "data/empty.yaml" +) + +qt_internal_add_resource(tst_configuration "qmake_immediate" + PREFIX + "/" + FILES + ${qmake_immediate_resource_files} +) + +qt_internal_extend_target(tst_configuration CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/configuration/configuration.pro b/tests/auto/configuration/configuration.pro new file mode 100644 index 00000000..da3ff241 --- /dev/null +++ b/tests/auto/configuration/configuration.pro @@ -0,0 +1,12 @@ +TARGET = tst_configuration + +include($$PWD/../tests.pri) + +QT *= appman_common-private appman_main-private + +SOURCES += tst_configuration.cpp + +RESOURCES += \ + data/empty.yaml \ + data/config1.yaml \ + data/config2.yaml \ diff --git a/tests/auto/configuration/data/config1.yaml b/tests/auto/configuration/data/config1.yaml new file mode 100644 index 00000000..06954344 --- /dev/null +++ b/tests/auto/configuration/data/config1.yaml @@ -0,0 +1,98 @@ +formatVersion: 1 +formatType: am-configuration +--- +runtimes: + r-test: + r-parameter: r-value + +containers: + selection: selectionFunction + c-test: + c-parameter: c-value + +intents: + disable: true + timeouts: + disambiguation: 1 + startApplication: 2 + replyFromApplication: 3 + replyFromSystem: 4 + +plugins: + startup: [ s1, s2 ] + container: [ c1, c2 ] + +logging: + dlt: + id: 'dltid' + description: 'dltdesc' + rules: [ lr1, lr2 ] + messagePattern: 'msgPattern' + useAMConsoleLogger: true + +installer: + disable: true + caCertificates: [ cert1, cert2 ] + +dbus: + iface1: + register: 'foobus' + +quicklaunch: + idleLoad: 0.5 + runtimesPerContainer: 5 + +ui: + opengl: + desktopProfile: 'compatibility' + esMajorVersion: 5 + esMinorVersion: 15 + enableTouchEmulation: true + iconThemeSearchPaths: [ itsp1, itsp2 ] + iconThemeName: mytheme + style: mystyle + loadDummyData: true + importPaths: [ ip1, ip2 ] + pluginPaths: [ pp1, pp2 ] + windowIcon: 'icon.png' + fullscreen: true + mainQml: main.qml + resources: [ r1, r2 ] + +applications: + builtinAppsManifestDir: 'builtin-dir' + installationDir: 'installation-dir' + documentDir: 'doc-dir' + +crashAction: + printBacktrace: true + printQmlStack: true + waitForGdbAttach: true + dumpCore: true + +systemProperties: + public: + public-prop: public-value + protected: + protected-prop: protected-value + private: + private-prop: private-value + +flags: + forceSingleProcess: true + forceMultiProcess: true + noSecurity: true + developmentMode: true + noUiWatchdog: true + +wayland: + socketName: "my-wlsock-42" + extraSockets: + - path: path-es1 + permissions: 0440 + userId: 1 + groupId: 2 + - path: path-es2 + permissions: 0222 + userId: 3 + groupId: 4 diff --git a/tests/auto/configuration/data/config2.yaml b/tests/auto/configuration/data/config2.yaml new file mode 100644 index 00000000..cd9d9766 --- /dev/null +++ b/tests/auto/configuration/data/config2.yaml @@ -0,0 +1,88 @@ +formatVersion: 1 +formatType: am-configuration +--- +runtimes: + r-test: + r-parameter: xr-value + r-test2: + r-parameter2: r-value2 + +containers: + selection: + - '2': second + + c-test: + c-parameter: xc-value + c-test2: + c-parameter2: c-value2 + +intents: + timeouts: + disambiguation: 5 + startApplication: 6 + replyFromApplication: 7 + replyFromSystem: 8 + +plugins: + startup: s3 + container: [ c3, c4 ] + +logging: + dlt: + id: 'dltid2' + description: 'dltdesc2' + rules: lr3 + messagePattern: 'msgPattern2' + useAMConsoleLogger: 'auto' + +installer: + disable: true + caCertificates: [ cert3 ] + +dbus: + iface1: + register: 'foobus1' + iface2: + register: 'foobus2' + +quicklaunch: + idleLoad: 0.2 + runtimesPerContainer: 3 + +ui: + opengl: + desktopProfile: 'classic' + esMajorVersion: 1 + esMinorVersion: 0 + enableTouchEmulation: true + iconThemeSearchPaths: [ itsp3 ] + iconThemeName: mytheme2 + style: mystyle2 + loadDummyData: true + importPaths: ip3 + pluginPaths: pp3 + windowIcon: 'icon2.png' + fullscreen: true + mainQml: main2.qml + resources: r3 + +applications: + builtinAppsManifestDir: 'builtin-dir2' + installationDir: 'installation-dir2' + documentDir: 'doc-dir2' + +systemProperties: + public: + public-prop: xpublic-value + public-prop2: public-value2 + protected: + protected-prop: xprotected-value + protected-prop2: protected-value2 + private: + private-prop: xprivate-value + private-prop2: private-value2 + +wayland: + socketName: "other-wlsock-0" + extraSockets: + - path: path-es3 diff --git a/tests/auto/configuration/data/empty.yaml b/tests/auto/configuration/data/empty.yaml new file mode 100644 index 00000000..a87fe348 --- /dev/null +++ b/tests/auto/configuration/data/empty.yaml @@ -0,0 +1,3 @@ +formatVersion: 1 +formatType: am-configuration +--- diff --git a/tests/auto/configuration/tst_configuration.cpp b/tests/auto/configuration/tst_configuration.cpp new file mode 100644 index 00000000..67eeec41 --- /dev/null +++ b/tests/auto/configuration/tst_configuration.cpp @@ -0,0 +1,523 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore/QtCore> +#include <QtTest/QtTest> + +#include <QtAppManMain/configuration.h> +#include <QtAppManCommon/exception.h> +#include <QtAppManCommon/global.h> + +QT_USE_NAMESPACE_AM + +class tst_Configuration : public QObject +{ + Q_OBJECT + +public: + tst_Configuration(); + +private slots: + void defaultConfig(); + void simpleConfig(); + void mergedConfig(); + void commandLineConfig(); + +private: + QDir pwd; +}; + + +tst_Configuration::tst_Configuration() +{ } + +void tst_Configuration::defaultConfig() +{ + Configuration c; + c.parseWithArguments({ qSL("test"), qSL("--no-cache") }); + + QVERIFY(c.noCache()); + + // command line only + QCOMPARE(c.noFullscreen(), false); + QCOMPARE(c.verbose(), false); + QCOMPARE(c.slowAnimations(), false); + QCOMPARE(c.noDltLogging(), false); + QCOMPARE(c.singleApp(), qSL("")); + QCOMPARE(c.qmlDebugging(), false); + + // values from config file + QCOMPARE(c.mainQmlFile(), qSL("")); + + QCOMPARE(c.builtinAppsManifestDirs(), {}); + QCOMPARE(c.documentDir(), qSL("")); + + QCOMPARE(c.installationDir(), qSL("")); + QCOMPARE(c.disableInstaller(), false); + QCOMPARE(c.disableIntents(), false); + QCOMPARE(c.intentTimeoutForDisambiguation(), 10000); + QCOMPARE(c.intentTimeoutForStartApplication(), 3000); + QCOMPARE(c.intentTimeoutForReplyFromApplication(), 5000); + QCOMPARE(c.intentTimeoutForReplyFromSystem(), 20000); + + QCOMPARE(c.fullscreen(), false); + QCOMPARE(c.windowIcon(), qSL("")); + QCOMPARE(c.importPaths(), {}); + QCOMPARE(c.pluginPaths(), {}); + QCOMPARE(c.loadDummyData(), false); + QCOMPARE(c.noSecurity(), false); + QCOMPARE(c.developmentMode(), false); + QCOMPARE(c.noUiWatchdog(), false); + QCOMPARE(c.forceSingleProcess(), false); + QCOMPARE(c.forceMultiProcess(), false); + QCOMPARE(c.loggingRules(), {}); + QCOMPARE(c.messagePattern(), qSL("")); + QCOMPARE(c.useAMConsoleLogger(), QVariant()); + QCOMPARE(c.style(), qSL("")); + QCOMPARE(c.iconThemeName(), qSL("")); + QCOMPARE(c.iconThemeSearchPaths(), {}); + QCOMPARE(c.enableTouchEmulation(), false); + QCOMPARE(c.dltId(), qSL("")); + QCOMPARE(c.dltDescription(), qSL("")); + QCOMPARE(c.resources(), {}); + + QCOMPARE(c.openGLConfiguration(), QVariantMap {}); + + QCOMPARE(c.installationLocations(), {}); + + QCOMPARE(c.containerSelectionConfiguration(), {}); + QCOMPARE(c.containerConfigurations(), QVariantMap {}); + QCOMPARE(c.runtimeConfigurations(), QVariantMap {}); + + QCOMPARE(c.dbusRegistration("iface1"), qSL("auto")); + + QCOMPARE(c.rawSystemProperties(), QVariantMap {}); + + QCOMPARE(c.quickLaunchIdleLoad(), qreal(0)); + QCOMPARE(c.quickLaunchRuntimesPerContainer(), 0); + + QString defaultWaylandSocketName = +#if defined(Q_OS_LINUX) + qSL("qtam-wayland-0"); +#else + QString(); +#endif + + QCOMPARE(c.waylandSocketName(), defaultWaylandSocketName); + QCOMPARE(c.waylandExtraSockets(), {}); + + QCOMPARE(c.managerCrashAction(), QVariantMap {}); + + QCOMPARE(c.caCertificates(), {}); + + QCOMPARE(c.pluginFilePaths("container"), {}); + QCOMPARE(c.pluginFilePaths("startup"), {}); +} + +void tst_Configuration::simpleConfig() +{ + Configuration c({ qSL(":/data/config1.yaml") }, qSL(":/build-config.yaml")); + c.parseWithArguments({ qSL("test"), qSL("--no-cache") }); + + QVERIFY(c.noCache()); + + // command line only + QCOMPARE(c.noFullscreen(), false); + QCOMPARE(c.verbose(), false); + QCOMPARE(c.slowAnimations(), false); + QCOMPARE(c.noDltLogging(), false); + QCOMPARE(c.singleApp(), qSL("")); + QCOMPARE(c.qmlDebugging(), false); + + // values from config file + QCOMPARE(c.mainQmlFile(), qSL("main.qml")); + + QCOMPARE(c.builtinAppsManifestDirs(), { qSL("builtin-dir") }); + QCOMPARE(c.documentDir(), qSL("doc-dir")); + + QCOMPARE(c.installationDir(), qSL("installation-dir")); + QCOMPARE(c.disableInstaller(), true); + QCOMPARE(c.disableIntents(), true); + QCOMPARE(c.intentTimeoutForDisambiguation(), 1); + QCOMPARE(c.intentTimeoutForStartApplication(), 2); + QCOMPARE(c.intentTimeoutForReplyFromApplication(), 3); + QCOMPARE(c.intentTimeoutForReplyFromSystem(), 4); + + QCOMPARE(c.fullscreen(), true); + QCOMPARE(c.windowIcon(), qSL("icon.png")); + QCOMPARE(c.importPaths(), QStringList({ pwd.absoluteFilePath(qSL("ip1")), pwd.absoluteFilePath(qSL("ip2")) })); + QCOMPARE(c.pluginPaths(), QStringList({ qSL("pp1"), qSL("pp2") })); + QCOMPARE(c.loadDummyData(), true); + QCOMPARE(c.noSecurity(), true); + QCOMPARE(c.developmentMode(), true); + QCOMPARE(c.noUiWatchdog(), true); + QCOMPARE(c.forceSingleProcess(), true); + QCOMPARE(c.forceMultiProcess(), true); + QCOMPARE(c.loggingRules(), QStringList({ qSL("lr1"), qSL("lr2") })); + QCOMPARE(c.messagePattern(), qSL("msgPattern")); + QCOMPARE(c.useAMConsoleLogger(), QVariant(true)); + QCOMPARE(c.style(), qSL("mystyle")); + QCOMPARE(c.iconThemeName(), qSL("mytheme")); + QCOMPARE(c.iconThemeSearchPaths(), QStringList({ qSL("itsp1"), qSL("itsp2") })); + QCOMPARE(c.enableTouchEmulation(), true); + QCOMPARE(c.dltId(), qSL("dltid")); + QCOMPARE(c.dltDescription(), qSL("dltdesc")); + QCOMPARE(c.resources(), QStringList({ qSL("r1"), qSL("r2") })); + + QCOMPARE(c.openGLConfiguration(), QVariantMap + ({ + { qSL("desktopProfile"), qSL("compatibility") }, + { qSL("esMajorVersion"), 5 }, + { qSL("esMinorVersion"), 15 } + })); + + QCOMPARE(c.installationLocations(), {}); + + QList<QPair<QString, QString>> containerSelectionConfiguration { + { qSL("*"), qSL("selectionFunction") } + }; + QCOMPARE(c.containerSelectionConfiguration(), containerSelectionConfiguration); + QCOMPARE(c.containerConfigurations(), QVariantMap + ({ + { qSL("c-test"), QVariantMap { + { qSL("c-parameter"), qSL("c-value") } + } } + })); + QCOMPARE(c.runtimeConfigurations(), QVariantMap + ({ + { qSL("r-test"), QVariantMap { + { qSL("r-parameter"), qSL("r-value") } + } } + })); + + QCOMPARE(c.dbusRegistration("iface1"), qSL("foobus")); + + QCOMPARE(c.rawSystemProperties(), QVariantMap + ({ + { qSL("public"), QVariantMap { + { qSL("public-prop"), qSL("public-value") } + } }, + { qSL("protected"), QVariantMap { + { qSL("protected-prop"), qSL("protected-value") } + } }, + { qSL("private"), QVariantMap { + { qSL("private-prop"), qSL("private-value") } + } } + })); + + QCOMPARE(c.quickLaunchIdleLoad(), qreal(0.5)); + QCOMPARE(c.quickLaunchRuntimesPerContainer(), 5); + + QCOMPARE(c.waylandSocketName(), qSL("my-wlsock-42")); + + QCOMPARE(c.waylandExtraSockets(), QVariantList + ({ + QVariantMap { + { qSL("path"), qSL("path-es1") }, + { qSL("permissions"), 0440 }, + { qSL("userId"), 1 }, + { qSL("groupId"), 2 } + }, + QVariantMap { + { qSL("path"), qSL("path-es2") }, + { qSL("permissions"), 0222 }, + { qSL("userId"), 3 }, + { qSL("groupId"), 4 } + } + })); + + QCOMPARE(c.managerCrashAction(), QVariantMap + ({ + { qSL("printBacktrace"), true }, + { qSL("printQmlStack"), true }, + { qSL("waitForGdbAttach"), true }, + { qSL("dumpCore"), true } + })); + + QCOMPARE(c.caCertificates(), QStringList({ qSL("cert1"), qSL("cert2") })); + + QCOMPARE(c.pluginFilePaths("startup"), QStringList({ qSL("s1"), qSL("s2") })); + QCOMPARE(c.pluginFilePaths("container"), QStringList({ qSL("c1"), qSL("c2") })); +} + +void tst_Configuration::mergedConfig() +{ + Configuration c({ qSL(":/data/config1.yaml"), qSL(":/data/config2.yaml") }, qSL(":/build-config.yaml")); + c.parseWithArguments({ qSL("test"), qSL("--no-cache") }); + + QVERIFY(c.noCache()); + + // command line only + QCOMPARE(c.noFullscreen(), false); + QCOMPARE(c.verbose(), false); + QCOMPARE(c.slowAnimations(), false); + QCOMPARE(c.noDltLogging(), false); + QCOMPARE(c.singleApp(), qSL("")); + QCOMPARE(c.qmlDebugging(), false); + + // values from config file + QCOMPARE(c.mainQmlFile(), qSL("main2.qml")); + + QCOMPARE(c.builtinAppsManifestDirs(), QStringList({ qSL("builtin-dir"), qSL("builtin-dir2") })); + QCOMPARE(c.documentDir(), qSL("doc-dir2")); + + QCOMPARE(c.installationDir(), qSL("installation-dir2")); + QCOMPARE(c.disableInstaller(), true); + QCOMPARE(c.disableIntents(), true); + QCOMPARE(c.intentTimeoutForDisambiguation(), 5); + QCOMPARE(c.intentTimeoutForStartApplication(), 6); + QCOMPARE(c.intentTimeoutForReplyFromApplication(), 7); + QCOMPARE(c.intentTimeoutForReplyFromSystem(), 8); + + QCOMPARE(c.fullscreen(), true); + QCOMPARE(c.windowIcon(), qSL("icon2.png")); + QCOMPARE(c.importPaths(), QStringList + ({ pwd.absoluteFilePath(qSL("ip1")), + pwd.absoluteFilePath(qSL("ip2")), + pwd.absoluteFilePath(qSL("ip3")) })); + QCOMPARE(c.pluginPaths(), QStringList({ qSL("pp1"), qSL("pp2"), qSL("pp3") })); + QCOMPARE(c.loadDummyData(), true); + QCOMPARE(c.noSecurity(), true); + QCOMPARE(c.developmentMode(), true); + QCOMPARE(c.noUiWatchdog(), true); + QCOMPARE(c.forceSingleProcess(), true); + QCOMPARE(c.forceMultiProcess(), true); + QCOMPARE(c.loggingRules(), QStringList({ qSL("lr1"), qSL("lr2"), qSL("lr3") })); + QCOMPARE(c.messagePattern(), qSL("msgPattern2")); + QCOMPARE(c.useAMConsoleLogger(), QVariant()); + QCOMPARE(c.style(), qSL("mystyle2")); + QCOMPARE(c.iconThemeName(), qSL("mytheme2")); + QCOMPARE(c.iconThemeSearchPaths(), QStringList({ qSL("itsp1"), qSL("itsp2"), qSL("itsp3") })); + QCOMPARE(c.enableTouchEmulation(), true); + QCOMPARE(c.dltId(), qSL("dltid2")); + QCOMPARE(c.dltDescription(), qSL("dltdesc2")); + QCOMPARE(c.resources(), QStringList({ qSL("r1"), qSL("r2"), qSL("r3") })); + + QCOMPARE(c.openGLConfiguration(), QVariantMap + ({ + { qSL("desktopProfile"), qSL("classic") }, + { qSL("esMajorVersion"), 1 }, + { qSL("esMinorVersion"), 0 }, + })); + + QCOMPARE(c.installationLocations(), {}); + + QList<QPair<QString, QString>> containerSelectionConfiguration { + { qSL("*"), qSL("selectionFunction") }, + { qSL("2"), qSL("second") } + }; + QCOMPARE(c.containerSelectionConfiguration(), containerSelectionConfiguration); + QCOMPARE(c.containerConfigurations(), QVariantMap + ({ + { qSL("c-test"), QVariantMap { + { qSL("c-parameter"), qSL("xc-value") }, + } }, + { qSL("c-test2"), QVariantMap { + { qSL("c-parameter2"), qSL("c-value2") }, + } } + + })); + + QCOMPARE(c.runtimeConfigurations(), QVariantMap + ({ + { qSL("r-test"), QVariantMap { + { qSL("r-parameter"), qSL("xr-value") }, + } }, + { qSL("r-test2"), QVariantMap { + { qSL("r-parameter2"), qSL("r-value2") }, + } } + + })); + + QCOMPARE(c.dbusRegistration("iface1"), qSL("foobus1")); + QCOMPARE(c.dbusRegistration("iface2"), qSL("foobus2")); + + QCOMPARE(c.rawSystemProperties(), QVariantMap + ({ + { qSL("public"), QVariantMap { + { qSL("public-prop"), qSL("xpublic-value") }, + { qSL("public-prop2"), qSL("public-value2") } + } }, + { qSL("protected"), QVariantMap { + { qSL("protected-prop"), qSL("xprotected-value") }, + { qSL("protected-prop2"), qSL("protected-value2") } + } }, + { qSL("private"), QVariantMap { + { qSL("private-prop"), qSL("xprivate-value") }, + { qSL("private-prop2"), qSL("private-value2") } + } } + })); + + QCOMPARE(c.quickLaunchIdleLoad(), qreal(0.2)); + QCOMPARE(c.quickLaunchRuntimesPerContainer(), 3); + + QCOMPARE(c.waylandSocketName(), qSL("other-wlsock-0")); + + QCOMPARE(c.waylandExtraSockets(), QVariantList + ({ + QVariantMap { + { qSL("path"), qSL("path-es1") }, + { qSL("permissions"), 0440 }, + { qSL("userId"), 1 }, + { qSL("groupId"), 2 } + }, + QVariantMap { + { qSL("path"), qSL("path-es2") }, + { qSL("permissions"), 0222 }, + { qSL("userId"), 3 }, + { qSL("groupId"), 4 } + }, + QVariantMap { + { qSL("path"), qSL("path-es3") }, + } + })); + + QCOMPARE(c.managerCrashAction(), QVariantMap + ({ + { qSL("printBacktrace"), true }, + { qSL("printQmlStack"), true }, + { qSL("waitForGdbAttach"), true }, + { qSL("dumpCore"), true } + })); + + QCOMPARE(c.caCertificates(), QStringList({ qSL("cert1"), qSL("cert2"), qSL("cert3") })); + + QCOMPARE(c.pluginFilePaths("container"), QStringList({ qSL("c1"), qSL("c2"), qSL("c3"), qSL("c4") })); + QCOMPARE(c.pluginFilePaths("startup"), QStringList({ qSL("s1"), qSL("s2"), qSL("s3") })); +} + +void tst_Configuration::commandLineConfig() +{ + Configuration c; + QStringList commandLine { qSL("test"), qSL("--no-cache") }; + + commandLine << "--builtin-apps-manifest-dir" << "builtin-dir-cl1" + << "--builtin-apps-manifest-dir" << "builtin-dir-cl2" + << "--installation-dir" << "installation-dir-cl" + << "--document-dir" << "document-dir-cl" + << "--disable-installer" + << "--disable-intents" + << "--dbus" << "system" + << "--no-fullscreen" + << "-I" << "ip-cl1" + << "-I" << "ip-cl2" + << "-v" + << "--slow-animations" + << "--load-dummydata" + << "--no-security" + << "--development-mode" + << "--no-ui-watchdog" + << "--no-dlt-logging" + << "--force-single-process" + << "--force-multi-process" + << "--wayland-socket-name" << "wlsock-1" + << "--single-app" << "appname" + << "--logging-rule" << "cl-lr1" + << "--logging-rule" << "cl-lr2" + << "--qml-debug" + << "--enable-touch-emulation" + << "main-cl.qml"; + + c.parseWithArguments(commandLine); + + QVERIFY(c.noCache()); + + // command line only + QCOMPARE(c.noFullscreen(), true); + QCOMPARE(c.verbose(), true); + QCOMPARE(c.slowAnimations(), true); + QCOMPARE(c.noDltLogging(), true); + QCOMPARE(c.singleApp(), qSL("appname")); + QCOMPARE(c.qmlDebugging(), true); + + // values from config file + QCOMPARE(c.mainQmlFile(), qSL("main-cl.qml")); + + QCOMPARE(c.builtinAppsManifestDirs(), QStringList({ qSL("builtin-dir-cl1"), qSL("builtin-dir-cl2") })); + QCOMPARE(c.documentDir(), qSL("document-dir-cl")); + + QCOMPARE(c.installationDir(), qSL("installation-dir-cl")); + QCOMPARE(c.disableInstaller(), true); + QCOMPARE(c.disableIntents(), true); + QCOMPARE(c.intentTimeoutForDisambiguation(), 10000); + QCOMPARE(c.intentTimeoutForStartApplication(), 3000); + QCOMPARE(c.intentTimeoutForReplyFromApplication(), 5000); + QCOMPARE(c.intentTimeoutForReplyFromSystem(), 20000); + + QCOMPARE(c.fullscreen(), false); + QCOMPARE(c.windowIcon(), qSL("")); + QCOMPARE(c.importPaths(), QStringList({ pwd.absoluteFilePath(qSL("ip-cl1")), + pwd.absoluteFilePath(qSL("ip-cl2")) })); + QCOMPARE(c.pluginPaths(), {}); + QCOMPARE(c.loadDummyData(), true); + QCOMPARE(c.noSecurity(), true); + QCOMPARE(c.developmentMode(), true); + QCOMPARE(c.noUiWatchdog(), true); + QCOMPARE(c.forceSingleProcess(), true); + QCOMPARE(c.forceMultiProcess(), true); + QCOMPARE(c.loggingRules(), QStringList({ qSL("cl-lr1"), qSL("cl-lr2") })); + QCOMPARE(c.messagePattern(), qSL("")); + QCOMPARE(c.useAMConsoleLogger(), QVariant()); + QCOMPARE(c.style(), qSL("")); + QCOMPARE(c.iconThemeName(), qSL("")); + QCOMPARE(c.iconThemeSearchPaths(), {}); + QCOMPARE(c.enableTouchEmulation(), true); + QCOMPARE(c.dltId(), qSL("")); + QCOMPARE(c.dltDescription(), qSL("")); + QCOMPARE(c.resources(), {}); + + QCOMPARE(c.openGLConfiguration(), QVariantMap {}); + + QCOMPARE(c.installationLocations(), {}); + + QCOMPARE(c.containerSelectionConfiguration(), {}); + QCOMPARE(c.containerConfigurations(), QVariantMap{}); + QCOMPARE(c.runtimeConfigurations(), QVariantMap{}); + + QCOMPARE(c.dbusRegistration("iface1"), qSL("system")); + + QCOMPARE(c.rawSystemProperties(), QVariantMap {}); + + QCOMPARE(c.quickLaunchIdleLoad(), qreal(0)); + QCOMPARE(c.quickLaunchRuntimesPerContainer(), 0); + + QCOMPARE(c.waylandSocketName(), qSL("wlsock-1")); + QCOMPARE(c.waylandExtraSockets(), {}); + + QCOMPARE(c.managerCrashAction(), QVariantMap {}); + + QCOMPARE(c.caCertificates(), {}); + + QCOMPARE(c.pluginFilePaths("container"), {}); + QCOMPARE(c.pluginFilePaths("startup"), {}); +} + + +QTEST_MAIN(tst_Configuration) + +#include "tst_configuration.moc" diff --git a/tests/auto/cryptography/CMakeLists.txt b/tests/auto/cryptography/CMakeLists.txt new file mode 100644 index 00000000..4cc87a7d --- /dev/null +++ b/tests/auto/cryptography/CMakeLists.txt @@ -0,0 +1,20 @@ + +qt_internal_add_test(tst_cryptography + SOURCES + ../error-checking.h + tst_cryptography.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManCommonPrivate + Qt::AppManCryptoPrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_cryptography CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/cryptography/cryptography.pro b/tests/auto/cryptography/cryptography.pro new file mode 100644 index 00000000..d18f6441 --- /dev/null +++ b/tests/auto/cryptography/cryptography.pro @@ -0,0 +1,7 @@ +TARGET = tst_cryptography + +include($$PWD/../tests.pri) + +QT *= appman_common-private appman_crypto-private + +SOURCES += tst_cryptography.cpp diff --git a/tests/auto/cryptography/tst_cryptography.cpp b/tests/auto/cryptography/tst_cryptography.cpp new file mode 100644 index 00000000..ff8c2ae6 --- /dev/null +++ b/tests/auto/cryptography/tst_cryptography.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> + +#include "cryptography.h" + +QT_USE_NAMESPACE_AM + +class tst_Cryptography : public QObject +{ + Q_OBJECT + +public: + tst_Cryptography(); + +private slots: + void random(); +}; + +tst_Cryptography::tst_Cryptography() +{ } + +void tst_Cryptography::random() +{ + QVERIFY(Cryptography::generateRandomBytes(-1).isEmpty()); + QVERIFY(Cryptography::generateRandomBytes(0).isEmpty()); + QVERIFY(!Cryptography::generateRandomBytes(1).isEmpty()); + QCOMPARE(Cryptography::generateRandomBytes(128).size(), 128); +} + +QTEST_APPLESS_MAIN(tst_Cryptography) + +#include "tst_cryptography.moc" diff --git a/tests/auto/debugwrapper/CMakeLists.txt b/tests/auto/debugwrapper/CMakeLists.txt new file mode 100644 index 00000000..fc041f88 --- /dev/null +++ b/tests/auto/debugwrapper/CMakeLists.txt @@ -0,0 +1,19 @@ + +qt_internal_add_test(tst_debugwrapper + SOURCES + ../error-checking.h + tst_debugwrapper.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManManagerPrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_debugwrapper CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/debugwrapper/debugwrapper.pro b/tests/auto/debugwrapper/debugwrapper.pro new file mode 100644 index 00000000..b7e9128a --- /dev/null +++ b/tests/auto/debugwrapper/debugwrapper.pro @@ -0,0 +1,8 @@ +TARGET = tst_debugwrapper + +include($$PWD/../tests.pri) + +QT *= \ + appman_manager-private + +SOURCES += tst_debugwrapper.cpp diff --git a/tests/auto/debugwrapper/tst_debugwrapper.cpp b/tests/auto/debugwrapper/tst_debugwrapper.cpp new file mode 100644 index 00000000..8037bb31 --- /dev/null +++ b/tests/auto/debugwrapper/tst_debugwrapper.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> + +#include <debugwrapper.h> + +#include "../error-checking.h" + +QT_USE_NAMESPACE_AM + +class tst_DebugWrapper : public QObject +{ + Q_OBJECT + +public: + tst_DebugWrapper(QObject *parent = nullptr); + ~tst_DebugWrapper(); + +private slots: + void specification_data(); + void specification(); + + void substitute_data(); + void substitute(); +}; + + +tst_DebugWrapper::tst_DebugWrapper(QObject *parent) + : QObject(parent) +{ } + +tst_DebugWrapper::~tst_DebugWrapper() +{ } + +typedef QMap<QString, QString> StringMap; +Q_DECLARE_METATYPE(StringMap) + +void tst_DebugWrapper::specification_data() +{ + QTest::addColumn<QString>("spec"); + QTest::addColumn<bool>("valid"); + QTest::addColumn<StringMap>("env"); + QTest::addColumn<QStringList>("cmd"); + + QMap<QString, QString> noenv; + + QTest::newRow("empty") << "" << false << noenv << QStringList(); + QTest::newRow("empty2") << " " << false << noenv << QStringList(); + + QTest::newRow("nocmd") << "foo=bar" << true << StringMap {{ "foo", "bar" }} << QStringList { "%program%", "%arguments%" }; + QTest::newRow("nocmd2") << "foo=bar " << true << StringMap {{ "foo", "bar" }} << QStringList { "%program%", "%arguments%" }; + + QTest::newRow("1") << "foo" << true << noenv << QStringList { "foo", "%program%", "%arguments%" }; + QTest::newRow("2") << " foo" << true << noenv << QStringList { "foo", "%program%", "%arguments%" }; + QTest::newRow("3") << "foo " << true << noenv << QStringList { "foo", "%program%", "%arguments%" }; + QTest::newRow("4") << "foo bar" << true << noenv << QStringList { "foo", "bar", "%program%", "%arguments%" }; + QTest::newRow("5") << "foo bar" << true << noenv << QStringList { "foo", "bar", "%program%", "%arguments%" }; + QTest::newRow("6") << "foo bar baz" << true << noenv << QStringList { "foo", "bar", "baz", "%program%", "%arguments%" }; + QTest::newRow("7") << "fo\\ o b\\nar b\\\\az" << true << noenv << QStringList { "fo o", "b\nar", "b\\az", "%program%", "%arguments%" }; + QTest::newRow("8") << "foo=bar baz" << true << StringMap {{ "foo", "bar" }} << QStringList { "baz", "%program%", "%arguments%" }; + QTest::newRow("9") << "foo=bar a= baz zab" << true << StringMap {{ "foo", "bar" }, { "a", QString() }} << QStringList { "baz", "zab", "%program%", "%arguments%" }; + QTest::newRow("a") << "foo=b\\ a=\\n baz z\\ ab" << true << StringMap {{ "foo", "b a=\n" }} << QStringList { "baz", "z ab", "%program%", "%arguments%" }; + QTest::newRow("b") << "a=b c d=e" << true << StringMap {{ "a", "b" }} << QStringList { "c", "d=e", "%program%", "%arguments%" }; + + QTest::newRow("z") << "a=b %program% c %arguments% d" << true << StringMap {{ "a", "b" }} << QStringList { "%program%", "c", "%arguments%", "d" }; + QTest::newRow("y") << "a=b %program% c d" << true << StringMap {{ "a", "b" }} << QStringList { "%program%", "c", "d", "%arguments%" }; + QTest::newRow("x") << "a=b %arguments%" << true << StringMap {{ "a", "b" }} << QStringList { "%arguments%", "%program%" }; + QTest::newRow("w") << "%program% %arguments%" << true << noenv << QStringList { "%program%", "%arguments%" }; + QTest::newRow("w") << "%program% foo-%program% foo-%arguments%-bar %arguments%" << true << noenv << QStringList { "%program%", "foo-%program%", "foo-%arguments%-bar", "%arguments%" }; +} + +void tst_DebugWrapper::specification() +{ + QFETCH(QString, spec); + QFETCH(bool, valid); + QFETCH(StringMap, env); + QFETCH(QStringList, cmd); + + StringMap resultEnv; + QStringList resultCmd; + QCOMPARE(DebugWrapper::parseSpecification(spec, resultCmd, resultEnv), valid); + QCOMPARE(cmd, resultCmd); + QCOMPARE(env, resultEnv); +} + +void tst_DebugWrapper::substitute_data() +{ + QTest::addColumn<QStringList>("cmd"); + QTest::addColumn<QString>("program"); + QTest::addColumn<QStringList>("arguments"); + QTest::addColumn<QStringList>("result"); + + QTest::newRow("1") << QStringList { "%program%", "%arguments%" } + << QString("prg") << QStringList { "arg1", "arg2" } + << QStringList { "prg", "arg1", "arg2" }; + + QTest::newRow("2") << QStringList { "%program%" } + << QString("prg") << QStringList { "arg1", "arg2" } + << QStringList { "prg" }; + + QTest::newRow("3") << QStringList { "%program%", "\"x-%program%\"", "%arguments%", "x-%arguments%" } + << QString("prg") << QStringList { "arg1", "arg2" } + << QStringList { "prg", "\"x-prg\"", "arg1", "arg2", "x-arg1 arg2" }; + + QTest::newRow("4") << QStringList { "foo", "%arguments%", "bar", "%program%", "baz", "%arguments%", "foo2" } + << QString("prg") << QStringList { "a1", "a2", "a3" } + << QStringList { "foo", "a1", "a2", "a3", "bar", "prg", "baz", "a1", "a2", "a3", "foo2" }; +} + +void tst_DebugWrapper::substitute() +{ + QFETCH(QStringList, cmd); + QFETCH(QString, program); + QFETCH(QStringList, arguments); + QFETCH(QStringList, result); + + QCOMPARE(DebugWrapper::substituteCommand(cmd, program, arguments), result); +} + +QTEST_APPLESS_MAIN(tst_DebugWrapper) + +#include "tst_debugwrapper.moc" diff --git a/tests/auto/error-checking.h b/tests/auto/error-checking.h new file mode 100644 index 00000000..e80cdde5 --- /dev/null +++ b/tests/auto/error-checking.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +#pragma once + +#include <QTest> + +// sadly this has to be a define for QVERIFY2() to work +#define AM_CHECK_ERRORSTRING(_actual_errstr, _expected_errstr) do { \ + if (_expected_errstr.startsWith(QLatin1String("~"))) { \ + QRegularExpression re(_expected_errstr.mid(1)); \ + QVERIFY2(re.match(_actual_errstr).hasMatch(), \ + qPrintable("\n Got : " + _actual_errstr.toLocal8Bit() + \ + "\n Expected: " + _expected_errstr.toLocal8Bit())); \ + } else { \ + QCOMPARE(_actual_errstr, _expected_errstr); \ + } \ +} while (false) diff --git a/tests/auto/installationreport/CMakeLists.txt b/tests/auto/installationreport/CMakeLists.txt new file mode 100644 index 00000000..f0204d00 --- /dev/null +++ b/tests/auto/installationreport/CMakeLists.txt @@ -0,0 +1,21 @@ + +qt_internal_add_test(tst_installationreport + SOURCES + ../error-checking.h + tst_installationreport.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManCryptoPrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_installationreport CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/installationreport/installationreport.pro b/tests/auto/installationreport/installationreport.pro new file mode 100644 index 00000000..ce1aa46b --- /dev/null +++ b/tests/auto/installationreport/installationreport.pro @@ -0,0 +1,10 @@ +TARGET = tst_installationreport + +include($$PWD/../tests.pri) + +QT *= \ + appman_common-private \ + appman_crypto-private \ + appman_application-private \ + +SOURCES += tst_installationreport.cpp diff --git a/tests/auto/installationreport/tst_installationreport.cpp b/tests/auto/installationreport/tst_installationreport.cpp new file mode 100644 index 00000000..edb1b35a --- /dev/null +++ b/tests/auto/installationreport/tst_installationreport.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> + +#include "global.h" +#include "exception.h" +#include "installationreport.h" + +QT_USE_NAMESPACE_AM + +class tst_InstallationReport : public QObject +{ + Q_OBJECT + +public: + tst_InstallationReport(); + +private slots: + void test(); +}; + +tst_InstallationReport::tst_InstallationReport() +{ } + +void tst_InstallationReport::test() +{ + QStringList files { qSL("test"), qSL("more/test"), qSL("another/test/file") }; + + InstallationReport ir(qSL("com.pelagicore.test")); + QVERIFY(!ir.isValid()); + ir.addFile(files.first()); + QVERIFY(!ir.isValid()); + ir.setDiskSpaceUsed(42); + QVERIFY(!ir.isValid()); + ir.setDigest("##digest##"); + QVERIFY(ir.isValid()); + ir.addFiles(files.mid(1)); + ir.setDeveloperSignature("%%dev-sig%%"); + ir.setStoreSignature("$$store-sig$$"); + + QVERIFY(ir.isValid()); + QCOMPARE(ir.packageId(), qSL("com.pelagicore.test")); + QCOMPARE(ir.files(), files); + QCOMPARE(ir.diskSpaceUsed(), 42ULL); + QCOMPARE(ir.digest().constData(), "##digest##"); + QCOMPARE(ir.developerSignature().constData(), "%%dev-sig%%"); + QCOMPARE(ir.storeSignature().constData(), "$$store-sig$$"); + + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + QVERIFY(ir.serialize(&buffer)); + buffer.seek(0); + + InstallationReport ir2; + try { + ir2.deserialize(&buffer); + } catch (const Exception &e) { + QVERIFY2(false, e.what()); + } + buffer.seek(0); + + QVERIFY(ir2.isValid()); + QCOMPARE(ir2.packageId(), qSL("com.pelagicore.test")); + QCOMPARE(ir2.files(), files); + QCOMPARE(ir2.diskSpaceUsed(), 42ULL); + QCOMPARE(ir2.digest().constData(), "##digest##"); + QCOMPARE(ir2.developerSignature().constData(), "%%dev-sig%%"); + QCOMPARE(ir2.storeSignature().constData(), "$$store-sig$$"); + + QByteArray &yaml = buffer.buffer(); + QVERIFY(!yaml.isEmpty()); + + int pos = yaml.lastIndexOf("\n---\nhmac: '"); + QVERIFY(pos > 0); + pos += 12; + QByteArray hmac = QMessageAuthenticationCode::hash("data", "key", QCryptographicHash::Sha256).toHex(); + yaml.replace(pos, hmac.size(), hmac); + QCOMPARE(yaml.mid(pos + hmac.size(), 2).constData(), "'\n"); + + try { + ir2.deserialize(&buffer); + QVERIFY(false); + } catch (...) { + } +} + +QTEST_APPLESS_MAIN(tst_InstallationReport) + +#include "tst_installationreport.moc" diff --git a/tests/auto/main/CMakeLists.txt b/tests/auto/main/CMakeLists.txt new file mode 100644 index 00000000..05e6d2cd --- /dev/null +++ b/tests/auto/main/CMakeLists.txt @@ -0,0 +1,39 @@ + +qt_internal_add_test(tst_main + SOURCES + ../error-checking.h + tst_main.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManIntentServerPrivate + Qt::AppManMainPrivate + Qt::AppManManagerPrivate +) + +# Resources: +set(main_resource_files + "dummy.qml" +) + +qt_internal_add_resource(tst_main "main" + PREFIX + "/foo" + FILES + ${main_resource_files} +) + + +#### Keys ignored in scope 1:.:.:main.pro:<TRUE>: +# OTHER_FILES = "am-config.yaml" + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_main CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/main/am-config.yaml b/tests/auto/main/am-config.yaml new file mode 100644 index 00000000..ff538f1b --- /dev/null +++ b/tests/auto/main/am-config.yaml @@ -0,0 +1,10 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/builtin-apps" + installationDir: "/tmp/am-test-main/apps" + documentDir: "/tmp/am-test-main/docs" + +ui: + mainQml: "${CONFIG_PWD}/dummy.qml" diff --git a/tests/auto/main/builtin-apps/hello-world.red/icon.png b/tests/auto/main/builtin-apps/hello-world.red/icon.png Binary files differnew file mode 100644 index 00000000..04ca44dd --- /dev/null +++ b/tests/auto/main/builtin-apps/hello-world.red/icon.png diff --git a/tests/auto/main/builtin-apps/hello-world.red/info.yaml b/tests/auto/main/builtin-apps/hello-world.red/info.yaml new file mode 100644 index 00000000..fa52180e --- /dev/null +++ b/tests/auto/main/builtin-apps/hello-world.red/info.yaml @@ -0,0 +1,24 @@ +formatVersion: 1 +formatType: am-package +--- +id: 'hello-world.red' +icon: 'icon.png' +name: + en: 'Hello Red' + +applications: +- id: red1 + runtime: 'qml' + code: 'main.qml' + +- id: red2 + runtime: 'qml' + code: 'main2.qml' + +intents: +- id: red.intent1 + handlingApplicationId: red1 + categories: [ launcher, one ] +- id: red.intent2 + handlingApplicationId: red2 + categories: [ launcher, two ] diff --git a/tests/auto/main/builtin-apps/hello-world.red/main.qml b/tests/auto/main/builtin-apps/hello-world.red/main.qml new file mode 100644 index 00000000..b225bdfc --- /dev/null +++ b/tests/auto/main/builtin-apps/hello-world.red/main.qml @@ -0,0 +1,11 @@ +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + color: "red" + + Text { + anchors.centerIn: parent + text: "Hello World!" + } +} diff --git a/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/.installation-report.yaml b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/.installation-report.yaml new file mode 100644 index 00000000..ec57b3d7 --- /dev/null +++ b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/.installation-report.yaml @@ -0,0 +1,14 @@ +%YAML 1.1 +--- +formatType: 'am-installation-report' +formatVersion: 3 +--- +packageId: 'hello-world.red' +digest: '45ca0f9dfbddca129687900ae3260fe4a35ca09efd189581e196c0a75da47a0f' +diskSpaceUsed: 575488 +files: +- 'info.yaml' +- 'icon.png' +- 'main.qml' +--- +hmac: '48fce75b29a2b621f1a1462b434e6de0cac78837fe733bc12ab8e727363c5226' diff --git a/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/icon.png b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/icon.png Binary files differnew file mode 100644 index 00000000..04ca44dd --- /dev/null +++ b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/icon.png diff --git a/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/info.yaml b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/info.yaml new file mode 100644 index 00000000..af6a9f70 --- /dev/null +++ b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'hello-world.red' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'Hello Updated Red' diff --git a/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/main.qml b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/main.qml new file mode 100644 index 00000000..29b2e715 --- /dev/null +++ b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/main.qml @@ -0,0 +1,11 @@ +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + color: "crimson" + + Text { + anchors.centerIn: parent + text: "Hello Updated World!" + } +} diff --git a/tests/auto/main/dir-with-update-already-installed/docs/hello-world.red/placeholder b/tests/auto/main/dir-with-update-already-installed/docs/hello-world.red/placeholder new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/auto/main/dir-with-update-already-installed/docs/hello-world.red/placeholder diff --git a/tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/icon.png b/tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/icon.png Binary files differnew file mode 100644 index 00000000..04ca44dd --- /dev/null +++ b/tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/icon.png diff --git a/tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/info.yaml b/tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/info.yaml new file mode 100644 index 00000000..af6a9f70 --- /dev/null +++ b/tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'hello-world.red' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'Hello Updated Red' diff --git a/tests/auto/main/dummy.qml b/tests/auto/main/dummy.qml new file mode 100644 index 00000000..2d95c818 --- /dev/null +++ b/tests/auto/main/dummy.qml @@ -0,0 +1,3 @@ +import QtQml 2.0 + +QtObject { } diff --git a/tests/auto/main/main.pro b/tests/auto/main/main.pro new file mode 100644 index 00000000..e60ec75f --- /dev/null +++ b/tests/auto/main/main.pro @@ -0,0 +1,20 @@ +TARGET = tst_main + +include($$PWD/../tests.pri) + +# this test keeps crashing in a VirtualBox based CI environment, when executed +# via ssh (which gets the test run in Window's session 0 (no visible desktop) +luxoft-ci:CONFIG *= insignificant_test + +QT *= appman_manager-private \ + appman_application-private \ + appman_common-private \ + appman_main-private \ + appman_intent_server-private \ + +SOURCES += tst_main.cpp + +RESOURCES = main.qrc + +OTHER_FILES += am-config.yaml + diff --git a/tests/auto/main/main.qrc b/tests/auto/main/main.qrc new file mode 100644 index 00000000..8cae2bbb --- /dev/null +++ b/tests/auto/main/main.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/foo"> + <file>dummy.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/main/tst_main.cpp b/tests/auto/main/tst_main.cpp new file mode 100644 index 00000000..9d9f04a5 --- /dev/null +++ b/tests/auto/main/tst_main.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> +#include <QDir> +#include <QString> + +#include "packagemanager.h" +#include "package.h" +#include "applicationmanager.h" +#include "logging.h" +#include "main.h" +#include "intentserver.h" +#include "intent.h" +#include <QtAppManMain/defaultconfiguration.h> + + +QT_USE_NAMESPACE_AM + +class tst_Main : public QObject +{ + Q_OBJECT + +public: + tst_Main(); + ~tst_Main(); + +private slots: + void initTestCase(); + void init(); + void cleanup(); + void installAndRemoveUpdateForBuiltIn(); + void updateForBuiltInAlreadyInstalled(); + void loadDatabaseWithUpdatedBuiltInApp(); + void mainQmlFile_data(); + void mainQmlFile(); + +private: + void cleanUpInstallationDir(); + void installPackage(const QString &path); + void removePackage(const QString &id); + void initMain(); + void destroyMain(); + void copyRecursively(const QString &sourceDir, const QString &destDir); + int argc; + char **argv; + Main *main{nullptr}; + DefaultConfiguration *config{nullptr}; + bool m_verbose = false; +}; + +tst_Main::tst_Main() +{ + argc = 4; + argv = new char*[argc + 1]; + argv[0] = qstrdup("tst_Main"); + argv[1] = qstrdup("--dbus"); + argv[2] = qstrdup("none"); + argv[3] = qstrdup("--no-cache"); + argv[4] = nullptr; +} + +tst_Main::~tst_Main() +{ + for (int i = 0; i < argc; ++i) + delete []argv[i]; + delete []argv; +} + +void tst_Main::initTestCase() +{ + if (!QDir(qL1S(AM_TESTDATA_DIR "/packages")).exists()) + QSKIP("No test packages available in the data/ directory"); + + m_verbose = qEnvironmentVariableIsSet("AM_VERBOSE_TEST"); + qInfo() << "Verbose mode is" << (m_verbose ? "on" : "off") << "(change by (un)setting $AM_VERBOSE_TEST)"; +} + +void tst_Main::copyRecursively(const QString &sourcePath, const QString &destPath) +{ + QDir sourceDir(sourcePath); + QDir destDir(destPath); + + QStringList subdirNames = sourceDir.entryList(QDir::NoDotAndDotDot | QDir::AllDirs); + for (auto subdirName : subdirNames) { + destDir.mkdir(subdirName); + copyRecursively(sourceDir.filePath(subdirName), destDir.filePath(subdirName)); + } + + QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Hidden); + for (auto fileName : fileNames) + QFile::copy(sourceDir.filePath(fileName), destDir.filePath(fileName)); +} + +void tst_Main::cleanUpInstallationDir() +{ + { + QDir testTmpDir("/tmp/am-test-main"); + if (testTmpDir.exists()) + testTmpDir.removeRecursively(); + } + QDir tmpDir("/tmp"); + tmpDir.mkdir("am-test-main"); +} + +void tst_Main::init() +{ + cleanUpInstallationDir(); +} + +void tst_Main::initMain() +{ + main = new Main(argc, argv); + + QString amConfigPath = QFINDTESTDATA("am-config.yaml"); + auto pathList = QStringList(amConfigPath); + + config = new DefaultConfiguration(pathList, QString()); + config->parseWithArguments(QCoreApplication::arguments()); + if (m_verbose) + config->setForceVerbose(true); + + main->setup(config); + + PackageManager::instance()->setAllowInstallationOfUnsignedPackages(true); +} + +void tst_Main::destroyMain() +{ + if (main) { + main->shutDown(); + main->exec(); + delete main; + main = nullptr; + } + if (config) { + delete config; + config = nullptr; + } +} + +void tst_Main::cleanup() +{ + destroyMain(); + + delete config; + config = nullptr; +} + +void tst_Main::installPackage(const QString &pkgPath) +{ + auto packageManager = PackageManager::instance(); + + bool installationFinished = false; + + connect(packageManager, &PackageManager::taskRequestingInstallationAcknowledge, + this, [packageManager](const QString &taskId, Package *, + const QVariantMap &, const QVariantMap &) { + packageManager->acknowledgePackageInstallation(taskId); + }); + + connect(packageManager, &PackageManager::taskFinished, + this, [&installationFinished](const QString &) { + installationFinished = true; + }); + + packageManager->startPackageInstallation(QUrl::fromLocalFile(pkgPath)); + + QTRY_VERIFY(installationFinished); +} + +void tst_Main::removePackage(const QString &id) +{ + auto packageManager = PackageManager::instance(); + + bool removalFinished = false; + + connect(packageManager, &PackageManager::taskFinished, + this, [this, &removalFinished](const QString &) { + Q_UNUSED(this); // gcc 9.2 bug: without capturing 'this', broken code will be generated + removalFinished = true; + }); + + packageManager->removePackage(id, false /* keepDocuments */); + + QTRY_VERIFY(removalFinished); +} + +/* + Install an application with the same id of an existing, builtin, one. + Then remove it. + At all stages ApplicationManager should contain the same Application + object. All that changes is the contents of that Application: + from original, to updated, and then back to original. + */ +void tst_Main::installAndRemoveUpdateForBuiltIn() +{ + initMain(); + + auto appMan = ApplicationManager::instance(); + QCOMPARE(appMan->count(), 2); + auto intents = IntentServer::instance(); + QCOMPARE(intents->count(), 2); + + auto app1 = appMan->application(0); + QCOMPARE(app1->name(qSL("en")), qSL("Hello Red")); + QCOMPARE(app1->id(), qSL("red1")); + auto app2 = appMan->application(1); + QCOMPARE(app2->name(qSL("en")), qSL("Hello Red")); + QCOMPARE(app2->id(), qSL("red2")); + + auto intent1 = intents->applicationIntent(qSL("red.intent1"), qSL("red1")); + QVERIFY(intent1); + QCOMPARE(intent1->intentId(), qSL("red.intent1")); + QCOMPARE(intent1->applicationId(), qSL("red1")); + QCOMPARE(intent1->packageId(), qSL("hello-world.red")); + QVERIFY(intent1->categories().contains(qSL("one"))); + QVERIFY(intent1->categories().contains(qSL("launcher"))); + + auto intent2 = intents->applicationIntent(qSL("red.intent2"), qSL("red2")); + QVERIFY(intent2); + QCOMPARE(intent2->intentId(), qSL("red.intent2")); + QCOMPARE(intent2->applicationId(), qSL("red2")); + QCOMPARE(intent2->packageId(), qSL("hello-world.red")); + QVERIFY(intent2->categories().contains(qSL("two"))); + QVERIFY(intent2->categories().contains(qSL("launcher"))); + + installPackage(qL1S(AM_TESTDATA_DIR "packages/hello-world.red.appkg")); + + QCOMPARE(appMan->count(), 1); + QCOMPARE(intents->count(), 0); + + // it must still be a different Application instance as before the installation, but quite + // often we get the same pointer back because it's a delete/new back-to-back + app1 = appMan->application(0); + + // but with different contents + QCOMPARE(app1->name(qSL("en")), qSL("Hello Updated Red")); + intent1 = intents->applicationIntent(qSL("red.intent1"), qSL("red1")); + QVERIFY(!intent1); + + removePackage(qSL("hello-world.red")); + + // After removal of the updated version all data in Application should be as before the + // installation took place. + QCOMPARE(appMan->count(), 2); + QCOMPARE(intents->count(), 2); + + app1 = appMan->application(0); + QCOMPARE(app1->name(qSL("en")), qSL("Hello Red")); + intent1 = intents->applicationIntent(qSL("red.intent1"), qSL("red1")); + QVERIFY(intent1); + QCOMPARE(intent1->intentId(), qSL("red.intent1")); + intent2 = intents->applicationIntent(qSL("red.intent2"), qSL("red2")); + QVERIFY(intent2); + QCOMPARE(intent2->intentId(), qSL("red.intent2")); +} + +/* + Situation: there's already an update installed for a built-in application and there's no database + file yet (or a database rebuild has been requested, in which case any existing database file is + ignored). + + Check that ApplicationManager ends up with only one ApplicationObject as both entries + (the built-in and the installed one) share the same applicaion id. And check also that + this ApplicationObject is showing data from the installed update. + */ +void tst_Main::updateForBuiltInAlreadyInstalled() +{ + copyRecursively(QFINDTESTDATA("dir-with-update-already-installed"), "/tmp/am-test-main"); + + initMain(); + + auto appMan = ApplicationManager::instance(); + QCOMPARE(appMan->count(), 1); + + auto app = appMan->application(0); + QCOMPARE(app->name(qSL("en")), qSL("Hello Updated Red")); +} + +/* + Install an update for a built-in app and quit Main. A database will be generated. + + Then, on next iteration of Main, Applications will be created from that database. + Check that ApplicationManager shows only one, updated, built-in app. + */ +void tst_Main::loadDatabaseWithUpdatedBuiltInApp() +{ + initMain(); + installPackage(qL1S(AM_TESTDATA_DIR "packages/hello-world.red.appkg")); + destroyMain(); + + initMain(); + + auto appMan = ApplicationManager::instance(); + QCOMPARE(appMan->count(), 1); + + auto app = appMan->application(0); + QCOMPARE(app->name(qSL("en")), qSL("Hello Updated Red")); +} + +void tst_Main::mainQmlFile_data() +{ + QTest::addColumn<QString>("mainQml"); + QTest::addColumn<QString>("expectedErrorMsg"); + + QTest::newRow("none") << "" << "No main QML file specified"; + + QTest::newRow("invalid") << "foo/bar.qml" << "Invalid main QML file specified: foo/bar.qml"; + QTest::newRow("invalid-dir") << "." << "Invalid main QML file specified: ."; + QTest::newRow("invalid-qrc1") << ":/bar.qml" << "Invalid main QML file specified: :/bar.qml"; + // "://bar.qml" yields an absolute file path of ":" and QFile::exists() would always return true + QTest::newRow("invalid-qrc2") << "://bar.qml" << "Invalid main QML file specified: ://bar.qml"; + QTest::newRow("invalid-qrc-dir") << ":/foo" << "Invalid main QML file specified: :/foo"; + + QTest::newRow("valid-native") << QFINDTESTDATA("dummy.qml") << ""; + QTest::newRow("valid-qrc-file") << ":/foo/dummy.qml" << ""; + QTest::newRow("valid-qrc-url") << "qrc:///foo/dummy.qml" << ""; + + // Passes unchecked: + QTest::newRow("https") << "https://www.qt.io/foo/bar.qml" << ""; + QTest::newRow("assets") << "assets:///foo/bar.qml" << ""; +} + +void tst_Main::mainQmlFile() +{ + QFETCH(QString, mainQml); + QFETCH(QString, expectedErrorMsg); + + QStringList arguments; + arguments << "tst_Main"; + arguments << "--dbus"; + arguments << "none"; + arguments << mainQml; + + main = new Main(argc, argv); + + config = new DefaultConfiguration(QStringList(QFINDTESTDATA("am-config.yaml")), QString()); + config->parseWithArguments(arguments); + + try { + main->setup(config); + QVERIFY2(expectedErrorMsg.isEmpty(), "Exception was expected, but none was thrown"); + } catch (const std::exception &e) { + QCOMPARE(e.what(), expectedErrorMsg); + } + + delete config; + config = nullptr; + delete main; + main = nullptr; +} + +QTEST_APPLESS_MAIN(tst_Main) + +#include "tst_main.moc" diff --git a/tests/auto/packagecreator/CMakeLists.txt b/tests/auto/packagecreator/CMakeLists.txt new file mode 100644 index 00000000..a9cf00d8 --- /dev/null +++ b/tests/auto/packagecreator/CMakeLists.txt @@ -0,0 +1,21 @@ + +qt_internal_add_test(tst_packagecreator + SOURCES + ../error-checking.h + tst_packagecreator.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManPackagePrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_packagecreator CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/packagecreator/packagecreator.pro b/tests/auto/packagecreator/packagecreator.pro new file mode 100644 index 00000000..269c12c7 --- /dev/null +++ b/tests/auto/packagecreator/packagecreator.pro @@ -0,0 +1,10 @@ +TARGET = tst_packagecreator + +include($$PWD/../tests.pri) + +QT *= \ + appman_common-private \ + appman_application-private \ + appman_package-private + +SOURCES += tst_packagecreator.cpp diff --git a/tests/auto/packagecreator/tst_packagecreator.cpp b/tests/auto/packagecreator/tst_packagecreator.cpp new file mode 100644 index 00000000..1cd237b6 --- /dev/null +++ b/tests/auto/packagecreator/tst_packagecreator.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtTest> + +#include "global.h" +#include "installationreport.h" +#include "packageutilities.h" +#include "packagecreator.h" +#include "utilities.h" + +#include "../error-checking.h" + +QT_USE_NAMESPACE_AM + +static int processTimeout = 3000; + +class tst_PackageCreator : public QObject +{ + Q_OBJECT + +public: + tst_PackageCreator(); + +private slots: + void initTestCase(); + + void createAndVerify_data(); + void createAndVerify(); + +private: + QString escapeFilename(const QString &name); + +private: + QDir m_baseDir; + bool m_tarAvailable = false; + bool m_isCygwin = false; +}; + +tst_PackageCreator::tst_PackageCreator() + : m_baseDir(qSL(AM_TESTDATA_DIR)) +{ } + +void tst_PackageCreator::initTestCase() +{ + processTimeout *= timeoutFactor(); + + // check if tar command is available at all + QProcess tar; + tar.start(qSL("tar"), { qSL("--version") }); + m_tarAvailable = tar.waitForStarted(processTimeout) + && tar.waitForFinished(processTimeout) + && (tar.exitStatus() == QProcess::NormalExit); + + m_isCygwin = tar.readAllStandardOutput().contains("Cygwin"); + + QVERIFY(PackageUtilities::checkCorrectLocale()); + + if (!QDir(qL1S(AM_TESTDATA_DIR "/packages")).exists()) + QSKIP("No test packages available in the data/ directory"); +} + +void tst_PackageCreator::createAndVerify_data() +{ + QTest::addColumn<QStringList>("files"); + QTest::addColumn<bool>("expectedSuccess"); + QTest::addColumn<QString>("errorString"); + + QTest::newRow("basic") << QStringList { qSL("testfile") } << true << QString(); + QTest::newRow("no-such-file") << QStringList { qSL("tastfile") } << false << qSL("~file not found: .*"); +} + +void tst_PackageCreator::createAndVerify() +{ + QFETCH(QStringList, files); + QFETCH(bool, expectedSuccess); + QFETCH(QString, errorString); + + QTemporaryFile output; + QVERIFY(output.open()); + + InstallationReport report(qSL("com.pelagicore.test")); + report.addFiles(files); + + PackageCreator creator(m_baseDir, &output, report); + bool result = creator.create(); + output.close(); + + if (expectedSuccess) { + QVERIFY2(result, qPrintable(creator.errorString())); + } else { + QVERIFY(creator.errorCode() != Error::None); + QVERIFY(creator.errorCode() != Error::Canceled); + QVERIFY(!creator.wasCanceled()); + + AM_CHECK_ERRORSTRING(creator.errorString(), errorString); + return; + } + + // check the tar listing + if (!m_tarAvailable) + QSKIP("No tar command found in PATH - skipping the verification part of the test!"); + + QProcess tar; + tar.start(qSL("tar"), { qSL("-tzf"), escapeFilename(output.fileName()) }); + QVERIFY2(tar.waitForStarted(processTimeout) && + tar.waitForFinished(processTimeout) && + (tar.exitStatus() == QProcess::NormalExit) && + (tar.exitCode() == 0), qPrintable(tar.errorString())); + + QStringList expectedContents = files; + expectedContents.sort(); + expectedContents.prepend(qSL("--PACKAGE-HEADER--")); + expectedContents.append(qSL("--PACKAGE-FOOTER--")); + + QStringList actualContents = QString::fromLocal8Bit(tar.readAllStandardOutput()).split(qL1C('\n'), Qt::SkipEmptyParts); +#if defined(Q_OS_WIN) + actualContents.replaceInStrings(qSL("\r"), QString()); +#endif + QCOMPARE(actualContents, expectedContents); + + // check the contents of the files + + for (const QString &file : qAsConst(files)) { + QFile src(m_baseDir.absoluteFilePath(file)); + QVERIFY2(src.open(QFile::ReadOnly), qPrintable(src.errorString())); + QByteArray data = src.readAll(); + + tar.start(qSL("tar"), { qSL("-xzOf"), escapeFilename(output.fileName()), file }); + QVERIFY2(tar.waitForStarted(processTimeout) && + tar.waitForFinished(processTimeout) && + (tar.exitStatus() == QProcess::NormalExit) && + (tar.exitCode() == 0), qPrintable(tar.errorString())); + + QCOMPARE(tar.readAllStandardOutput(), data); + } +} + +QString tst_PackageCreator::escapeFilename(const QString &name) +{ + if (!m_isCygwin) { + return name; + } else { + QString s = QFileInfo(name).absoluteFilePath(); + QString t = qSL("/cygdrive/"); + t.append(s.at(0)); + return t + s.mid(2); + } +} + +int main(int argc, char *argv[]) +{ + PackageUtilities::ensureCorrectLocale(); + QCoreApplication app(argc, argv); + app.setAttribute(Qt::AA_Use96Dpi, true); + tst_PackageCreator tc; + QTEST_SET_MAIN_SOURCE_PATH + return QTest::qExec(&tc, argc, argv); +} + +#include "tst_packagecreator.moc" diff --git a/tests/auto/packageextractor/CMakeLists.txt b/tests/auto/packageextractor/CMakeLists.txt new file mode 100644 index 00000000..9775d7a3 --- /dev/null +++ b/tests/auto/packageextractor/CMakeLists.txt @@ -0,0 +1,21 @@ + +qt_internal_add_test(tst_packageextractor + SOURCES + ../error-checking.h + tst_packageextractor.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManPackagePrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_packageextractor CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/packageextractor/packageextractor.pro b/tests/auto/packageextractor/packageextractor.pro new file mode 100644 index 00000000..761720cf --- /dev/null +++ b/tests/auto/packageextractor/packageextractor.pro @@ -0,0 +1,10 @@ +TARGET = tst_packageextractor + +include($$PWD/../tests.pri) + +QT *= \ + appman_common-private \ + appman_application-private \ + appman_package-private + +SOURCES += tst_packageextractor.cpp diff --git a/tests/auto/packageextractor/tst_packageextractor.cpp b/tests/auto/packageextractor/tst_packageextractor.cpp new file mode 100644 index 00000000..50dd78a6 --- /dev/null +++ b/tests/auto/packageextractor/tst_packageextractor.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtTest> +#include <QtNetwork> +#include <QTemporaryDir> +#include <qplatformdefs.h> + +#if defined(Q_OS_UNIX) +# include <unistd.h> +# include <fcntl.h> +# include <errno.h> +#endif + +#include "global.h" +#include "packageextractor.h" +#include "installationreport.h" +#include "packageutilities.h" + +#include "../error-checking.h" + +QT_USE_NAMESPACE_AM + +class tst_PackageExtractor : public QObject +{ + Q_OBJECT + +public: + tst_PackageExtractor(); + +private slots: + void initTestCase(); + void init(); + void cleanup(); + + void extractAndVerify_data(); + void extractAndVerify(); + + void cancelExtraction(); + + void extractFromFifo(); + +private: + QString m_taest; + QScopedPointer<QTemporaryDir> m_extractDir; +}; + +tst_PackageExtractor::tst_PackageExtractor() + : m_taest(QString::fromUtf8("t\xc3\xa4st")) +{ } + +void tst_PackageExtractor::initTestCase() +{ + if (!QDir(qL1S(AM_TESTDATA_DIR "/packages")).exists()) + QSKIP("No test packages available in the data/ directory"); + + QVERIFY(PackageUtilities::checkCorrectLocale()); +} + +void tst_PackageExtractor::init() +{ + m_extractDir.reset(new QTemporaryDir()); + QVERIFY(m_extractDir->isValid()); +} + +void tst_PackageExtractor::cleanup() +{ + m_extractDir.reset(); +} + +void tst_PackageExtractor::extractAndVerify_data() +{ + QTest::addColumn<QString>("path"); + QTest::addColumn<bool>("expectedSuccess"); + QTest::addColumn<QString>("errorString"); + QTest::addColumn<QStringList>("entries"); + QTest::addColumn<QMap<QString, QByteArray>>("content"); + QTest::addColumn<QMap<QString, qint64>>("sizes"); + + QStringList noEntries; + QMap<QString, QByteArray> noContent; + QMap<QString, qint64> noSizes; + + QTest::newRow("normal") << "packages/test.appkg" + << true << QString() + << QStringList { + "info.yaml", + "icon.png", + "test", + m_taest } + << QMap<QString, QByteArray> { + { "test", "test\n" }, + { m_taest, "test with umlaut\n" } } + << noSizes; + + QTest::newRow("big") << "packages/bigtest.appkg" + << true << QString() + << QStringList { + "info.yaml", + "icon.png", + "test", + m_taest, + "bigtest" } + << QMap<QString, QByteArray> { + { "test", "test\n" }, + { m_taest, "test with umlaut\n" } } + << QMap<QString, qint64> { + // { "info.yaml", 213 }, // this is different on Windows: \n vs. \r\n + { "icon.png", 1157 }, + { "bigtest", 5*1024*1024 }, + { "test", 5 }, + { m_taest, 17 } }; + + QTest::newRow("invalid-url") << "packages/no-such-file.appkg" + << false << "~Error opening .*: (No such file or directory|The system cannot find the file specified\\.)" + << noEntries << noContent << noSizes; + QTest::newRow("invalid-format") << "packages/test-invalid-format.appkg" + << false << "~.* could not open archive: Unrecognized archive format" + << noEntries << noContent << noSizes; + QTest::newRow("invalid-digest") << "packages/test-invalid-footer-digest.appkg" + << false << "~package digest mismatch.*" + << noEntries << noContent << noSizes; + QTest::newRow("invalid-path") << "packages/test-invalid-path.appkg" + << false << "~invalid archive entry .*: pointing outside of extraction directory" + << noEntries << noContent << noSizes; +} + +void tst_PackageExtractor::extractAndVerify() +{ + // macros are stupid... + typedef QMap<QString, QByteArray> ByteArrayMap; + typedef QMap<QString, qint64> IntMap; + + QFETCH(QString, path); + QFETCH(bool, expectedSuccess); + QFETCH(QString, errorString); + QFETCH(QStringList, entries); + QFETCH(ByteArrayMap, content); + QFETCH(IntMap, sizes); + + PackageExtractor extractor(QUrl::fromLocalFile(AM_TESTDATA_DIR + path), m_extractDir->path()); + bool result = extractor.extract(); + + if (expectedSuccess) { + QVERIFY2(result, qPrintable(extractor.errorString())); + } else { + QVERIFY(extractor.errorCode() != Error::None); + QVERIFY(extractor.errorCode() != Error::Canceled); + QVERIFY(!extractor.wasCanceled()); + + AM_CHECK_ERRORSTRING(extractor.errorString(), errorString); + return; + } + + QStringList checkEntries(entries); + QDirIterator it(m_extractDir->path(), QDir::NoDotAndDotDot | QDir::AllEntries, QDirIterator::Subdirectories); + while (it.hasNext()) { + QString entry = it.next(); + entry = entry.mid(m_extractDir->path().size() + 1); + + QVERIFY2(checkEntries.contains(entry), qPrintable(entry)); + + if (content.contains(entry)) { + QVERIFY(QDir(m_extractDir->path()).exists(entry)); + QFile f(QDir(m_extractDir->path()).absoluteFilePath(entry)); + QVERIFY(f.open(QFile::ReadOnly)); + QCOMPARE(f.readAll(), content.value(entry)); + } + + if (sizes.contains(entry)) { + QVERIFY(QDir(m_extractDir->path()).exists(entry)); + QFile f(QDir(m_extractDir->path()).absoluteFilePath(entry)); + QCOMPARE(f.size(), sizes.value(entry)); + } + + QVERIFY(checkEntries.removeOne(entry)); + } + + QVERIFY2(checkEntries.isEmpty(), qPrintable(checkEntries.join(qL1C(' ')))); + + QStringList reportEntries = extractor.installationReport().files(); + reportEntries.sort(); + entries.sort(); + QCOMPARE(reportEntries, entries); +} + +void tst_PackageExtractor::cancelExtraction() +{ + { + PackageExtractor extractor(QUrl::fromLocalFile(qL1S(AM_TESTDATA_DIR "packages/test.appkg")), m_extractDir->path()); + extractor.cancel(); + QVERIFY(!extractor.extract()); + QVERIFY(extractor.wasCanceled()); + QVERIFY(extractor.errorCode() == Error::Canceled); + QVERIFY(extractor.hasFailed()); + } + { + PackageExtractor extractor(QUrl::fromLocalFile(qL1S(AM_TESTDATA_DIR "packages/test.appkg")), m_extractDir->path()); + connect(&extractor, &PackageExtractor::progress, this, [&extractor](qreal p) { + if (p >= 0.1) + extractor.cancel(); + }); + QVERIFY(!extractor.extract()); + QVERIFY(extractor.wasCanceled()); + QVERIFY(extractor.errorCode() == Error::Canceled); + QVERIFY(extractor.hasFailed()); + } +} + +class FifoSource : public QThread // clazy:exclude=missing-qobject-macro +{ +public: + FifoSource(const QString &file) + : m_file(file) + { + m_fifoPath = QDir::temp().absoluteFilePath(qSL("autotext-package-extractor-%1.fifo")) + .arg(QCoreApplication::applicationPid()) + .toLocal8Bit(); +#ifdef Q_OS_UNIX + QVERIFY2(m_file.open(QFile::ReadOnly), qPrintable(m_file.errorString())); + QVERIFY2(::mkfifo(m_fifoPath, 0600) == 0, ::strerror(errno)); +#endif + } + + ~FifoSource() + { +#ifdef Q_OS_UNIX + ::unlink(m_fifoPath); +#endif + } + + QString path() const + { + return QString::fromLocal8Bit(m_fifoPath); + } + + void run() override + { +#ifdef Q_OS_UNIX + int fifoFd = QT_OPEN(m_fifoPath, O_WRONLY); + QVERIFY2(fifoFd >= 0, ::strerror(errno)); + + QByteArray buffer; + buffer.resize(1024 * 1024); + + while (!m_file.atEnd()) { + qint64 bytesRead = m_file.read(buffer.data(), buffer.size()); + QVERIFY(bytesRead >= 0); + qint64 bytesWritten = QT_WRITE(fifoFd, buffer.constData(), bytesRead); + QCOMPARE(bytesRead, bytesWritten); + } + QT_CLOSE(fifoFd); +#endif + } + +private: + QFile m_file; + QByteArray m_fifoPath; +}; + +void tst_PackageExtractor::extractFromFifo() +{ +#if !defined(Q_OS_UNIX) + QSKIP("No FIFO support on this platform"); +#endif + + FifoSource fifo(qL1S(AM_TESTDATA_DIR "packages/test.appkg")); + fifo.start(); + + PackageExtractor extractor(QUrl::fromLocalFile(fifo.path()), m_extractDir->path()); + QVERIFY2(extractor.extract(), qPrintable(extractor.errorString())); + QTRY_VERIFY(fifo.isFinished()); +} + +int main(int argc, char *argv[]) +{ + PackageUtilities::ensureCorrectLocale(); + QCoreApplication app(argc, argv); + app.setAttribute(Qt::AA_Use96Dpi, true); + tst_PackageExtractor tc; + QTEST_SET_MAIN_SOURCE_PATH + return QTest::qExec(&tc, argc, argv); +} + +#include "tst_packageextractor.moc" diff --git a/tests/auto/packager-tool/CMakeLists.txt b/tests/auto/packager-tool/CMakeLists.txt new file mode 100644 index 00000000..8521f7f1 --- /dev/null +++ b/tests/auto/packager-tool/CMakeLists.txt @@ -0,0 +1,23 @@ + +qt_internal_add_test(tst_packager-tool + SOURCES + ../../../src/tools/packager/packagingjob.cpp + ../error-checking.h + tst_packager-tool.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + INCLUDE_DIRECTORIES + ../../../src/tools/packager + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManCryptoPrivate + Qt::AppManManagerPrivate + Qt::AppManPackagePrivate +) + +qt_internal_extend_target(tst_packager-tool CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/packager-tool/packager-tool.pro b/tests/auto/packager-tool/packager-tool.pro new file mode 100644 index 00000000..08876ac6 --- /dev/null +++ b/tests/auto/packager-tool/packager-tool.pro @@ -0,0 +1,15 @@ +TARGET = tst_packager-tool + +include($$PWD/../tests.pri) + +QT *= \ + appman_common-private \ + appman_crypto-private \ + appman_application-private \ + appman_package-private \ + appman_manager-private \ + +INCLUDEPATH += $$PWD/../../src/tools/packager +SOURCES += $$PWD/../../src/tools/packager/packagingjob.cpp + +SOURCES += tst_packager-tool.cpp diff --git a/tests/auto/packager-tool/tst_packager-tool.cpp b/tests/auto/packager-tool/tst_packager-tool.cpp new file mode 100644 index 00000000..7826caee --- /dev/null +++ b/tests/auto/packager-tool/tst_packager-tool.cpp @@ -0,0 +1,414 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtTest> +#include <QCoreApplication> + +#include "global.h" +#include "applicationmanager.h" +#include "application.h" +#include "qtyaml.h" +#include "exception.h" +#include "packagedatabase.h" +#include "packagemanager.h" +#include "packagingjob.h" +#include "qmlinprocessruntime.h" +#include "runtimefactory.h" +#include "utilities.h" + +#include "../error-checking.h" + +QT_USE_NAMESPACE_AM + +static int spyTimeout = 5000; // shorthand for specifying QSignalSpy timeouts + +class tst_PackagerTool : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanup(); + + void test(); + void brokenMetadata_data(); + void brokenMetadata(); + void iconFileName(); + +private: + QString pathTo(const char *file) + { + return QDir(m_workDir.path()).absoluteFilePath(QLatin1String(file)); + } + + bool createInfoYaml(QTemporaryDir &tmp, const QString &changeField = QString(), const QVariant &toValue = QVariant()); + bool createIconPng(QTemporaryDir &tmp); + bool createCode(QTemporaryDir &tmp); + void createDummyFile(QTemporaryDir &tmp, const QString &fileName, const char *data); + + void installPackage(const QString &filePath); + + PackageManager *m_pm = nullptr; + QTemporaryDir m_workDir; + + QString m_devPassword; + QString m_devCertificate; + QString m_storePassword; + QString m_storeCertificate; + QStringList m_caFiles; + QString m_hardwareId; +}; + +void tst_PackagerTool::initTestCase() +{ + if (!QDir(qL1S(AM_TESTDATA_DIR "/packages")).exists()) + QSKIP("No test packages available in the data/ directory"); + + spyTimeout *= timeoutFactor(); + + QVERIFY(m_workDir.isValid()); + QVERIFY(QDir::root().mkpath(pathTo("internal-0"))); + QVERIFY(QDir::root().mkpath(pathTo("documents-0"))); + + m_hardwareId = qSL("foobar"); + + PackageDatabase *pdb = new PackageDatabase({}, pathTo("internal-0")); + try { + m_pm = PackageManager::createInstance(pdb, pathTo("documents-0")); + m_pm->setHardwareId(m_hardwareId); + m_pm->enableInstaller(); + } catch (const Exception &e) { + QVERIFY2(false, e.what()); + } + + QVERIFY(ApplicationManager::createInstance(true)); + + + // crypto stuff - we need to load the root CA and developer CA certificates + + QFile devcaFile(qL1S(AM_TESTDATA_DIR "certificates/devca.crt")); + QFile caFile(qL1S(AM_TESTDATA_DIR "certificates/ca.crt")); + QVERIFY2(devcaFile.open(QIODevice::ReadOnly), qPrintable(devcaFile.errorString())); + QVERIFY2(caFile.open(QIODevice::ReadOnly), qPrintable(devcaFile.errorString())); + + QList<QByteArray> chainOfTrust; + chainOfTrust << devcaFile.readAll() << caFile.readAll(); + QVERIFY(!chainOfTrust.at(0).isEmpty()); + QVERIFY(!chainOfTrust.at(1).isEmpty()); + m_pm->setCACertificates(chainOfTrust); + + m_caFiles << devcaFile.fileName() << caFile.fileName(); + + m_devPassword = qSL("password"); + m_devCertificate = qL1S(AM_TESTDATA_DIR "certificates/dev1.p12"); + m_storePassword = qSL("password"); + m_storeCertificate = qL1S(AM_TESTDATA_DIR "certificates/store.p12"); + + RuntimeFactory::instance()->registerRuntime(new QmlInProcessRuntimeManager(qSL("qml"))); +} + +void tst_PackagerTool::cleanup() +{ + recursiveOperation(pathTo("internal-0"), safeRemove); + recursiveOperation(pathTo("documents-0"), safeRemove); + + QDir dir(m_workDir.path()); + QStringList fileNames = dir.entryList(QDir::Files); + for (auto fileName : fileNames) + dir.remove(fileName); +} + +// exceptions are nice -- just not for unit testing :) +static bool packagerCheck(PackagingJob *p, QString &errorString) +{ + bool result = false; + try { + p->execute(); + errorString.clear(); + result = (p->resultCode() == 0); + if (!result) + errorString = p->output(); + } catch (const Exception &e) { \ + errorString = e.errorString(); + } + delete p; + return result; +} + +void tst_PackagerTool::test() +{ + QTemporaryDir tmp; + QString errorString; + + // no valid destination + QVERIFY(!packagerCheck(PackagingJob::create(pathTo("test.appkg"), pathTo("test.appkg")), errorString)); + QVERIFY2(errorString.contains(qL1S("is not a directory")), qPrintable(errorString)); + + // no valid info.yaml + QVERIFY(!packagerCheck(PackagingJob::create(pathTo("test.appkg"), tmp.path()), errorString)); + QVERIFY2(errorString.contains(qL1S("Cannot open for reading")), qPrintable(errorString)); + + // add an info.yaml file + createInfoYaml(tmp); + + // no icon + QVERIFY(!packagerCheck(PackagingJob::create(pathTo("test.appkg"), tmp.path()), errorString)); + QVERIFY2(errorString.contains(qL1S("missing the file referenced by the 'icon' field")), qPrintable(errorString)); + + // add an icon + createIconPng(tmp); + + // no valid code + QVERIFY(!packagerCheck(PackagingJob::create(pathTo("test.appkg"), tmp.path()), errorString)); + QVERIFY2(errorString.contains(qL1S("missing the file referenced by the 'code' field")), qPrintable(errorString)); + + // add a code file + createCode(tmp); + + // invalid destination + QVERIFY(!packagerCheck(PackagingJob::create(tmp.path(), tmp.path()), errorString)); + QVERIFY2(errorString.contains(qL1S("could not create package file")), qPrintable(errorString)); + + // now everything is correct - try again + QVERIFY2(packagerCheck(PackagingJob::create(pathTo("test.appkg"), tmp.path()), errorString), qPrintable(errorString)); + + // invalid source package + QVERIFY(!packagerCheck(PackagingJob::developerSign( + pathTo("no-such-file"), + pathTo("test.dev-signed.appkg"), + m_devCertificate, + m_devPassword), errorString)); + QVERIFY2(errorString.contains(qL1S("does not exist")), qPrintable(errorString)); + + // invalid destination package + QVERIFY(!packagerCheck(PackagingJob::developerSign( + pathTo("test.appkg"), + pathTo("."), + m_devCertificate, + m_devPassword), errorString)); + QVERIFY2(errorString.contains(qL1S("could not create package file")), qPrintable(errorString)); + + + // invalid dev key + QVERIFY(!packagerCheck(PackagingJob::developerSign( + pathTo("test.appkg"), + pathTo("test.dev-signed.appkg"), + m_devCertificate, + qSL("wrong-password")), errorString)); + QVERIFY2(errorString.contains(qL1S("could not create signature")), qPrintable(errorString)); + + // invalid store key + QVERIFY(!packagerCheck(PackagingJob::storeSign( + pathTo("test.appkg"), + pathTo("test.store-signed.appkg"), + m_storeCertificate, + qSL("wrong-password"), + m_hardwareId), errorString)); + QVERIFY2(errorString.contains(qL1S("could not create signature")), qPrintable(errorString)); + + // sign + QVERIFY2(packagerCheck(PackagingJob::developerSign( + pathTo("test.appkg"), + pathTo("test.dev-signed.appkg"), + m_devCertificate, + m_devPassword), errorString), qPrintable(errorString)); + + QVERIFY2(packagerCheck(PackagingJob::storeSign( + pathTo("test.appkg"), + pathTo("test.store-signed.appkg"), + m_storeCertificate, + m_storePassword, + m_hardwareId), errorString), qPrintable(errorString)); + + // verify + QVERIFY2(packagerCheck(PackagingJob::developerVerify( + pathTo("test.dev-signed.appkg"), + m_caFiles), errorString), qPrintable(errorString)); + + QVERIFY2(packagerCheck(PackagingJob::storeVerify( + pathTo("test.store-signed.appkg"), + m_caFiles, + m_hardwareId), errorString), qPrintable(errorString)); + + // now that we have it, see if the package actually installs correctly + + installPackage(pathTo("test.dev-signed.appkg")); + + QDir checkDir(pathTo("internal-0")); + QVERIFY(checkDir.cd(qSL("com.pelagicore.test"))); + + for (const QString &file : { qSL("info.yaml"), qSL("icon.png"), qSL("test.qml") }) { + QVERIFY(checkDir.exists(file)); + QFile src(QDir(tmp.path()).absoluteFilePath(file)); + QVERIFY(src.open(QFile::ReadOnly)); + QFile dst(checkDir.absoluteFilePath(file)); + QVERIFY(dst.open(QFile::ReadOnly)); + QCOMPARE(src.readAll(), dst.readAll()); + } +} + +void tst_PackagerTool::brokenMetadata_data() +{ + QTest::addColumn<QString>("yamlField"); + QTest::addColumn<QVariant>("yamlValue"); + QTest::addColumn<QString>("errorString"); + + QTest::newRow("missing-name") << qSL("name") << QVariant() << "~.*Required fields are missing: name.*"; + QTest::newRow("missing-runtime") << qSL("runtime") << QVariant() << "~.*Required fields are missing: runtime"; + QTest::newRow("missing-identifier") << qSL("id") << QVariant() << "~.*Required fields are missing: id"; + QTest::newRow("missing-code") << qSL("code") << QVariant() << "~.*Required fields are missing: code"; +} + +void tst_PackagerTool::brokenMetadata() +{ + QFETCH(QString, yamlField); + QFETCH(QVariant, yamlValue); + QFETCH(QString, errorString); + + QTemporaryDir tmp; + + createCode(tmp); + createIconPng(tmp); + createInfoYaml(tmp, yamlField, yamlValue); + + // check if packaging actually fails with the expected error + + QString error; + QVERIFY2(!packagerCheck(PackagingJob::create(pathTo("test.appkg"), tmp.path()), error), qPrintable(error)); + AM_CHECK_ERRORSTRING(error, errorString); +} + +/* + Specify an icon whose name is different from "icon.png". + Packaging should work fine + */ +void tst_PackagerTool::iconFileName() +{ + QTemporaryDir tmp; + QString errorString; + + createInfoYaml(tmp, qSL("icon"), qSL("foo.bar")); + createCode(tmp); + createDummyFile(tmp, qSL("foo.bar"), "this-is-a-dummy-icon-file"); + + QVERIFY2(packagerCheck(PackagingJob::create(pathTo("test-foobar-icon.appkg"), tmp.path()), errorString), + qPrintable(errorString)); + + // see if the package installs correctly + + m_pm->setAllowInstallationOfUnsignedPackages(true); + installPackage(pathTo("test-foobar-icon.appkg")); + m_pm->setAllowInstallationOfUnsignedPackages(false); + + QDir checkDir(pathTo("internal-0")); + QVERIFY(checkDir.cd(qSL("com.pelagicore.test"))); + + for (const QString &file : { qSL("info.yaml"), qSL("foo.bar"), qSL("test.qml") }) { + QVERIFY(checkDir.exists(file)); + QFile src(QDir(tmp.path()).absoluteFilePath(file)); + QVERIFY(src.open(QFile::ReadOnly)); + QFile dst(checkDir.absoluteFilePath(file)); + QVERIFY(dst.open(QFile::ReadOnly)); + QCOMPARE(src.readAll(), dst.readAll()); + } +} + + +bool tst_PackagerTool::createInfoYaml(QTemporaryDir &tmp, const QString &changeField, const QVariant &toValue) +{ + QByteArray yaml = + "formatVersion: 1\n" + "formatType: am-application\n" + "---\n" + "id: com.pelagicore.test\n" + "name: { en_US: 'test' }\n" + "icon: icon.png\n" + "code: test.qml\n" + "runtime: qml\n"; + + if (!changeField.isEmpty()) { + QVector<QVariant> docs; + try { + docs = YamlParser::parseAllDocuments(yaml); + } catch (...) { + } + + QVariantMap map = docs.at(1).toMap(); + if (!toValue.isValid()) + map.remove(changeField); + else + map[changeField] = toValue; + yaml = QtYaml::yamlFromVariantDocuments({ docs.at(0), map }); + } + + QFile infoYaml(QDir(tmp.path()).absoluteFilePath(qSL("info.yaml"))); + return infoYaml.open(QFile::WriteOnly) && infoYaml.write(yaml) == yaml.size(); +} + +bool tst_PackagerTool::createIconPng(QTemporaryDir &tmp) +{ + QFile iconPng(QDir(tmp.path()).absoluteFilePath(qSL("icon.png"))); + return iconPng.open(QFile::WriteOnly) && iconPng.write("\x89PNG") == 4; +} + +bool tst_PackagerTool::createCode(QTemporaryDir &tmp) +{ + QFile code(QDir(tmp.path()).absoluteFilePath(qSL("test.qml"))); + return code.open(QFile::WriteOnly) && code.write("// test") == 7LL; +} + +void tst_PackagerTool::createDummyFile(QTemporaryDir &tmp, const QString &fileName, const char *data) +{ + QFile code(QDir(tmp.path()).absoluteFilePath(fileName)); + QVERIFY(code.open(QFile::WriteOnly)); + + auto written = code.write(data); + + QCOMPARE(written, static_cast<qint64>(strlen(data))); +} + +void tst_PackagerTool::installPackage(const QString &filePath) +{ + QSignalSpy finishedSpy(m_pm, &PackageManager::taskFinished); + + m_pm->setDevelopmentMode(true); // allow packages without store signature + + QString taskId = m_pm->startPackageInstallation(QUrl::fromLocalFile(filePath)); + m_pm->acknowledgePackageInstallation(taskId); + + QVERIFY(finishedSpy.wait(2 * spyTimeout)); + QCOMPARE(finishedSpy.first()[0].toString(), taskId); + + m_pm->setDevelopmentMode(false); +} + +QTEST_GUILESS_MAIN(tst_PackagerTool) + +#include "tst_packager-tool.moc" diff --git a/tests/auto/processreader/CMakeLists.txt b/tests/auto/processreader/CMakeLists.txt new file mode 100644 index 00000000..5e60adfc --- /dev/null +++ b/tests/auto/processreader/CMakeLists.txt @@ -0,0 +1,23 @@ + +qt_internal_add_test(tst_processreader + SOURCES + ../error-checking.h + tst_processreader.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManManagerPrivate + Qt::AppManMonitorPrivate + Qt::AppManWindowPrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_processreader CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/processreader/advanced.smaps b/tests/auto/processreader/advanced.smaps new file mode 100644 index 00000000..61212565 --- /dev/null +++ b/tests/auto/processreader/advanced.smaps @@ -0,0 +1,252 @@ +55e362f85000-55e36315a000 r-xp 00000000 08:05 20988510 /qtbase/bin/appman +Size: 1876 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 1704 kB +Pss: 1704 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 1704 kB +Private_Dirty: 0 kB +Referenced: 1704 kB +Anonymous: 0 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 1704 kB +VmFlags: rd ex mr mw me dw ?? sd +55e36315b000-55e363162000 r--p 001d5000 08:05 20988510 /qtbase/bin/appman +Size: 28 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 28 kB +Pss: 28 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 28 kB +Referenced: 28 kB +Anonymous: 28 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 28 kB +VmFlags: rd mr mw me dw ac ?? sd +55e363162000-55e363164000 rw-p 001dc000 08:05 20988510 /qtbase/bin/appman +Size: 8 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 8 kB +Pss: 8 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 8 kB +Referenced: 8 kB +Anonymous: 8 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 8 kB +VmFlags: rd wr mr mw me dw ac ?? sd +55e363164000-55e363166000 rw-p 00000000 00:00 0 +Size: 8 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 4 kB +Pss: 4 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 4 kB +Referenced: 4 kB +Anonymous: 4 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 4 kB +VmFlags: rd wr mr mw me ac sd +55e3645eb000-55e364c57000 rw-p 00000000 00:00 0 [heap] +Size: 6576 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 6288 kB +Pss: 6288 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 6288 kB +Referenced: 6288 kB +Anonymous: 6288 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 6288 kB +VmFlags: rd wr mr mw me ac sd +7f85d0000000-7f85d093a000 rw-p 00000000 00:00 0 +Size: 9448 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 9448 kB +Pss: 9448 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 9448 kB +Referenced: 9448 kB +Anonymous: 9448 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 9448 kB +VmFlags: rd wr mr mw me nr sd +7f85d093a000-7f85d4000000 ---p 00000000 00:00 0 +Size: 56088 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 0 kB +Pss: 0 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 0 kB +Anonymous: 0 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 0 kB +VmFlags: mr mw me nr sd +7f85d5fef000-7f85d632d000 rw-s 00000000 00:2e 19336830 /run/user/1000/wayland-cursor-shared-jzyii9 (deleted) +Size: 3320 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 0 kB +Pss: 0 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 0 kB +Anonymous: 0 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 0 kB +VmFlags: rd wr sh mr mw me ms sd +7f85d63c5000-7f85d63fc000 r-xp 00000000 08:01 4984507 /lib/x86_64-linux-gnu/libnss_systemd.so.2 +Size: 220 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 64 kB +Pss: 3 kB +Shared_Clean: 64 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 64 kB +Anonymous: 0 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 3 kB +VmFlags: rd ex mr mw me sd +7ffd75741000-7ffd75763000 rw-p 00000000 00:00 0 [stack] +Size: 136 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 64 kB +Pss: 64 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 64 kB +Referenced: 64 kB +Anonymous: 64 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 64 kB +VmFlags: rd wr mr mw me gd ac +7ffd757e9000-7ffd757ec000 r--p 00000000 00:00 0 [vvar] +Size: 12 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 0 kB +Pss: 0 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 0 kB +Anonymous: 0 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 0 kB +VmFlags: rd mr pf io de dd sd +7ffd757ec000-7ffd757ee000 r-xp 00000000 00:00 0 [vdso] +Size: 8 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 4 kB +Pss: 0 kB +Shared_Clean: 4 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 4 kB +Anonymous: 0 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 0 kB +VmFlags: rd ex mr mw me de sd diff --git a/tests/auto/processreader/basic.smaps b/tests/auto/processreader/basic.smaps new file mode 100644 index 00000000..00640230 --- /dev/null +++ b/tests/auto/processreader/basic.smaps @@ -0,0 +1,352 @@ +00400000-00413000 r-xp 00000000 b3:01 266877 application +Size: 76 kB +Rss: 76 kB +Pss: 38 kB +Shared_Clean: 76 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 76 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd ex mr mw me dw +00422000-00423000 rw-p 00012000 b3:01 266877 application +Size: 4 kB +Rss: 4 kB +Pss: 4 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 4 kB +Referenced: 4 kB +Anonymous: 4 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me dw ac +00423000-004ec000 rw-p 00000000 00:00 0 [heap] +Size: 804 kB +Rss: 796 kB +Pss: 796 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 796 kB +Referenced: 796 kB +Anonymous: 796 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me ac +004ed000-00bc9000 rw-p 00000000 00:00 0 [heap] +Size: 7024 kB +Rss: 6700 kB +Pss: 6700 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 6700 kB +Referenced: 6700 kB +Anonymous: 6700 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me ac +7f54000000-7f54021000 rw-p 00000000 00:00 0 +Size: 132 kB +Rss: 4 kB +Pss: 4 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 4 kB +Referenced: 4 kB +Anonymous: 4 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me nr +7f54021000-7f58000000 ---p 00000000 00:00 0 +Size: 65404 kB +Rss: 0 kB +Pss: 0 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 0 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: mr mw me nr +7f5c000000-7f5c00a000 rw-p 00000000 00:00 0 +Size: 40 kB +Rss: 40 kB +Pss: 40 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 40 kB +Referenced: 40 kB +Anonymous: 40 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me nr +7f600d1000-7f604d1000 rw-s 00000000 00:09 12516 anon_inode:dmabuf +Size: 4096 kB +Rss: 1656 kB +Pss: 828 kB +Shared_Clean: 0 kB +Shared_Dirty: 1656 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 1656 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr sh mr mw me ms dc de dd +7f604d1000-7f608d1000 rw-s 00000000 00:09 12516 anon_inode:dmabuf +Size: 4096 kB +Rss: 4084 kB +Pss: 2042 kB +Shared_Clean: 0 kB +Shared_Dirty: 4084 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 4084 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr sh mr mw me ms dc de dd +7f614d2000-7f6168a000 r-xp 00000000 b3:01 12487 /verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/lib.so +Size: 1760 kB +Rss: 1448 kB +Pss: 726 kB +Shared_Clean: 1444 kB +Shared_Dirty: 0 kB +Private_Clean: 4 kB +Private_Dirty: 0 kB +Referenced: 1448 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd ex mr mw me +7f6168a000-7f61699000 ---p 001b8000 b3:01 12487 /verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/lib.so +Size: 60 kB +Rss: 0 kB +Pss: 0 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 0 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: mr mw me +7f61699000-7f616a0000 rw-p 001b7000 b3:01 12487 /verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/verylonglibrarypathname/lib.so +Size: 28 kB +Rss: 28 kB +Pss: 28 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 28 kB +Referenced: 28 kB +Anonymous: 28 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me ac +7f61925000-7f61ef7000 r-xp 00000000 b3:01 13766 /linelength98/linelen.so +Size: 5960 kB +Rss: 4356 kB +Pss: 1554 kB +Shared_Clean: 4264 kB +Shared_Dirty: 0 kB +Private_Clean: 92 kB +Private_Dirty: 0 kB +Referenced: 4356 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd ex mr mw me +7f61ef7000-7f61f07000 ---p 005d2000 b3:01 13766 /linelength99/lineleng.so +Size: 64 kB +Rss: 0 kB +Pss: 0 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 0 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: mr mw me +7f61f07000-7f6204c000 rw-p 005d2000 b3:01 13766 /linelength100/lineleng.so +Size: 1300 kB +Rss: 1084 kB +Pss: 986 kB +Shared_Clean: 156 kB +Shared_Dirty: 0 kB +Private_Clean: 24 kB +Private_Dirty: 904 kB +Referenced: 1084 kB +Anonymous: 904 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me ac +7f74146000-7f74945000 rw-p 00000000 00:00 0 [stack:3396] +Size: 8188 kB +Rss: 8 kB +Pss: 8 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 8 kB +Referenced: 8 kB +Anonymous: 8 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me ac +7f74946000-7f75145000 rw-p 00000000 00:00 0 [stack:3116] +Size: 8188 kB +Rss: 8 kB +Pss: 8 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 8 kB +Referenced: 8 kB +Anonymous: 8 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me ac +7f78fff000-7f79001000 rw-s 00000000 00:16 37047 /tmp/.gl3LHuLJ (deleted) +Size: 8 kB +Rss: 8 kB +Pss: 8 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 8 kB +Referenced: 8 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr sh mr mw me ms +7f79001000-7f79003000 r-xs 00000000 00:16 37047 /tmp/.gl3LHuLJ (deleted) +Size: 8 kB +Rss: 0 kB +Pss: 0 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 0 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd ex sh mr mw me ms +7f79005000-7f79006000 r--p 00000000 00:00 0 [vvar] +Size: 4 kB +Rss: 4 kB +Pss: 0 kB +Shared_Clean: 4 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 4 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd mr de +7f79006000-7f79007000 r-xp 00000000 00:00 0 [vdso] +Size: 4 kB +Rss: 4 kB +Pss: 0 kB +Shared_Clean: 4 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 4 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd ex mr mw me de +7ffc8f6000-7ffc917000 rw-p 00000000 00:00 0 [stack] +Size: 136 kB +Rss: 44 kB +Pss: 44 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 44 kB +Referenced: 44 kB +Anonymous: 44 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd wr mr mw me gd ac diff --git a/tests/auto/processreader/invalid.smaps b/tests/auto/processreader/invalid.smaps new file mode 100644 index 00000000..146a42b8 --- /dev/null +++ b/tests/auto/processreader/invalid.smaps @@ -0,0 +1,16 @@ +00400000-00413000 r-xp 00000000 b3:01 266877 application +Size: #missing +Rss: 76 kB +Pss: 38 kB +Shared_Clean: 76 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 0 kB +Referenced: 76 kB +Anonymous: 0 kB +AnonHugePages: 0 kB +Swap: 0 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Locked: 0 kB +VmFlags: rd ex mr mw me dw diff --git a/tests/auto/processreader/processreader.pro b/tests/auto/processreader/processreader.pro new file mode 100644 index 00000000..c5caa674 --- /dev/null +++ b/tests/auto/processreader/processreader.pro @@ -0,0 +1,11 @@ +TARGET = tst_processreader + +include($$PWD/../tests.pri) + +QT *= appman_monitor-private \ + appman_manager-private \ + appman_window-private \ + appman_application-private \ + appman_common-private + +SOURCES += tst_processreader.cpp diff --git a/tests/auto/processreader/tst_processreader.cpp b/tests/auto/processreader/tst_processreader.cpp new file mode 100644 index 00000000..baec84cb --- /dev/null +++ b/tests/auto/processreader/tst_processreader.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> +#include <QtAppManMonitor/processreader.h> + +QT_USE_NAMESPACE_AM + +class tst_ProcessReader : public QObject +{ + Q_OBJECT + +public: + tst_ProcessReader(); + +private slots: + void memInvalid_data(); + void memInvalid(); + void memTestProcess(); + void memBasic(); + void memAdvanced(); + +private: + void printMem(const ProcessReader &reader); + ProcessReader reader; +}; + +tst_ProcessReader::tst_ProcessReader() +{} + +void tst_ProcessReader::memInvalid_data() +{ + QTest::addColumn<QString>("file"); + + QTest::newRow("arbitrary") << QFINDTESTDATA("tst_processreader.cpp"); + QTest::newRow("binary") << QFINDTESTDATA("tst_processreader"); + QTest::newRow("missingvalue") << QFINDTESTDATA("invalid.smaps"); +} + +void tst_ProcessReader::memInvalid() +{ + QFETCH(QString, file); + + reader.testReadSmaps(file.toLocal8Bit()); + + QCOMPARE(reader.memory.totalVm, 0u); + QCOMPARE(reader.memory.totalRss, 0u); + QCOMPARE(reader.memory.totalPss, 0u); + QCOMPARE(reader.memory.textVm, 0u); + QCOMPARE(reader.memory.textRss, 0u); + QCOMPARE(reader.memory.textPss, 0u); + QCOMPARE(reader.memory.heapVm, 0u); + QCOMPARE(reader.memory.heapRss, 0u); + QCOMPARE(reader.memory.heapPss, 0u); +} + +void tst_ProcessReader::memTestProcess() +{ + const QByteArray file = "/proc/" + QByteArray::number(QCoreApplication::applicationPid()) + "/smaps"; + + QVERIFY(reader.testReadSmaps(file)); + //printMem(reader); + QVERIFY(reader.memory.totalVm >= reader.memory.totalRss); + QVERIFY(reader.memory.totalRss >= reader.memory.totalPss); + QVERIFY(reader.memory.textVm >= reader.memory.textRss); + QVERIFY(reader.memory.textRss >= reader.memory.textPss); + QVERIFY(reader.memory.heapVm >= reader.memory.heapRss); + QVERIFY(reader.memory.heapRss >= reader.memory.heapPss); +} + +void tst_ProcessReader::memBasic() +{ + QVERIFY(reader.testReadSmaps(QFINDTESTDATA("basic.smaps").toLocal8Bit())); + //printMem(reader); + QCOMPARE(reader.memory.totalVm, 107384u); + QCOMPARE(reader.memory.totalRss, 20352u); + QCOMPARE(reader.memory.totalPss, 13814u); + QCOMPARE(reader.memory.textVm, 7800u); + QCOMPARE(reader.memory.textRss, 5884u); + QCOMPARE(reader.memory.textPss, 2318u); + QCOMPARE(reader.memory.heapVm, 24376u); + QCOMPARE(reader.memory.heapRss, 7556u); + QCOMPARE(reader.memory.heapPss, 7556u); +} + +void tst_ProcessReader::memAdvanced() +{ + QVERIFY(reader.testReadSmaps(QFINDTESTDATA("advanced.smaps").toLocal8Bit())); + //printMem(reader); + QCOMPARE(reader.memory.totalVm, 77728u); + QCOMPARE(reader.memory.totalRss, 17612u); + QCOMPARE(reader.memory.totalPss, 17547u); + QCOMPARE(reader.memory.textVm, 2104u); + QCOMPARE(reader.memory.textRss, 1772u); + QCOMPARE(reader.memory.textPss, 1707u); + QCOMPARE(reader.memory.heapVm, 16032u); + QCOMPARE(reader.memory.heapRss, 15740u); + QCOMPARE(reader.memory.heapPss, 15740u); +} + +void tst_ProcessReader::printMem(const ProcessReader &reader) +{ + qDebug() << "totalVm:" << reader.memory.totalVm; + qDebug() << "totalRss:" << reader.memory.totalRss; + qDebug() << "totalPss:" << reader.memory.totalPss; + qDebug() << "textVm:" << reader.memory.textVm; + qDebug() << "textRss:" << reader.memory.textRss; + qDebug() << "textPss:" << reader.memory.textPss; + qDebug() << "heapVm:" << reader.memory.heapVm; + qDebug() << "heapRss:" << reader.memory.heapRss; + qDebug() << "heapPss:" << reader.memory.heapPss; +} + +QTEST_APPLESS_MAIN(tst_ProcessReader) + +#include "tst_processreader.moc" diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt new file mode 100644 index 00000000..73ee13fe --- /dev/null +++ b/tests/auto/qml/CMakeLists.txt @@ -0,0 +1,18 @@ +# Generated from qml.pro. + +add_subdirectory(simple) +add_subdirectory(windowmanager) +add_subdirectory(windowmapping) +add_subdirectory(windowitem) +add_subdirectory(windowitem2) +add_subdirectory(installer) +add_subdirectory(quicklaunch) +add_subdirectory(intents) +add_subdirectory(configs) +add_subdirectory(lifecycle) +add_subdirectory(resources) +if(multi-process) + add_subdirectory(crash/apps/tld.test.crash/terminator2) + add_subdirectory(crash) + add_subdirectory(processtitle) +endif() diff --git a/tests/auto/qml/configs/CMakeLists.txt b/tests/auto/qml/configs/CMakeLists.txt new file mode 100644 index 00000000..8bc54456 --- /dev/null +++ b/tests/auto/qml/configs/CMakeLists.txt @@ -0,0 +1,23 @@ +# Generated from configs.pro. + +##################################################################### +## configs Binary: +##################################################################### + +qt_internal_add_executable(configs + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:configs.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "test.configs.app" +# TEST_CONFIGURATIONS = "--force-single-process" "--force-single-process --single-app $$_PRO_FILE_PWD_/apps/test.configs.app/info.yaml" +# TEST_FILES = "tst_configs.qml" + +## Scopes: +##################################################################### + +#### Keys ignored in scope 2:.:.:configs.pro:multi-process: +# TEST_CONFIGURATIONS = "--force-multi-process" "-c $$_PRO_FILE_PWD_/am-config-nodbus.yaml --dbus none" "--single-app $$_PRO_FILE_PWD_/apps/test.configs.app/info.yaml" "--disable-installer --wayland-socket-name wayland-am" diff --git a/tests/auto/qml/configs/am-config-nodbus.yaml b/tests/auto/qml/configs/am-config-nodbus.yaml new file mode 100644 index 00000000..78030bf0 --- /dev/null +++ b/tests/auto/qml/configs/am-config-nodbus.yaml @@ -0,0 +1,6 @@ +formatVersion: 1 +formatType: am-configuration +--- +systemProperties: + private: + nodbus: yes diff --git a/tests/auto/qml/configs/am-config.yaml b/tests/auto/qml/configs/am-config.yaml new file mode 100644 index 00000000..5333dae3 --- /dev/null +++ b/tests/auto/qml/configs/am-config.yaml @@ -0,0 +1,11 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +# Workaround for a crash in the mesa software renderer (llvmpipe) +runtimes: + qml: + environmentVariables: + QT_QUICK_BACKEND: "software" diff --git a/tests/auto/qml/configs/apps/test.configs.app/app.qml b/tests/auto/qml/configs/apps/test.configs.app/app.qml new file mode 100644 index 00000000..07a5f413 --- /dev/null +++ b/tests/auto/qml/configs/apps/test.configs.app/app.qml @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 2.0 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + id: root + + onWindowPropertyChanged: { + if (name === "trigger" && value === "now") + root.setWindowProperty("ack", "done"); + } + + Notification { + id: notification + summary: "Test" + timeout: 20 + } + + ApplicationInterfaceExtension { + id: extension + name: "test.configs.interface" + + } + + Connections { + target: extension.object + function onTrigger(type) { + if (type === "Notification") { + if (target.func("bar") === 42) + notification.show(); + } else if (type === "PropertyChange") { + root.setWindowProperty("prop1", "bar"); + } + } + } + + Connections { + target: ApplicationInterface + function onQuit() { + target.acknowledgeQuit(); + } + } + + Component.onCompleted: { + setWindowProperty("prop1", "foo"); + } +} diff --git a/tests/auto/qml/configs/apps/test.configs.app/icon.png b/tests/auto/qml/configs/apps/test.configs.app/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/configs/apps/test.configs.app/icon.png diff --git a/tests/auto/qml/configs/apps/test.configs.app/info.yaml b/tests/auto/qml/configs/apps/test.configs.app/info.yaml new file mode 100644 index 00000000..401ed395 --- /dev/null +++ b/tests/auto/qml/configs/apps/test.configs.app/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.configs.app' +icon: 'icon.png' +code: 'app.qml' +runtime: 'qml' +name: + en: 'ApplicationManager Application' diff --git a/tests/auto/qml/configs/configs.pro b/tests/auto/qml/configs/configs.pro new file mode 100644 index 00000000..86604d5e --- /dev/null +++ b/tests/auto/qml/configs/configs.pro @@ -0,0 +1,15 @@ +load(am-config) + +AM_CONFIG = am-config.yaml +TEST_FILES = tst_configs.qml +TEST_APPS = test.configs.app +TEST_CONFIGURATIONS = "--force-single-process" \ + "--force-single-process --single-app $$_PRO_FILE_PWD_/apps/test.configs.app/info.yaml" +multi-process { + TEST_CONFIGURATIONS += "--force-multi-process" \ + "-c $$_PRO_FILE_PWD_/am-config-nodbus.yaml --dbus none" \ + "--single-app $$_PRO_FILE_PWD_/apps/test.configs.app/info.yaml" \ + "--disable-installer --wayland-socket-name wayland-am" +} + +load(am-qml-testcase) diff --git a/tests/auto/qml/configs/tst_configs.qml b/tests/auto/qml/configs/tst_configs.qml new file mode 100644 index 00000000..88c7bd48 --- /dev/null +++ b/tests/auto/qml/configs/tst_configs.qml @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + + +TestCase { + id: testCase + when: windowShown + name: "Configs" + visible: true + + + ApplicationIPCInterface { + id: appif + signal trigger(string type) + readonly property var _decltype_func: { "int": [ "string" ] } + function func(str) { return str === "bar" ? 42: 0; } + Component.onCompleted: ApplicationIPCManager.registerInterface(this, "test.configs.interface", {}); + } + + SignalSpy { + id: windowAddedSpy + target: WindowManager + signalName: "windowAdded" + } + + SignalSpy { + id: windowPropertyChangedSpy + // Workaround to flush Wayland messages, see https://bugreports.qt.io/browse/AUTOSUITE-709 + // A proper solution in QtWayland is sought here: https://bugreports.qt.io/browse/QTBUG-83422 + function aboutToBlockWait(timeout) + { + AmTest.aboutToBlock(); + wait(timeout); + } + target: WindowManager + signalName: "windowPropertyChanged" + } + +    SignalSpy { +        id: runStateChangedSpy +        target: ApplicationManager +        signalName: "applicationRunStateChanged" +    } + + + function cleanup() { + runStateChangedSpy.clear(); + ApplicationManager.stopApplication("test.configs.app"); + runStateChangedSpy.wait(); + runStateChangedSpy.wait(); + compare(runStateChangedSpy.signalArguments[1][1], ApplicationObject.NotRunning); + } + + function test_basic_ipc() { + compare(NotificationManager.count, 0); + compare(windowAddedSpy.count, 0); + verify(ApplicationManager.startApplication("test.configs.app")) + windowAddedSpy.wait(); + compare(windowAddedSpy.count, 1); + var window = windowAddedSpy.signalArguments[0][0]; + compare(window.windowProperty("prop1"), "foo"); + appif.trigger("PropertyChange"); + windowPropertyChangedSpy.wait(); + compare(windowPropertyChangedSpy.count, 1); + compare(windowPropertyChangedSpy.signalArguments[0][0], window); + compare(window.windowProperty("prop1"), "bar"); + windowPropertyChangedSpy.clear(); + + if (!ApplicationManager.systemProperties.nodbus) { + appif.trigger("Notification"); + tryVerify(function() { return NotificationManager.count === 1; }); + compare(NotificationManager.get(0).summary, "Test"); + } + + window.setWindowProperty("trigger", "now"); + windowPropertyChangedSpy.aboutToBlockWait(); + compare(windowPropertyChangedSpy.signalArguments[0][0], window); + compare(window.windowProperty("trigger"), "now"); + + windowPropertyChangedSpy.wait(); + compare(windowPropertyChangedSpy.signalArguments[1][0], window); + compare(window.windowProperty("ack"), "done"); + } +} diff --git a/tests/auto/qml/crash/CMakeLists.txt b/tests/auto/qml/crash/CMakeLists.txt new file mode 100644 index 00000000..2ba8b4a4 --- /dev/null +++ b/tests/auto/qml/crash/CMakeLists.txt @@ -0,0 +1,17 @@ +# Generated from crash.pro. + +##################################################################### +## crash Binary: +##################################################################### + +qt_internal_add_executable(crash + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:crash.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "tld.test.crash" +# TEST_CONFIGURATIONS = "--force-multi-process" +# TEST_FILES = "tst_crash.qml" diff --git a/tests/auto/qml/crash/am-config.yaml b/tests/auto/qml/crash/am-config.yaml new file mode 100644 index 00000000..61b2bfb2 --- /dev/null +++ b/tests/auto/qml/crash/am-config.yaml @@ -0,0 +1,8 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +flags: + noUiWatchdog: yes diff --git a/tests/auto/qml/crash/apps/tld.test.crash/app.qml b/tests/auto/qml/crash/apps/tld.test.crash/app.qml new file mode 100644 index 00000000..a97c60f2 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/app.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtApplicationManager.Application 2.0 +import Terminator 2.0 + +ApplicationManagerWindow { + function accessIllegalMemory() + { + Terminator.accessIllegalMemory(); + } + + Connections { + target: ApplicationInterface + function onOpenDocument(documentUrl) { + switch (documentUrl) { + case "illegalMemory": accessIllegalMemory(); break; + case "illegalMemoryInThread": Terminator.accessIllegalMemoryInThread(); break; + case "stackOverflow": Terminator.forceStackOverflow(); break; + case "divideByZero": Terminator.divideByZero(); break; + case "unhandledException": Terminator.throwUnhandledException(); break; + case "abort": Terminator.abort(); break; + case "raise": Terminator.raise(7); break; + case "gracefully": Terminator.exitGracefully(); break; + } + } + } +} diff --git a/tests/auto/qml/crash/apps/tld.test.crash/icon.png b/tests/auto/qml/crash/apps/tld.test.crash/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/icon.png diff --git a/tests/auto/qml/crash/apps/tld.test.crash/info.yaml b/tests/auto/qml/crash/apps/tld.test.crash/info.yaml new file mode 100644 index 00000000..77ca2e76 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/info.yaml @@ -0,0 +1,12 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.test.crash' +icon: 'icon.png' +code: 'app.qml' +runtime: 'qml' +version: '1.0' +name: + en: 'Crash test' +runtimeParameters: + importPaths: [ "terminator2" ] diff --git a/tests/auto/qml/crash/apps/tld.test.crash/terminator2/CMakeLists.txt b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/CMakeLists.txt new file mode 100644 index 00000000..21646c82 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/CMakeLists.txt @@ -0,0 +1,30 @@ +# Generated from terminator2.pro. + +##################################################################### +## terminator2plugin Generic Library: +##################################################################### + +qt_internal_add_cmake_library(terminator2plugin + MODULE + INSTALL_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/Terminator" + EXCEPTIONS + OUTPUT_DIRECTORY "$$_PRO_FILE_PWD_/Terminator" + SOURCES + qmlterminator2.cpp qmlterminator2.h + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick +) + +#### Keys ignored in scope 1:.:.:terminator2.pro:<TRUE>: +# INSTALLS = "target" "qmldir" +# OTHER_FILES = "qmldir" +# QMAKE_POST_LINK = "$$QMAKE_COPY" "$$replace($$list $$quote $$PWD/qmldir $$DESTDIR , /, $$QMAKE_DIR_SEP)" +# TEMPLATE = "lib" +# qmldir.files = "$$PWD/qmldir" +# qmldir.path = "$$DESTDIR" +# target.path = "$$DESTDIR" + +qt_autogen_tools_initial_setup(terminator2plugin) diff --git a/tests/auto/qml/crash/apps/tld.test.crash/terminator2/Terminator/qmldir b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/Terminator/qmldir new file mode 100644 index 00000000..ac15a495 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/Terminator/qmldir @@ -0,0 +1,2 @@ +module Terminator +plugin terminator2plugin diff --git a/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmldir b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmldir new file mode 100644 index 00000000..ac15a495 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmldir @@ -0,0 +1,2 @@ +module Terminator +plugin terminator2plugin diff --git a/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmlterminator2.cpp b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmlterminator2.cpp new file mode 100644 index 00000000..5a90dc49 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmlterminator2.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QQmlEngine> +#include <QJSEngine> + +#include "qmlterminator2.h" + +#include <signal.h> + + +static QObject *terminator_provider(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(scriptEngine) + return new Terminator(engine); +} + +void TerminatorPlugin::registerTypes(const char *uri) +{ + qmlRegisterSingletonType<Terminator>(uri, 2, 0, "Terminator", terminator_provider); +} + + +static void abortWithVeryLongSymbolNameOnTheStack800CharactersLong_CallMeIshmaelSomeYearsAgoNeverMindHowLongPreciselyHavingLittleOrNoMoneyInMyPurseAndNothingParticularToInterestMeOnShoreIThoughIWouldSailAboutALittlAndSeeTheWateryPartOfTheWorldItIsAWayIHaveOfDrivingOffTheSpleenAndRegulatingTheCirculationWhenenverIFindMyselfGrowingGrimAboutTheMouthWheneverItIsADampDrizzlyNovemberInMySoulWheneverIFindMyselfInvoluntarilyPausingBeforeCoffinWarehousesAndBringingUpTheRearOfEveryFuneralIMeetAndEspeciallyWheneverMyHyposGetSuchAnUpperHandOfMeThatItRequiresAStrongMoralPrincipleToPreventMeFromDeliberatelySteppingIntoTheStreetAndMethodicallyKnockingPeoplesHatsOffThenIAccountItHighTimeToGetToSeaAsSoonAsICanThisIsMySubstituteForPistolAndBallWithAPhilosophicalFlourishCatoThrowsHimselfUponHisSwordIQuietlyTakeToTheShip() +{ + ::abort(); +} + +void Terminator::accessIllegalMemory() const +{ + *(int*)1 = 42; +} + +void Terminator::accessIllegalMemoryInThread() +{ + TerminatorThread *t = new TerminatorThread(this); + t->start(); +} + +void Terminator::forceStackOverflow() const +{ + static constexpr int len = 100000; + volatile char buf[len]; + buf[len-1] = 42; + if (buf[len-1] == 42) + forceStackOverflow(); +} + +void Terminator::divideByZero() const +{ + int d = 0; + volatile int x = 42 / d; + Q_UNUSED(x) +} + +void Terminator::abort() const +{ + abortWithVeryLongSymbolNameOnTheStack800CharactersLong_CallMeIshmaelSomeYearsAgoNeverMindHowLongPreciselyHavingLittleOrNoMoneyInMyPurseAndNothingParticularToInterestMeOnShoreIThoughIWouldSailAboutALittlAndSeeTheWateryPartOfTheWorldItIsAWayIHaveOfDrivingOffTheSpleenAndRegulatingTheCirculationWhenenverIFindMyselfGrowingGrimAboutTheMouthWheneverItIsADampDrizzlyNovemberInMySoulWheneverIFindMyselfInvoluntarilyPausingBeforeCoffinWarehousesAndBringingUpTheRearOfEveryFuneralIMeetAndEspeciallyWheneverMyHyposGetSuchAnUpperHandOfMeThatItRequiresAStrongMoralPrincipleToPreventMeFromDeliberatelySteppingIntoTheStreetAndMethodicallyKnockingPeoplesHatsOffThenIAccountItHighTimeToGetToSeaAsSoonAsICanThisIsMySubstituteForPistolAndBallWithAPhilosophicalFlourishCatoThrowsHimselfUponHisSwordIQuietlyTakeToTheShip(); +} + +void Terminator::raise(int sig) const +{ + ::raise(sig); +} + +void Terminator::throwUnhandledException() const +{ + throw 42; +} + +void Terminator::exitGracefully() const +{ + exit(5); +} + + +TerminatorThread::TerminatorThread(Terminator *parent) + : QThread(parent), m_terminator(parent) +{ +} + +void TerminatorThread::run() +{ + m_terminator->accessIllegalMemory(); +} + +#include "moc_qmlterminator2.cpp" diff --git a/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmlterminator2.h b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmlterminator2.h new file mode 100644 index 00000000..27bc8e28 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmlterminator2.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +#pragma once + +#include <QQmlExtensionPlugin> +#include <QThread> + + +class TerminatorPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + void registerTypes(const char *uri) override; +}; + + +class Terminator : public QObject +{ + Q_OBJECT + +public: + Terminator(QObject *parent) : QObject(parent) {} + + Q_INVOKABLE void accessIllegalMemory() const; + Q_INVOKABLE void accessIllegalMemoryInThread(); + Q_INVOKABLE void forceStackOverflow() const; + Q_INVOKABLE void divideByZero() const; + Q_INVOKABLE void abort() const; + Q_INVOKABLE void raise(int sig) const; + Q_INVOKABLE void throwUnhandledException() const; + Q_INVOKABLE void exitGracefully() const; +}; + + +class TerminatorThread : public QThread +{ + Q_OBJECT + +public: + explicit TerminatorThread(Terminator *parent); + +private: + void run() override; + Terminator *m_terminator; +}; diff --git a/tests/auto/qml/crash/apps/tld.test.crash/terminator2/terminator2.pro b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/terminator2.pro new file mode 100644 index 00000000..3c482379 --- /dev/null +++ b/tests/auto/qml/crash/apps/tld.test.crash/terminator2/terminator2.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +CONFIG += plugin exceptions +QT += qml quick + +TARGET = $$qtLibraryTarget(terminator2plugin) + +HEADERS += qmlterminator2.h +SOURCES += qmlterminator2.cpp + +DESTDIR = $$_PRO_FILE_PWD_/Terminator +target.path=$$DESTDIR +qmldir.files=$$PWD/qmldir +qmldir.path=$$DESTDIR +INSTALLS += target qmldir + +OTHER_FILES += qmldir + +QMAKE_POST_LINK += $$QMAKE_COPY $$replace($$list($$quote($$PWD/qmldir) $$DESTDIR), /, $$QMAKE_DIR_SEP) diff --git a/tests/auto/qml/crash/crash.pro b/tests/auto/qml/crash/crash.pro new file mode 100644 index 00000000..c1c7d869 --- /dev/null +++ b/tests/auto/qml/crash/crash.pro @@ -0,0 +1,5 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_crash.qml +TEST_APPS = tld.test.crash +TEST_CONFIGURATIONS = "--force-multi-process" +load(am-qml-testcase) diff --git a/tests/auto/qml/crash/tst_crash.qml b/tests/auto/qml/crash/tst_crash.qml new file mode 100644 index 00000000..cc7f42d2 --- /dev/null +++ b/tests/auto/qml/crash/tst_crash.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +TestCase { + id: testCase + when: windowShown + name: "Crashtest" + + property string appId: "tld.test.crash" + property var app: ApplicationManager.application(appId); + + SignalSpy { +        id: runStateChangedSpy +        target: ApplicationManager +        signalName: "applicationRunStateChanged" +    } + + function initTestCase() { + compare(app.lastExitStatus, ApplicationObject.NormalExit) + } + + function test_crash_data() { + return [ { tag: "gracefully" }, + { tag: "illegalMemory" }, + { tag: "illegalMemoryInThread" }, + { tag: "unhandledException" }, + { tag: "abort" } ]; + //{ tag: "stackOverflow" }, + //{ tag: "divideByZero" }, + //{ tag: "raise" } ]; + } + + function test_crash(data) { + ApplicationManager.startApplication(appId); + runStateChangedSpy.wait(3000); + runStateChangedSpy.wait(3000); + compare(app.runState, ApplicationObject.Running); + ApplicationManager.startApplication(appId, data.tag); + runStateChangedSpy.wait(3000); + compare(app.runState, ApplicationObject.NotRunning); + if (data.tag === "gracefully") { + compare(app.lastExitStatus, ApplicationObject.NormalExit); + compare(app.lastExitCode, 5); + } else { + compare(app.lastExitStatus, ApplicationObject.CrashExit); + console.info("================================"); + console.info("=== INTENDED CRASH (TESTING) ==="); + console.info("================================"); + } + } +} diff --git a/tests/auto/qml/installer/CMakeLists.txt b/tests/auto/qml/installer/CMakeLists.txt new file mode 100644 index 00000000..6daf54ee --- /dev/null +++ b/tests/auto/qml/installer/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from installer.pro. + +##################################################################### +## installer Binary: +##################################################################### + +qt_internal_add_executable(installer + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:installer.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# AM_TESTDATA_DIR = "\"$$PWD/../../data/\"" +# TEST_FILES = "tst_installer.qml" diff --git a/tests/auto/qml/installer/am-config.yaml b/tests/auto/qml/installer/am-config.yaml new file mode 100644 index 00000000..d0e12789 --- /dev/null +++ b/tests/auto/qml/installer/am-config.yaml @@ -0,0 +1,10 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + installationDir: "/tmp/am-installer-test/apps" + documentDir: "/tmp/am-installer-test/docs" + +flags: + noSecurity: yes diff --git a/tests/auto/qml/installer/apps/hello-world.red/app1.qml b/tests/auto/qml/installer/apps/hello-world.red/app1.qml new file mode 100644 index 00000000..7e6ba025 --- /dev/null +++ b/tests/auto/qml/installer/apps/hello-world.red/app1.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + color: "green" +} diff --git a/tests/auto/qml/installer/apps/hello-world.red/icon1.png b/tests/auto/qml/installer/apps/hello-world.red/icon1.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/installer/apps/hello-world.red/icon1.png diff --git a/tests/auto/qml/installer/apps/hello-world.red/info.yaml b/tests/auto/qml/installer/apps/hello-world.red/info.yaml new file mode 100644 index 00000000..1b628d1e --- /dev/null +++ b/tests/auto/qml/installer/apps/hello-world.red/info.yaml @@ -0,0 +1,10 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'hello-world.red' +version: 'v1' +icon: 'icon1.png' +code: 'app1.qml' +runtime: 'qml' +name: + en: 'Builtin Installation Test App v1' diff --git a/tests/auto/qml/installer/installer.pro b/tests/auto/qml/installer/installer.pro new file mode 100644 index 00000000..2adfb2a4 --- /dev/null +++ b/tests/auto/qml/installer/installer.pro @@ -0,0 +1,6 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_installer.qml + +AM_TESTDATA_DIR=\"$$PWD/../../data/\" + +load(am-qml-testcase) diff --git a/tests/auto/qml/installer/tst_installer.qml b/tests/auto/qml/installer/tst_installer.qml new file mode 100644 index 00000000..d3443ee7 --- /dev/null +++ b/tests/auto/qml/installer/tst_installer.qml @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +TestCase { + name: "Installer" + when: windowShown + + property var stateList: [] + property int spyTimeout: 5000 * AmTest.timeoutFactor + + SignalSpy { + id: taskFinishedSpy + target: PackageManager + signalName: "taskFinished" + } + + SignalSpy { + id: taskFailedSpy + target: PackageManager + signalName: "taskFailed" + } + + SignalSpy { + id: taskStateChangedSpy + target: PackageManager + signalName: "taskStateChanged" + } + + SignalSpy { + id: taskRequestingInstallationAcknowledgeSpy + target: PackageManager + signalName: "taskRequestingInstallationAcknowledge" + } + + SignalSpy { +        id: applicationChangedSpy +        target: ApplicationManager +        signalName: "applicationChanged" +    } + + + function init() { + // Remove previous installations + + for (var pkg of [ "hello-world.red", "com.pelagicore.test" ]) { + var po = PackageManager.package(pkg) + if (!po || (po.builtIn && !po.builtInHasRemovableUpdate)) + continue + if (PackageManager.removePackage(pkg, false, true)) { + taskFinishedSpy.wait(spyTimeout); + compare(taskFinishedSpy.count, 1); + taskFinishedSpy.clear(); + } + } + } + + function test_1states() { + PackageManager.packageAdded.connect(function(pkgId) { + var pkg = PackageManager.package(pkgId); + stateList.push(pkg.state) + pkg.stateChanged.connect(function(state) { + compare(state, pkg.state) + stateList.push(state) + }) + }) + + taskStateChangedSpy.clear(); + var id = PackageManager.startPackageInstallation(ApplicationManager.systemProperties.AM_TESTDATA_DIR + + "/packages/test-dev-signed.appkg") + taskRequestingInstallationAcknowledgeSpy.wait(spyTimeout); + compare(taskRequestingInstallationAcknowledgeSpy.count, 1); + compare(taskRequestingInstallationAcknowledgeSpy.signalArguments[0][0], id); + var pkgId = taskRequestingInstallationAcknowledgeSpy.signalArguments[0][1].id + taskRequestingInstallationAcknowledgeSpy.clear(); + PackageManager.acknowledgePackageInstallation(id); + + if (!taskFinishedSpy.count) + taskFinishedSpy.wait(spyTimeout); + compare(taskFinishedSpy.count, 1); + taskFinishedSpy.clear(); + + compare(stateList.length, 2); + compare(stateList[0], PackageObject.BeingInstalled) + compare(stateList[1], PackageObject.Installed) + stateList = [] + + compare(PackageManager.package(pkgId).version, "1.0"); + + id = PackageManager.startPackageInstallation(ApplicationManager.systemProperties.AM_TESTDATA_DIR + + "/packages/test-update-dev-signed.appkg") + taskRequestingInstallationAcknowledgeSpy.wait(spyTimeout); + compare(taskRequestingInstallationAcknowledgeSpy.count, 1); + compare(taskRequestingInstallationAcknowledgeSpy.signalArguments[0][0], id); + taskRequestingInstallationAcknowledgeSpy.clear(); + PackageManager.acknowledgePackageInstallation(id); + + taskFinishedSpy.wait(spyTimeout); + compare(taskFinishedSpy.count, 1); + taskFinishedSpy.clear(); + + compare(stateList[0], PackageObject.BeingUpdated) + compare(stateList[1], PackageObject.Installed) + stateList = [] + + compare(PackageManager.package(pkgId).version, "2.0"); + + id = PackageManager.removePackage(pkgId, false, false); + + taskFinishedSpy.wait(spyTimeout); + compare(taskFinishedSpy.count, 1); + taskFinishedSpy.clear(); + + compare(stateList[0], PackageObject.BeingRemoved) + stateList = [] + // Cannot compare app.state any more, since app might already be dead + + verify(taskStateChangedSpy.count > 10); + var taskStates = [ PackageManager.Executing, + PackageManager.AwaitingAcknowledge, + PackageManager.Installing, + PackageManager.CleaningUp, + PackageManager.Finished, + PackageManager.Executing, + PackageManager.AwaitingAcknowledge, + PackageManager.Installing, + PackageManager.CleaningUp, + PackageManager.Finished, + PackageManager.Executing ] + for (var i = 0; i < taskStates.length; i++) + compare(taskStateChangedSpy.signalArguments[i][1], taskStates[i], "- index: " + i); + } + + function test_2cancel_update() { + var id = PackageManager.startPackageInstallation(ApplicationManager.systemProperties.AM_TESTDATA_DIR + + "/packages/test-dev-signed.appkg") + taskRequestingInstallationAcknowledgeSpy.wait(spyTimeout); + compare(taskRequestingInstallationAcknowledgeSpy.count, 1); + compare(taskRequestingInstallationAcknowledgeSpy.signalArguments[0][0], id); + var pkgId = taskRequestingInstallationAcknowledgeSpy.signalArguments[0][1].id + compare(pkgId, "com.pelagicore.test"); + taskRequestingInstallationAcknowledgeSpy.clear(); + PackageManager.acknowledgePackageInstallation(id); + + taskFinishedSpy.wait(spyTimeout); + taskFinishedSpy.clear(); + + var pkg = PackageManager.package(pkgId); + compare(pkg.version, "1.0"); + + id = PackageManager.startPackageInstallation(ApplicationManager.systemProperties.AM_TESTDATA_DIR + + "/packages/test-update-dev-signed.appkg") + taskRequestingInstallationAcknowledgeSpy.wait(spyTimeout); + pkgId = taskRequestingInstallationAcknowledgeSpy.signalArguments[0][1].id + compare(pkgId, "com.pelagicore.test"); + taskRequestingInstallationAcknowledgeSpy.clear(); + PackageManager.cancelTask(id); + + taskFailedSpy.wait(spyTimeout); + taskFailedSpy.clear(); + + compare(pkg.version, "1.0"); + } + + function test_3cancel_builtin_update() { + taskStateChangedSpy.clear() + var pkg = PackageManager.package("hello-world.red"); + verify(pkg.builtIn); + compare(pkg.icon.toString().slice(-9), "icon1.png") + compare(pkg.version, "v1"); + + var id = PackageManager.startPackageInstallation(ApplicationManager.systemProperties.AM_TESTDATA_DIR + + "/packages/hello-world.red.appkg") + taskRequestingInstallationAcknowledgeSpy.wait(spyTimeout); + compare(taskRequestingInstallationAcknowledgeSpy.count, 1); + compare(taskRequestingInstallationAcknowledgeSpy.signalArguments[0][0], id); + taskRequestingInstallationAcknowledgeSpy.clear(); + PackageManager.cancelTask(id); + + taskFailedSpy.wait(spyTimeout); + taskFailedSpy.clear(); + + verify(pkg.builtIn); + compare(pkg.icon.toString().slice(-9), "icon1.png") + compare(pkg.version, "v1"); + } + + function test_4builtin_update_downgrade() { + taskStateChangedSpy.clear() + + var id = PackageManager.startPackageInstallation(ApplicationManager.systemProperties.AM_TESTDATA_DIR + + "/packages/hello-world.red.appkg") + taskRequestingInstallationAcknowledgeSpy.wait(spyTimeout); + compare(taskRequestingInstallationAcknowledgeSpy.count, 1); + compare(taskRequestingInstallationAcknowledgeSpy.signalArguments[0][0], id); + taskRequestingInstallationAcknowledgeSpy.clear(); + PackageManager.acknowledgePackageInstallation(id); + + taskFinishedSpy.wait(spyTimeout); + var pkg = PackageManager.package("hello-world.red") + compare(pkg.version, "red"); + taskFinishedSpy.clear(); + applicationChangedSpy.clear(); + + // remvove is a downgrade + verify(pkg.builtIn) + verify(pkg.builtInHasRemovableUpdate) + verify(PackageManager.removePackage("hello-world.red", false, true)); + taskFinishedSpy.wait(spyTimeout); + compare(taskFinishedSpy.count, 1); + taskFinishedSpy.clear(); + + compare(applicationChangedSpy.count, 3); + compare(applicationChangedSpy.signalArguments[0][0], "hello-world.red"); + compare(applicationChangedSpy.signalArguments[0][1], ["isBlocked"]); + compare(applicationChangedSpy.signalArguments[2][1], []); + + verify(!pkg.blocked) + compare(pkg.version, "v1"); + } +} diff --git a/tests/auto/qml/intents/CMakeLists.txt b/tests/auto/qml/intents/CMakeLists.txt new file mode 100644 index 00000000..20543415 --- /dev/null +++ b/tests/auto/qml/intents/CMakeLists.txt @@ -0,0 +1,23 @@ +# Generated from intents.pro. + +##################################################################### +## intents Binary: +##################################################################### + +qt_internal_add_executable(intents + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:intents.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "intents1" "intents2" "cannot-start" +# TEST_CONFIGURATIONS = "--force-single-process" +# TEST_FILES = "tst_intents.qml" + +## Scopes: +##################################################################### + +#### Keys ignored in scope 2:.:.:intents.pro:multi-process: +# TEST_CONFIGURATIONS = "--force-multi-process" "--force-multi-process -c $$_PRO_FILE_PWD_/am-config-quick.yaml" diff --git a/tests/auto/qml/intents/am-config-quick.yaml b/tests/auto/qml/intents/am-config-quick.yaml new file mode 100644 index 00000000..96b3098c --- /dev/null +++ b/tests/auto/qml/intents/am-config-quick.yaml @@ -0,0 +1,6 @@ +formatVersion: 1 +formatType: am-configuration +--- +quicklaunch: + runtimesPerContainer: 1 + idleLoad: 1.0 diff --git a/tests/auto/qml/intents/am-config.yaml b/tests/auto/qml/intents/am-config.yaml new file mode 100644 index 00000000..1667c123 --- /dev/null +++ b/tests/auto/qml/intents/am-config.yaml @@ -0,0 +1,23 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +ui: + fullscreen: no + +flags: + noSecurity: yes + noUiWatchdog: yes + +runtimes: + qml: + quitTime: 1000 + +intents: + timeouts: + disambiguation: 1000 + startApplication: 1000 + replyFromApplication: 1000 + replyFromSystem: 2000 diff --git a/tests/auto/qml/intents/apps/cannot-start/cannot-start b/tests/auto/qml/intents/apps/cannot-start/cannot-start new file mode 100644 index 00000000..5c3118dc --- /dev/null +++ b/tests/auto/qml/intents/apps/cannot-start/cannot-start @@ -0,0 +1 @@ +dummy file diff --git a/tests/auto/qml/intents/apps/cannot-start/icon.png b/tests/auto/qml/intents/apps/cannot-start/icon.png Binary files differnew file mode 100644 index 00000000..adb840ce --- /dev/null +++ b/tests/auto/qml/intents/apps/cannot-start/icon.png diff --git a/tests/auto/qml/intents/apps/cannot-start/info.yaml b/tests/auto/qml/intents/apps/cannot-start/info.yaml new file mode 100644 index 00000000..64c81589 --- /dev/null +++ b/tests/auto/qml/intents/apps/cannot-start/info.yaml @@ -0,0 +1,12 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'cannot-start' +icon: 'icon.png' +code: 'cannot-start' +runtime: 'native' +name: + en: 'Cannot start' + +intents: +- id: cannot-start-intent diff --git a/tests/auto/qml/intents/apps/intents1/icon.png b/tests/auto/qml/intents/apps/intents1/icon.png Binary files differnew file mode 100644 index 00000000..adb840ce --- /dev/null +++ b/tests/auto/qml/intents/apps/intents1/icon.png diff --git a/tests/auto/qml/intents/apps/intents1/info.yaml b/tests/auto/qml/intents/apps/intents1/info.yaml new file mode 100644 index 00000000..d5f72561 --- /dev/null +++ b/tests/auto/qml/intents/apps/intents1/info.yaml @@ -0,0 +1,20 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'intents1' +icon: 'icon.png' +code: 'intents1.qml' +runtime: 'qml' +name: + en: 'Intents1' + +intents: +- id: only1 +- id: both +- id: match + parameterMatch: + list: [ 'a', 'b' ] + int: 42 + string: "^foo_.*_bar$" + complex: { 'a': 1 } +- id: custom-error diff --git a/tests/auto/qml/intents/apps/intents1/intents1.qml b/tests/auto/qml/intents/apps/intents1/intents1.qml new file mode 100644 index 00000000..d9e80183 --- /dev/null +++ b/tests/auto/qml/intents/apps/intents1/intents1.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQml 2.2 +import QtApplicationManager 2.0 +import QtApplicationManager.Application 2.0 + +QtObject { + id: root + + property var connections: Connections { + target: ApplicationInterface + function onQuit() { + target.acknowledgeQuit(); + } + } + + property var handler: IntentHandler { + intentIds: [ "only1", "both", "match" ] + onRequestReceived: { + request.sendReply({ "from": ApplicationInterface.applicationId, "in": request.parameters}) + } + } + + property var customErrorHandler: IntentHandler { + intentIds: [ "custom-error" ] + onRequestReceived: { + request.sendErrorReply("custom error") + } + } +} diff --git a/tests/auto/qml/intents/apps/intents2/icon.png b/tests/auto/qml/intents/apps/intents2/icon.png Binary files differnew file mode 100644 index 00000000..adb840ce --- /dev/null +++ b/tests/auto/qml/intents/apps/intents2/icon.png diff --git a/tests/auto/qml/intents/apps/intents2/info.yaml b/tests/auto/qml/intents/apps/intents2/info.yaml new file mode 100644 index 00000000..209ab30c --- /dev/null +++ b/tests/auto/qml/intents/apps/intents2/info.yaml @@ -0,0 +1,16 @@ +formatVersion: 1 +formatType: am-package +--- +id: 'intents2' +icon: 'icon.png' +name: + en: 'Intents2' +applications: + - id: 'intents2.1' + code: 'intents2.qml' + runtime: 'qml' + +intents: +- id: only2 +- id: both +- id: only1 # declared here, but no handler to provoke an error diff --git a/tests/auto/qml/intents/apps/intents2/intents2.qml b/tests/auto/qml/intents/apps/intents2/intents2.qml new file mode 100644 index 00000000..2545a3f5 --- /dev/null +++ b/tests/auto/qml/intents/apps/intents2/intents2.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQml 2.2 +import QtApplicationManager 2.0 +import QtApplicationManager.Application 2.0 + +QtObject { + id: root + + property var connections: Connections { + target: ApplicationInterface + function onQuit() { + target.acknowledgeQuit(); + } + } + + property var handler: IntentHandler { + intentIds: [ "both", "only2" ] + onRequestReceived: { + Qt.callLater(function() { + request.sendReply({ "from": ApplicationInterface.applicationId, "in": request.parameters}) + }) + } + } +} diff --git a/tests/auto/qml/intents/intents.pro b/tests/auto/qml/intents/intents.pro new file mode 100644 index 00000000..a05a7b72 --- /dev/null +++ b/tests/auto/qml/intents/intents.pro @@ -0,0 +1,12 @@ +load(am-config) + +AM_CONFIG = am-config.yaml +TEST_FILES = tst_intents.qml +TEST_APPS = intents1 intents2 cannot-start +TEST_CONFIGURATIONS = "--force-single-process" +multi-process { + TEST_CONFIGURATIONS += "--force-multi-process" \ + "--force-multi-process -c $$_PRO_FILE_PWD_/am-config-quick.yaml" +} + +load(am-qml-testcase) diff --git a/tests/auto/qml/intents/tst_intents.qml b/tests/auto/qml/intents/tst_intents.qml new file mode 100644 index 00000000..2caccfcb --- /dev/null +++ b/tests/auto/qml/intents/tst_intents.qml @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtTest 1.0 +import QtApplicationManager 2.0 +import QtApplicationManager.SystemUI 2.0 + +TestCase { + id: testCase + when: windowShown + name: "Intents" + + property var stdParams: { "para": "meter" } + property var matchParams: { "list": "a", "int": 42, "string": "foo_x_bar", "complex": { "a": 1 } } + + ListView { + id: listView + model: ApplicationManager + delegate: Item { + property var modelData: model + } + } + + function initTestCase() { + verify(ApplicationManager.application("intents1")) + verify(ApplicationManager.application("intents2.1")) + if (!ApplicationManager.singleProcess) + verify(ApplicationManager.application("cannot-start")) + } + + SignalSpy { + id: requestSpy + target: null + signalName: "replyReceived" + } + + function test_intent_object() { + verify(IntentServer.count> 0) + + // test intent properties + var intent = IntentServer.applicationIntent("both", "intents1") + verify(intent) + compare(intent.intentId, "both") + compare(intent.applicationId, "intents1") + compare(intent.visibility, IntentObject.Public) + compare(intent.requiredCapabilities, []) + compare(intent.parameterMatch, {}) + + verify(!IntentServer.applicationIntent("both", "intents3")) + verify(!IntentServer.applicationIntent("bothx", "intents1")) + verify(!IntentServer.applicationIntent("both", "")) + verify(!IntentServer.applicationIntent("", "intents1")) + verify(!IntentServer.applicationIntent("", "")) + } + + + function test_match() { + // first, check the matching on the server API + var intent = IntentServer.applicationIntent("match", "intents1") + verify(!intent) + intent = IntentServer.applicationIntent("match", "intents1", matchParams) + verify(intent) + compare(intent.parameterMatch, { "list": [ "a", "b" ], "int": 42, "string": "^foo_.*_bar$", "complex": { "a": 1 } }) + + var params = matchParams + params.list = "c" + verify(!IntentServer.applicationIntent("match", "intents1", params)) + params.list = "b" + verify(IntentServer.applicationIntent("match", "intents1", params)) + + params.int = 2 + verify(!IntentServer.applicationIntent("match", "intents1", params)) + params.int = 42 + + params.string = "foo" + verify(!IntentServer.applicationIntent("match", "intents1", params)) + params.string = "foo_test_bar" + verify(IntentServer.applicationIntent("match", "intents1", params)) + + params.complex = "string" + verify(!IntentServer.applicationIntent("match", "intents1", params)) + params.complex = matchParams.complex + } + + + function test_intents_data() { + return [ + {tag: "1-1", intentId: "only1", appId: "intents1", succeeding: true }, + {tag: "2-2", intentId: "only2", appId: "intents2.1", succeeding: true }, + {tag: "1-2", intentId: "only1", appId: "intents2.1", succeeding: false, + errorMessage: "No matching IntentHandler found." }, + {tag: "2-1", intentId: "only2", appId: "intents1", succeeding: false, + errorMessage: "No matching intent handler registered.", + ignoreWarning: 'Unknown intent "only2" was requested from application ":sysui:"' }, + {tag: "match-1", intentId: "match", appId: "intents1", succeeding: true, params: matchParams }, + {tag: "match-2", intentId: "match", appId: "intents1", succeeding: false, + params: function() { var x = Object.assign({}, matchParams); x.int = 1; return x }(), + errorMessage: "No matching intent handler registered.", + ignoreWarning: 'Unknown intent "match" was requested from application ":sysui:"' }, + {tag: "match-3", intentId: "match", appId: "intents1", succeeding: false, params: {}, + errorMessage: "No matching intent handler registered.", + ignoreWarning: 'Unknown intent "match" was requested from application ":sysui:"' }, + {tag: "unknown-1", intentId: "unknown", appId: "intents1", succeeding: false, + errorMessage: "No matching intent handler registered.", + ignoreWarning: 'Unknown intent "unknown" was requested from application ":sysui:"' }, + {tag: "unknown-2", intentId: "unknown", appId: "", succeeding: false, + errorMessage: "No matching intent handler registered.", + ignoreWarning: 'Unknown intent "unknown" was requested from application ":sysui:"' }, + {tag: "custom-error", intentId: "custom-error", appId: "intents1", succeeding: false, + errorMessage: "custom error" }, + {tag: "cannot-start", intentId: "cannot-start-intent", appId: "cannot-start", succeeding: false, + errorMessage: /Starting handler application timed out after .*/ }, + ]; + } + + function test_intents(data) { + if (data.appId && !ApplicationManager.application(data.appId)) + skip("Application \"" + data.appId + "\" is not available") + + if (data.ignoreWarning) + ignoreWarning(data.ignoreWarning) + + var params = ("params" in data) ? data.params : stdParams + + var req = IntentClient.sendIntentRequest(data.intentId, data.appId, params) + verify(req) + requestSpy.target = req + tryCompare(requestSpy, "count", 1, 1000) + compare(req.succeeded, data.succeeding) + if (req.succeeded) { + compare(req.result, { "from": data.appId, "in": params }) + } else { + if (data.errorMessage instanceof RegExp) + verify(data.errorMessage.test(req.errorMessage)) + else + compare(req.errorMessage, data.errorMessage) + } + requestSpy.clear() + requestSpy.target = null + } + + function test_disambiguate_data() { + return [ + {tag: "no-signal", action: "none", succeeding: true }, + {tag: "reject", action: "reject", succeeding: false, + errorMessage: "Disambiguation was rejected" }, + {tag: "timeout", action: "timeout", succeeding: false, + errorMessage: /Disambiguation timed out after .*/ }, + {tag: "ack-invalid", action: "acknowledge", acknowledgeIntentId: "only1", succeeding: false, + errorMessage: "Failed to disambiguate", + ignoreWarning: /IntentServer::acknowledgeDisambiguationRequest for intent .* tried to disambiguate to the intent "only1" which was not in the list of potential disambiguations/ }, + {tag: "ack-valid", action: "acknowledge", succeeding: true } + ]; + } + + SignalSpy { + id: disambiguateSpy + target: IntentServer + } + + function test_disambiguate(data) { + var intentId = "both" + + if (data.ignoreWarning) + ignoreWarning(data.ignoreWarning) + + disambiguateSpy.signalName = data.action === "none" ? "" : "disambiguationRequest"; + + var req = IntentClient.sendIntentRequest("both", stdParams) + verify(req) + requestSpy.target = req + + if (data.action !== "none") { + tryCompare(disambiguateSpy, "count", 1, 1000) + var possibleIntents = disambiguateSpy.signalArguments[0][1] + compare(possibleIntents.length, 2) + compare(possibleIntents[0].intentId, intentId) + compare(possibleIntents[1].intentId, intentId) + compare(possibleIntents[0].applicationId, "intents1") + compare(possibleIntents[1].applicationId, "intents2.1") + compare(disambiguateSpy.signalArguments[0][2], stdParams) + + switch (data.action) { + case "reject": + IntentServer.rejectDisambiguationRequest(disambiguateSpy.signalArguments[0][0]) + break + case "acknowledge": + var intent = data.acknowledgeIntentId ? IntentServer.applicationIntent(data.acknowledgeIntentId, + possibleIntents[0].applicationId) + : possibleIntents[1] + IntentServer.acknowledgeDisambiguationRequest(disambiguateSpy.signalArguments[0][0], intent) + break + } + disambiguateSpy.clear() + } + + tryCompare(requestSpy, "count", 1, data.action === "timeout" ? 15000 : 1000) + var succeeding = data.succeeding + compare(req.succeeded, succeeding) + if (succeeding) { + compare(req.errorMessage, "") + verify(req.result !== {}) + } else { + if (data.errorMessage instanceof RegExp) + verify(data.errorMessage.test(req.errorMessage)) + else + compare(req.errorMessage, data.errorMessage) + compare(req.result, {}) + } + requestSpy.clear() + } +} diff --git a/tests/auto/qml/lifecycle/CMakeLists.txt b/tests/auto/qml/lifecycle/CMakeLists.txt new file mode 100644 index 00000000..36b09f7c --- /dev/null +++ b/tests/auto/qml/lifecycle/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from lifecycle.pro. + +##################################################################### +## lifecycle Binary: +##################################################################### + +qt_internal_add_executable(lifecycle + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:lifecycle.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "tld.test.lifecycle" +# TEST_FILES = "tst_lifecycle.qml" diff --git a/tests/auto/qml/lifecycle/am-config.yaml b/tests/auto/qml/lifecycle/am-config.yaml new file mode 100644 index 00000000..61b2bfb2 --- /dev/null +++ b/tests/auto/qml/lifecycle/am-config.yaml @@ -0,0 +1,8 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +flags: + noUiWatchdog: yes diff --git a/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/app.qml b/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/app.qml new file mode 100644 index 00000000..a5f21ad0 --- /dev/null +++ b/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/app.qml @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + Image { + anchors.centerIn: parent + source: ApplicationInterface.icon + } +} diff --git a/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/icon.png b/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/icon.png diff --git a/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/info.yaml b/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/info.yaml new file mode 100644 index 00000000..198cbe51 --- /dev/null +++ b/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.test.lifecycle' +name: + en: 'Lifecycle Tests' +icon: 'icon.png' +code: 'app.qml' +runtime: 'qml' diff --git a/tests/auto/qml/lifecycle/lifecycle.pro b/tests/auto/qml/lifecycle/lifecycle.pro new file mode 100644 index 00000000..98ab9709 --- /dev/null +++ b/tests/auto/qml/lifecycle/lifecycle.pro @@ -0,0 +1,5 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_lifecycle.qml +TEST_APPS = tld.test.lifecycle + +load(am-qml-testcase) diff --git a/tests/auto/qml/lifecycle/tst_lifecycle.qml b/tests/auto/qml/lifecycle/tst_lifecycle.qml new file mode 100644 index 00000000..d75584e2 --- /dev/null +++ b/tests/auto/qml/lifecycle/tst_lifecycle.qml @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +TestCase { + id: testCase + when: windowShown + name: "LifeCycleTest" + visible: true + + property var app: ApplicationManager.application("tld.test.lifecycle"); + + + WindowItem { + id: chrome + anchors.fill: parent + } + + Connections { + target: WindowManager + function onWindowAdded(window) { + chrome.window = window; + } + } + + Connections { + target: chrome.window + function onContentStateChanged() { + if (chrome.window.contentState === WindowObject.NoSurface) + chrome.window = null; + } + } + + + SignalSpy { +        id: runStateChangedSpy +        target: ApplicationManager +        signalName: "applicationRunStateChanged" +    } + + SignalSpy { +        id: objectDestroyedSpy +        target: AmTest +        signalName: "objectDestroyed" +    } + + Timer { + id: stopTimer + interval: 1 + onTriggered: app.stop(); + } + + + function cleanup() { + objectDestroyedSpy.clear(); + var index = AmTest.observeObjectDestroyed(app.runtime); + app.stop(); + while (app.runState !== ApplicationObject.NotRunning) + runStateChangedSpy.wait(); + objectDestroyedSpy.wait(); + compare(objectDestroyedSpy.signalArguments[0][0], index); + } + + + // Start followed by quick stop/start in single-porcess mode caused an abort in the past + function test_fast_stop_start() { + app.start(); + runStateChangedSpy.wait(); + compare(app.runState, ApplicationObject.StartingUp); + runStateChangedSpy.wait(); + compare(app.runState, ApplicationObject.Running); + + objectDestroyedSpy.clear(); + var index = AmTest.observeObjectDestroyed(app.runtime); + + app.stop(); + runStateChangedSpy.wait(); + compare(app.runState, ApplicationObject.ShuttingDown); + runStateChangedSpy.wait(); + compare(app.runState, ApplicationObject.NotRunning); + + app.start(); + runStateChangedSpy.wait(); + compare(app.runState, ApplicationObject.StartingUp); + runStateChangedSpy.wait(); + compare(app.runState, ApplicationObject.Running); + + objectDestroyedSpy.wait(); + compare(objectDestroyedSpy.signalArguments[0][0], index); + } + + // Quick start/stop followd by start in single-process mode caused an abort in the past + function test_fast_start_stop() { + app.start(); + stopTimer.start(); + + while (app.runState !== ApplicationObject.NotRunning) + runStateChangedSpy.wait(); + + app.start(); + while (app.runState !== ApplicationObject.Running) + runStateChangedSpy.wait(); + } +} diff --git a/tests/auto/qml/processtitle/CMakeLists.txt b/tests/auto/qml/processtitle/CMakeLists.txt new file mode 100644 index 00000000..55cd61b9 --- /dev/null +++ b/tests/auto/qml/processtitle/CMakeLists.txt @@ -0,0 +1,17 @@ +# Generated from processtitle.pro. + +##################################################################### +## processtitle Binary: +##################################################################### + +qt_internal_add_executable(processtitle + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:processtitle.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "test.processtitle.app" +# TEST_CONFIGURATIONS = "--force-multi-process" "--force-multi-process -c $$_PRO_FILE_PWD_/am-config-quick.yaml" +# TEST_FILES = "tst_processtitle.qml" diff --git a/tests/auto/qml/processtitle/am-config-quick.yaml b/tests/auto/qml/processtitle/am-config-quick.yaml new file mode 100644 index 00000000..a21a1dea --- /dev/null +++ b/tests/auto/qml/processtitle/am-config-quick.yaml @@ -0,0 +1,10 @@ +formatVersion: 1 +formatType: am-configuration +--- +quicklaunch: + runtimesPerContainer: 1 + idleLoad: 1.0 + +systemProperties: + private: + quickLaunch: true diff --git a/tests/auto/qml/processtitle/am-config.yaml b/tests/auto/qml/processtitle/am-config.yaml new file mode 100644 index 00000000..4ab6878e --- /dev/null +++ b/tests/auto/qml/processtitle/am-config.yaml @@ -0,0 +1,5 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" diff --git a/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/app.qml b/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/app.qml new file mode 100644 index 00000000..7371a7c8 --- /dev/null +++ b/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/app.qml @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2020 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtApplicationManager.Application 2.0 + +Connections { + target: ApplicationInterface + function onQuit() { + target.acknowledgeQuit(); + } +} diff --git a/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/icon.png b/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/icon.png diff --git a/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/info.yaml b/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/info.yaml new file mode 100644 index 00000000..2e1cae3b --- /dev/null +++ b/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7' +icon: 'icon.png' +code: 'app.qml' +runtime: 'qml' +name: + en: 'Large App' diff --git a/tests/auto/qml/processtitle/apps/test.processtitle.app/app.qml b/tests/auto/qml/processtitle/apps/test.processtitle.app/app.qml new file mode 100644 index 00000000..7371a7c8 --- /dev/null +++ b/tests/auto/qml/processtitle/apps/test.processtitle.app/app.qml @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2020 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtApplicationManager.Application 2.0 + +Connections { + target: ApplicationInterface + function onQuit() { + target.acknowledgeQuit(); + } +} diff --git a/tests/auto/qml/processtitle/apps/test.processtitle.app/icon.png b/tests/auto/qml/processtitle/apps/test.processtitle.app/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/processtitle/apps/test.processtitle.app/icon.png diff --git a/tests/auto/qml/processtitle/apps/test.processtitle.app/info.yaml b/tests/auto/qml/processtitle/apps/test.processtitle.app/info.yaml new file mode 100644 index 00000000..e178aa04 --- /dev/null +++ b/tests/auto/qml/processtitle/apps/test.processtitle.app/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.processtitle.app' +icon: 'icon.png' +code: 'app.qml' +runtime: 'qml' +name: + en: 'Small App' diff --git a/tests/auto/qml/processtitle/processtitle.pro b/tests/auto/qml/processtitle/processtitle.pro new file mode 100644 index 00000000..e78b54a1 --- /dev/null +++ b/tests/auto/qml/processtitle/processtitle.pro @@ -0,0 +1,7 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_processtitle.qml +TEST_APPS = test.processtitle.app +TEST_CONFIGURATIONS = "--force-multi-process" \ + "--force-multi-process -c $$_PRO_FILE_PWD_/am-config-quick.yaml" + +load(am-qml-testcase) diff --git a/tests/auto/qml/processtitle/tst_processtitle.qml b/tests/auto/qml/processtitle/tst_processtitle.qml new file mode 100644 index 00000000..acb3646e --- /dev/null +++ b/tests/auto/qml/processtitle/tst_processtitle.qml @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2020 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + + +TestCase { + id: testCase + when: windowShown + name: "ProcessTitle" + visible: true + + property int sysuiPid + + ProcessStatus { + id: processStatus + applicationId: "" + Component.onCompleted: sysuiPid = processId; + } + + +    SignalSpy { +        id: runStateChangedSpy +        target: ApplicationManager +        signalName: "applicationRunStateChanged" +    } + + function test_launcher_qml_data() { + return [ { tag: "small", appId: "test.processtitle.app", resId: "test.processtitle.app" }, + { tag: "large", appId: "appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7", + resId: "appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appa" } ]; + } + + function test_launcher_qml(data) { + const executable = "appman-launcher-qml"; + var sigIdx; + var quickArg; + var pid + if (ApplicationManager.systemProperties.quickLaunch) { + sigIdx = 0; + quickArg = " --quicklaunch" + tryVerify(function() { + pid = AmTest.findChildProcess(sysuiPid, executable + quickArg); + return pid + }); + wait(250); + verify(AmTest.cmdLine(pid).endsWith(executable + quickArg)); + } else { + sigIdx = 1; + quickArg = "" + } + + runStateChangedSpy.clear(); + verify(ApplicationManager.startApplication(data.appId)); + runStateChangedSpy.wait(); + if (sigIdx === 1) + runStateChangedSpy.wait(); + + compare(runStateChangedSpy.signalArguments[sigIdx][0], data.appId); + compare(runStateChangedSpy.signalArguments[sigIdx][1], ApplicationObject.Running); + + processStatus.applicationId = data.appId; + pid = processStatus.processId; + verify(AmTest.ps(pid).endsWith(executable + ": " + data.resId + quickArg)); + verify(AmTest.cmdLine(pid).endsWith(executable + ": " + data.resId + quickArg)); + verify(AmTest.environment(pid).includes("AM_CONFIG=%YAML")); + verify(AmTest.environment(pid).includes("AM_NO_DLT_LOGGING=1")); + verify(AmTest.environment(pid).includes("WAYLAND_DISPLAY=")); + + runStateChangedSpy.clear(); + ApplicationManager.stopAllApplications(); + runStateChangedSpy.wait(); + runStateChangedSpy.wait(); + compare(runStateChangedSpy.signalArguments[1][1], ApplicationObject.NotRunning); + } +} diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro new file mode 100644 index 00000000..902ab2f1 --- /dev/null +++ b/tests/auto/qml/qml.pro @@ -0,0 +1,20 @@ +load(am-config) + +TEMPLATE = subdirs +SUBDIRS = \ + simple \ + windowmanager \ + windowmapping \ + windowitem \ + windowitem2 \ + installer \ + quicklaunch \ + intents \ + configs \ + lifecycle \ + resources + +multi-process: SUBDIRS += \ + crash/apps/tld.test.crash/terminator2 \ + crash \ + processtitle diff --git a/tests/auto/qml/quicklaunch/CMakeLists.txt b/tests/auto/qml/quicklaunch/CMakeLists.txt new file mode 100644 index 00000000..9fcb8aeb --- /dev/null +++ b/tests/auto/qml/quicklaunch/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from quicklaunch.pro. + +##################################################################### +## quicklaunch Binary: +##################################################################### + +qt_internal_add_executable(quicklaunch + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:quicklaunch.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "tld.test.quicklaunch" +# TEST_FILES = "tst_quicklaunch.qml" diff --git a/tests/auto/qml/quicklaunch/am-config.yaml b/tests/auto/qml/quicklaunch/am-config.yaml new file mode 100644 index 00000000..e8de3d00 --- /dev/null +++ b/tests/auto/qml/quicklaunch/am-config.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +quicklaunch: + runtimesPerContainer: 2 + idleLoad: 1.0 diff --git a/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/app.qml b/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/app.qml new file mode 100644 index 00000000..571288d1 --- /dev/null +++ b/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/app.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + width: 320 + height: 240 + + ApplicationInterfaceExtension { + name: "quicklaunch.interface" + onReadyChanged: object.acknowledge(); + } +} diff --git a/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/icon.png b/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/icon.png diff --git a/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/info.yaml b/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/info.yaml new file mode 100644 index 00000000..e898a02b --- /dev/null +++ b/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/info.yaml @@ -0,0 +1,10 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.test.quicklaunch' +icon: 'icon.png' +code: 'app.qml' +runtime: 'qml' +version: '1.0' +name: + en: 'Quicklaunch' diff --git a/tests/auto/qml/quicklaunch/quicklaunch.pro b/tests/auto/qml/quicklaunch/quicklaunch.pro new file mode 100644 index 00000000..2d990a4b --- /dev/null +++ b/tests/auto/qml/quicklaunch/quicklaunch.pro @@ -0,0 +1,5 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_quicklaunch.qml +TEST_APPS = tld.test.quicklaunch + +load(am-qml-testcase) diff --git a/tests/auto/qml/quicklaunch/tst_quicklaunch.qml b/tests/auto/qml/quicklaunch/tst_quicklaunch.qml new file mode 100644 index 00000000..d9744eec --- /dev/null +++ b/tests/auto/qml/quicklaunch/tst_quicklaunch.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +// Tests are meaningless in single-process mode, but still work + +TestCase { + id: testCase + when: windowShown + name: "Quicklaunch" + + property bool acknowledged: false + + SignalSpy { + id: windowAddedSpy + target: WindowManager + signalName: "windowAdded" + } + + SignalSpy { +        id: runStateChangedSpy +        signalName: "runStateChanged" +    } + + ApplicationIPCInterface { + function acknowledge() { acknowledged = true; } + Component.onCompleted: ApplicationIPCManager.registerInterface(this, "quicklaunch.interface", {}); + } + + + function test_quicklaunch() { + var app = ApplicationManager.application("tld.test.quicklaunch"); + runStateChangedSpy.target = app; + + wait(1000); + // Check for quick-launching is done every second in appman. After 1s now, this test + // sometimes caused some race where the app would not be started at all in the past: + app.start(); + windowAddedSpy.wait(3000); + tryCompare(testCase, "acknowledged", true); + runStateChangedSpy.clear(); + app.stop(true); + runStateChangedSpy.wait(3000); // wait for ShuttingDown + runStateChangedSpy.wait(3000); // wait for NotRunning + + wait(1000); + // Unfortunately there is no reliable means to determine, whether a quicklaunch process + // is running, but after at least 2s now, there should be a process that can be attached to. + acknowledged = false; + app.start(); + windowAddedSpy.wait(3000); + tryCompare(testCase, "acknowledged", true); + runStateChangedSpy.clear(); + app.stop(true); + runStateChangedSpy.wait(3000); // wait for ShuttingDown + runStateChangedSpy.wait(3000); // wait for NotRunning + } +} diff --git a/tests/auto/qml/resources/CMakeLists.txt b/tests/auto/qml/resources/CMakeLists.txt new file mode 100644 index 00000000..0893a7ac --- /dev/null +++ b/tests/auto/qml/resources/CMakeLists.txt @@ -0,0 +1,37 @@ +# Generated from resources.pro. + +##################################################################### +## systemuiplugin Generic Library: +##################################################################### + +qt_internal_add_cmake_library(systemuiplugin + MODULE + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +) + +# Resources: +set(systemuiplugin_resource_files + "qml/widgets/MagentaRect.qml" +) + +qt_internal_add_resource(systemuiplugin "systemuiplugin" + PREFIX + "/" + FILES + ${systemuiplugin_resource_files} +) + + +#### Keys ignored in scope 2:.:.:test.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# DIRECTORIES = "apps/app2/qml" "relative" +# FILES = "am-config.yaml" "apps/app1/icon.png" "apps/app1/info.yaml" "apps/app2/icon.png" "apps/app2/info.yaml" +# RESOURCE_SOURCE = "systemuifile.qrc" +# TEMPLATE = "lib" +# TEST_APPS = "app1" "app2" +# TEST_FILES = "tst_resource.qml" + +qt_autogen_tools_initial_setup(systemuiplugin) +add_subdirectory(appcommon) +add_subdirectory(apps/app1) +add_subdirectory(apps/app2) diff --git a/tests/auto/qml/resources/am-config.yaml b/tests/auto/qml/resources/am-config.yaml new file mode 100644 index 00000000..a06cb076 --- /dev/null +++ b/tests/auto/qml/resources/am-config.yaml @@ -0,0 +1,22 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + installationDir: "/tmp/am-resource-test/apps" + +quicklaunch: + runtimesPerContainer: 1 + idleLoad: 1.0 + +runtimes: + qml: + resources: "appcommon/appcommonfile.rcc" + importPaths: [ "qrc:/appcommon/qml", "relative" ] + quicklaunchQml: "qrc:/appcommon/Quicklaunch.qml" + +ui: + importPaths: "qrc:///qml" + resources: + - "systemuifile.rcc" + - "${CONFIG_PWD}/systemuiplugin" # libsystemuiplugin.so would only work on Linux diff --git a/tests/auto/qml/resources/appcommon/CMakeLists.txt b/tests/auto/qml/resources/appcommon/CMakeLists.txt new file mode 100644 index 00000000..45d12696 --- /dev/null +++ b/tests/auto/qml/resources/appcommon/CMakeLists.txt @@ -0,0 +1,2 @@ +# Generated from appcommon.pro. + diff --git a/tests/auto/qml/resources/appcommon/Quicklaunch.qml b/tests/auto/qml/resources/appcommon/Quicklaunch.qml new file mode 100644 index 00000000..e9cc4144 --- /dev/null +++ b/tests/auto/qml/resources/appcommon/Quicklaunch.qml @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 +import common 1.0 + +CommonObj { + Component.onCompleted: console.info("Quicklaunch - meaning: " + meaning); +} diff --git a/tests/auto/qml/resources/appcommon/appcommon.pro b/tests/auto/qml/resources/appcommon/appcommon.pro new file mode 100644 index 00000000..47028775 --- /dev/null +++ b/tests/auto/qml/resources/appcommon/appcommon.pro @@ -0,0 +1,7 @@ +TEMPLATE = aux + +OTHER_FILES += Quicklaunch.qml \ + qml/common/CommonObj.qml + +RESOURCE_SOURCE = appcommonfile.qrc +load(generate-resource) diff --git a/tests/auto/qml/resources/appcommon/appcommonfile.qrc b/tests/auto/qml/resources/appcommon/appcommonfile.qrc new file mode 100644 index 00000000..4dc15f85 --- /dev/null +++ b/tests/auto/qml/resources/appcommon/appcommonfile.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="appcommon"> + <file>qml/common/CommonObj.qml</file> + <file>qml/common/qmldir</file> + <file>Quicklaunch.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/resources/appcommon/qml/common/CommonObj.qml b/tests/auto/qml/resources/appcommon/qml/common/CommonObj.qml new file mode 100644 index 00000000..1b181488 --- /dev/null +++ b/tests/auto/qml/resources/appcommon/qml/common/CommonObj.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 + +QtObject { + property int meaning: 42 +} diff --git a/tests/auto/qml/resources/appcommon/qml/common/qmldir b/tests/auto/qml/resources/appcommon/qml/common/qmldir new file mode 100644 index 00000000..5458b257 --- /dev/null +++ b/tests/auto/qml/resources/appcommon/qml/common/qmldir @@ -0,0 +1,2 @@ +module common +CommonObj 1.0 CommonObj.qml diff --git a/tests/auto/qml/resources/apps/app1/CMakeLists.txt b/tests/auto/qml/resources/apps/app1/CMakeLists.txt new file mode 100644 index 00000000..7ff28b4a --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/CMakeLists.txt @@ -0,0 +1,32 @@ +# Generated from app1.pro. + +##################################################################### +## app1plugin Generic Library: +##################################################################### + +qt_internal_add_cmake_library(app1plugin + MODULE + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui +) + +# Resources: +set(app1plugin_resource_files + "qml/forms/AquaRect.qml" +) + +qt_internal_add_resource(app1plugin "app1plugin" + PREFIX + "/app1" + FILES + ${app1plugin_resource_files} +) + + +#### Keys ignored in scope 1:.:.:app1.pro:<TRUE>: +# OTHER_FILES = "info.yaml" "icon.png" +# RESOURCE_SOURCE = "app1file.qrc" +# TEMPLATE = "lib" + +qt_autogen_tools_initial_setup(app1plugin) diff --git a/tests/auto/qml/resources/apps/app1/app1.pro b/tests/auto/qml/resources/apps/app1/app1.pro new file mode 100644 index 00000000..cbbb4758 --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/app1.pro @@ -0,0 +1,11 @@ +TEMPLATE = lib +TARGET = app1plugin +CONFIG += plugin +RESOURCES = app1plugin.qrc + +RESOURCE_SOURCE = app1file.qrc +load(generate-resource) + +OTHER_FILES += \ + info.yaml \ + icon.png diff --git a/tests/auto/qml/resources/apps/app1/app1.qml b/tests/auto/qml/resources/apps/app1/app1.qml new file mode 100644 index 00000000..d519f3ac --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/app1.qml @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtApplicationManager.Application 2.0 +import forms 1.0 +import elements 1.0 + +ApplicationManagerWindow { + YellowRect {} + AquaRect {} + LinenRect {} +} diff --git a/tests/auto/qml/resources/apps/app1/app1file.qrc b/tests/auto/qml/resources/apps/app1/app1file.qrc new file mode 100644 index 00000000..6c958e89 --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/app1file.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/app1"> + <file>app1.qml</file> + <file>qml/forms/YellowRect.qml</file> + <file>qml/forms/qmldir</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/resources/apps/app1/app1plugin.qrc b/tests/auto/qml/resources/apps/app1/app1plugin.qrc new file mode 100644 index 00000000..6c4e2ae3 --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/app1plugin.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/app1"> + <file>qml/forms/AquaRect.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/resources/apps/app1/icon.png b/tests/auto/qml/resources/apps/app1/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/icon.png diff --git a/tests/auto/qml/resources/apps/app1/info.yaml b/tests/auto/qml/resources/apps/app1/info.yaml new file mode 100644 index 00000000..a5287f2c --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/info.yaml @@ -0,0 +1,17 @@ +formatVersion: 1 +formatType: am-package +--- +id: 'app1' +icon: 'icon.png' +name: + en: 'Resource Test App 1' + +applications: + - id: 'app1' + code: 'qrc:///app1/app1.qml' + runtime: 'qml' + runtimeParameters: + importPaths: "qrc:///app1/qml" + resources: + - app1file.rcc + - app1plugin # libapp1plugin.so would only work on Linux diff --git a/tests/auto/qml/resources/apps/app1/qml/forms/AquaRect.qml b/tests/auto/qml/resources/apps/app1/qml/forms/AquaRect.qml new file mode 100644 index 00000000..cefb485c --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/qml/forms/AquaRect.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 + +Rectangle { + color: "aqua" +} diff --git a/tests/auto/qml/resources/apps/app1/qml/forms/YellowRect.qml b/tests/auto/qml/resources/apps/app1/qml/forms/YellowRect.qml new file mode 100644 index 00000000..b1c91a1f --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/qml/forms/YellowRect.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 + +Rectangle { + color: "yellow" +} diff --git a/tests/auto/qml/resources/apps/app1/qml/forms/qmldir b/tests/auto/qml/resources/apps/app1/qml/forms/qmldir new file mode 100644 index 00000000..12a5506b --- /dev/null +++ b/tests/auto/qml/resources/apps/app1/qml/forms/qmldir @@ -0,0 +1,4 @@ +module forms +YellowRect 1.0 qrc:///app1/qml/forms/YellowRect.qml +# :/app1/qml/forms/YellowRect.qml would not work +AquaRect 1.0 AquaRect.qml diff --git a/tests/auto/qml/resources/apps/app2/BlueRect.qml b/tests/auto/qml/resources/apps/app2/BlueRect.qml new file mode 100644 index 00000000..138bdff4 --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/BlueRect.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 + +Rectangle { + color: "blue" +} diff --git a/tests/auto/qml/resources/apps/app2/CMakeLists.txt b/tests/auto/qml/resources/apps/app2/CMakeLists.txt new file mode 100644 index 00000000..03a5a5a1 --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/CMakeLists.txt @@ -0,0 +1,2 @@ +# Generated from app2.pro. + diff --git a/tests/auto/qml/resources/apps/app2/app2.pro b/tests/auto/qml/resources/apps/app2/app2.pro new file mode 100644 index 00000000..7a5190df --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/app2.pro @@ -0,0 +1,8 @@ +TEMPLATE = aux + +RESOURCE_SOURCE = app2.qrc +load(generate-resource) + +OTHER_FILES += \ + info.yaml \ + icon.png diff --git a/tests/auto/qml/resources/apps/app2/app2.qml b/tests/auto/qml/resources/apps/app2/app2.qml new file mode 100644 index 00000000..c0310ee0 --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/app2.qml @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtApplicationManager.Application 2.0 +import QtQuick 2.11 +import appwidgets 1.0 +import common 1.0 + +ApplicationManagerWindow { + CommonObj { id: common } + BlueRect { } + GreenRect { } + + Timer { + running: true + interval: 20 + onTriggered: setWindowProperty("meaning", common.meaning); + } +} diff --git a/tests/auto/qml/resources/apps/app2/app2.qrc b/tests/auto/qml/resources/apps/app2/app2.qrc new file mode 100644 index 00000000..80bc560c --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/app2.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>app2.qml</file> + <file>BlueRect.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/resources/apps/app2/icon.png b/tests/auto/qml/resources/apps/app2/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/icon.png diff --git a/tests/auto/qml/resources/apps/app2/info.yaml b/tests/auto/qml/resources/apps/app2/info.yaml new file mode 100644 index 00000000..6eda565b --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/info.yaml @@ -0,0 +1,15 @@ +formatVersion: 1 +formatType: am-package +--- +id: 'app2' +icon: 'icon.png' +name: + en: 'Resource Test App 2' + +applications: + - id: 'app2' + code: ':/app2.qml' + runtime: 'qml' + runtimeParameters: + importPaths: "qml" + resources: "app2.rcc" diff --git a/tests/auto/qml/resources/apps/app2/qml/appwidgets/GreenRect.qml b/tests/auto/qml/resources/apps/app2/qml/appwidgets/GreenRect.qml new file mode 100644 index 00000000..7bf8d3a3 --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/qml/appwidgets/GreenRect.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 + +Rectangle { + color: "green" +} diff --git a/tests/auto/qml/resources/apps/app2/qml/appwidgets/qmldir b/tests/auto/qml/resources/apps/app2/qml/appwidgets/qmldir new file mode 100644 index 00000000..a158bf25 --- /dev/null +++ b/tests/auto/qml/resources/apps/app2/qml/appwidgets/qmldir @@ -0,0 +1,2 @@ +module appwidgets +GreenRect 1.0 GreenRect.qml diff --git a/tests/auto/qml/resources/qml/widgets/MagentaRect.qml b/tests/auto/qml/resources/qml/widgets/MagentaRect.qml new file mode 100644 index 00000000..511cea95 --- /dev/null +++ b/tests/auto/qml/resources/qml/widgets/MagentaRect.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 + +Rectangle { + color: "magenta" +} diff --git a/tests/auto/qml/resources/qml/widgets/RedRect.qml b/tests/auto/qml/resources/qml/widgets/RedRect.qml new file mode 100644 index 00000000..a27122e7 --- /dev/null +++ b/tests/auto/qml/resources/qml/widgets/RedRect.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 + +Rectangle { + color: "red" +} diff --git a/tests/auto/qml/resources/qml/widgets/qmldir b/tests/auto/qml/resources/qml/widgets/qmldir new file mode 100644 index 00000000..be36132a --- /dev/null +++ b/tests/auto/qml/resources/qml/widgets/qmldir @@ -0,0 +1,3 @@ +module widgets +RedRect 1.0 RedRect.qml +MagentaRect 1.0 MagentaRect.qml diff --git a/tests/auto/qml/resources/relative/elements/LinenRect.qml b/tests/auto/qml/resources/relative/elements/LinenRect.qml new file mode 100644 index 00000000..1fdf9205 --- /dev/null +++ b/tests/auto/qml/resources/relative/elements/LinenRect.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2020 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 + +Rectangle { + color: "linen" +} diff --git a/tests/auto/qml/resources/relative/elements/qmldir b/tests/auto/qml/resources/relative/elements/qmldir new file mode 100644 index 00000000..16a01e45 --- /dev/null +++ b/tests/auto/qml/resources/relative/elements/qmldir @@ -0,0 +1,2 @@ +module elements +LinenRect 1.0 LinenRect.qml diff --git a/tests/auto/qml/resources/resources.pro b/tests/auto/qml/resources/resources.pro new file mode 100644 index 00000000..6da64f00 --- /dev/null +++ b/tests/auto/qml/resources/resources.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs +SUBDIRS = test.pro \ + appcommon \ + apps/app1 \ + apps/app2 diff --git a/tests/auto/qml/resources/systemuifile.qrc b/tests/auto/qml/resources/systemuifile.qrc new file mode 100644 index 00000000..ce48e1a4 --- /dev/null +++ b/tests/auto/qml/resources/systemuifile.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>qml/widgets/RedRect.qml</file> + <file>qml/widgets/qmldir</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/resources/systemuiplugin.qrc b/tests/auto/qml/resources/systemuiplugin.qrc new file mode 100644 index 00000000..7fe63a54 --- /dev/null +++ b/tests/auto/qml/resources/systemuiplugin.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>qml/widgets/MagentaRect.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/resources/test.pro b/tests/auto/qml/resources/test.pro new file mode 100644 index 00000000..3553c02a --- /dev/null +++ b/tests/auto/qml/resources/test.pro @@ -0,0 +1,18 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_resource.qml +TEST_APPS = app1 app2 + +DIRECTORIES = apps/app2/qml relative +FILES = am-config.yaml \ + apps/app1/icon.png apps/app1/info.yaml \ + apps/app2/icon.png apps/app2/info.yaml +DESTDIR = $$OUT_PWD +load(am-qml-testcase) + +RESOURCE_SOURCE = systemuifile.qrc +load(generate-resource) + +TEMPLATE = lib +TARGET = systemuiplugin +CONFIG += plugin +RESOURCES = systemuiplugin.qrc diff --git a/tests/auto/qml/resources/tst_resource.qml b/tests/auto/qml/resources/tst_resource.qml new file mode 100644 index 00000000..8eb0e406 --- /dev/null +++ b/tests/auto/qml/resources/tst_resource.qml @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 +import widgets 1.0 + +TestCase { + id: testCase + when: windowShown + name: "ResourceTest" + + RedRect {} + MagentaRect {} + + SignalSpy { +        id: runStateChangedSpy +        target: ApplicationManager +        signalName: "applicationRunStateChanged" +    } + + SignalSpy { + id: windowPropertyChangedSpy + target: WindowManager + signalName: "windowPropertyChanged" + } + + function test_basic_data() { + return [ { tag: "app1" }, + { tag: "app2" } ]; + } + + function test_basic(data) { + wait(1200); // wait for quicklaunch + + var app = ApplicationManager.application(data.tag); + windowPropertyChangedSpy.clear(); + + app.start(); + while (app.runState !== ApplicationObject.Running) + runStateChangedSpy.wait(3000); + + if (data.tag === "app2") { + windowPropertyChangedSpy.wait(2000); + compare(windowPropertyChangedSpy.count, 1); + compare(windowPropertyChangedSpy.signalArguments[0][0].windowProperty("meaning"), 42); + } + + app.stop(); + while (app.runState !== ApplicationObject.NotRunning) + runStateChangedSpy.wait(3000); + } +} diff --git a/tests/auto/qml/simple/CMakeLists.txt b/tests/auto/qml/simple/CMakeLists.txt new file mode 100644 index 00000000..9e793676 --- /dev/null +++ b/tests/auto/qml/simple/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from simple.pro. + +##################################################################### +## simple Binary: +##################################################################### + +qt_internal_add_executable(simple + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:simple.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "tld.test.simple1" "tld.test.simple2" +# TEST_FILES = "tst_applicationmanager.qml" diff --git a/tests/auto/qml/simple/am-config.yaml b/tests/auto/qml/simple/am-config.yaml new file mode 100644 index 00000000..2a515905 --- /dev/null +++ b/tests/auto/qml/simple/am-config.yaml @@ -0,0 +1,56 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +ui: + fullscreen: no + +systemProperties: + ignored: 42 + public: + pub1: 'pub1' + pubandpro: 'pub2' + pubandpri: 'pub3' + inall: 'public' + protected: + pro1: 'pro1' + pubandpro: 'pro2' + proandpri: 'pro4' + inall: 'protected' + nested: + level2: + level31: 'overwritten' + level32: 'hidden' + private: + booleanTest: on + stringTest: "pelagicore" + nullTest: ~ + intTest: -1 + floatTest: .5 + arrayTest: [ + "value1", + "value2" + ] + mapTest: { + "key1" : "1", + "key2" : "2" + } + nested: + level2: + level31: 31 + level21: 21 + level22: 22 + pubandpri: 'pri3' + proandpri: 'pri4' + inall: 'private' + +# development setup: +flags: + noSecurity: yes + noUiWatchdog: yes + +runtimes: + qml: + quitTime: 5000 diff --git a/tests/auto/qml/simple/apps/tld.test.simple1/app1.qml b/tests/auto/qml/simple/apps/tld.test.simple1/app1.qml new file mode 100644 index 00000000..11c7ad78 --- /dev/null +++ b/tests/auto/qml/simple/apps/tld.test.simple1/app1.qml @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + id: root + + Rectangle { + anchors.centerIn: parent + width: 180; height: 180; radius: width/4 + color: "red" + } + + Connections { + target: ApplicationInterface + function onQuit() { + target.acknowledgeQuit(); + } + } +} diff --git a/tests/auto/qml/simple/apps/tld.test.simple1/icon.png b/tests/auto/qml/simple/apps/tld.test.simple1/icon.png Binary files differnew file mode 100644 index 00000000..adb840ce --- /dev/null +++ b/tests/auto/qml/simple/apps/tld.test.simple1/icon.png diff --git a/tests/auto/qml/simple/apps/tld.test.simple1/info.yaml b/tests/auto/qml/simple/apps/tld.test.simple1/info.yaml new file mode 100644 index 00000000..49a0e1e4 --- /dev/null +++ b/tests/auto/qml/simple/apps/tld.test.simple1/info.yaml @@ -0,0 +1,23 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.test.simple1' +icon: 'icon.png' +code: 'app1.qml' +runtime: 'qml' +version: '1.0' +name: + en: 'Simple1' +applicationProperties: + ignored: 42 + protected: + pro1: 'pro1' + proandpri: 'pro2' + private: + pri1: 'pri1' + proandpri: 'pri2' + +mimeTypes: [ + "x-scheme-handler/x-test", + "text/plain" + ] diff --git a/tests/auto/qml/simple/apps/tld.test.simple2/app.qml b/tests/auto/qml/simple/apps/tld.test.simple2/app.qml new file mode 100644 index 00000000..4f046770 --- /dev/null +++ b/tests/auto/qml/simple/apps/tld.test.simple2/app.qml @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + id: root + + Rectangle { + anchors.centerIn: parent + width: 180; height: 180; radius: width/4 + color: "red" + } + + Connections { + target: ApplicationInterface + function onQuit() { + //Do nothing, so we get killed by appman + } + } +} diff --git a/tests/auto/qml/simple/apps/tld.test.simple2/icon.png b/tests/auto/qml/simple/apps/tld.test.simple2/icon.png Binary files differnew file mode 100644 index 00000000..adb840ce --- /dev/null +++ b/tests/auto/qml/simple/apps/tld.test.simple2/icon.png diff --git a/tests/auto/qml/simple/apps/tld.test.simple2/info.yaml b/tests/auto/qml/simple/apps/tld.test.simple2/info.yaml new file mode 100644 index 00000000..f3366712 --- /dev/null +++ b/tests/auto/qml/simple/apps/tld.test.simple2/info.yaml @@ -0,0 +1,13 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.test.simple2' +icon: 'icon.png' +code: 'app.qml' +runtime: 'qml' +name: + en: 'Caps' + +capabilities: +- cameraAccess +- locationAccess diff --git a/tests/auto/qml/simple/simple.pro b/tests/auto/qml/simple/simple.pro new file mode 100644 index 00000000..72dbea95 --- /dev/null +++ b/tests/auto/qml/simple/simple.pro @@ -0,0 +1,5 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_applicationmanager.qml +TEST_APPS = tld.test.simple1 tld.test.simple2 + +load(am-qml-testcase) diff --git a/tests/auto/qml/simple/text-file.txt b/tests/auto/qml/simple/text-file.txt new file mode 100644 index 00000000..0a05cde5 --- /dev/null +++ b/tests/auto/qml/simple/text-file.txt @@ -0,0 +1 @@ +mimeType test diff --git a/tests/auto/qml/simple/tst_applicationmanager.qml b/tests/auto/qml/simple/tst_applicationmanager.qml new file mode 100644 index 00000000..ce3db553 --- /dev/null +++ b/tests/auto/qml/simple/tst_applicationmanager.qml @@ -0,0 +1,487 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Window 2.0 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +TestCase { + id: testCase + when: windowShown + name: "ApplicationManager" + + property var simpleApplication + property var capsApplication + // Either appman is build in single-process mode or it was started with --force-single-process + property bool singleProcess : Qt.application.arguments.indexOf("--force-single-process") !== -1 + || buildConfig[0].CONFIG.indexOf("multi-process") === -1 + property QtObject windowHandler: QtObject { + function windowAddedHandler(window) { + // console.info("window " + window + " added"); + } + + function windowContentStateChangedHandler(window) { + // console.info("window content state = " + window.contentState); + } + } + + ListView { + id: listView + model: ApplicationManager + delegate: Item { + property var modelData: model + } + } + + ApplicationModel { + id: appModel + sortFunction: function(la, ra) { return la.id > ra.id } + } + + function initTestCase() { + //Wait for the debugging wrappers to be setup. + wait(2000); + WindowManager.windowAdded.connect(windowHandler.windowAddedHandler) + WindowManager.windowContentStateChanged.connect(windowHandler.windowContentStateChangedHandler) + + compare(ApplicationManager.count, 2) + simpleApplication = ApplicationManager.application(0); + capsApplication = ApplicationManager.application(1); + } + + function test_properties() { + // Only true for the dummyimports + compare(ApplicationManager.dummy, false) + // Disabled in the am-config.yaml + compare(ApplicationManager.securityChecksEnabled, false) + + compare(ApplicationManager.singleProcess, singleProcess) + } + + function test_systemProperties() { + compare(ApplicationManager.systemProperties.ignored, undefined) + compare(ApplicationManager.systemProperties.booleanTest, true) + compare(ApplicationManager.systemProperties.stringTest, "pelagicore") + compare(ApplicationManager.systemProperties.intTest, -1) + compare(ApplicationManager.systemProperties.floatTest, .5) + compare(ApplicationManager.systemProperties.arrayTest[0], "value1") + compare(ApplicationManager.systemProperties.arrayTest[1], "value2") + compare(ApplicationManager.systemProperties.mapTest["key1"], "1") + compare(ApplicationManager.systemProperties.mapTest["key2"], "2") + compare(ApplicationManager.systemProperties.nested.level21, 21) + compare(ApplicationManager.systemProperties.nested.level2.level31, 31) + compare(ApplicationManager.systemProperties.nested.level2.level32, undefined) + compare(ApplicationManager.systemProperties.nested.level21, 21) + compare(ApplicationManager.systemProperties.nested.level22, 22) + compare(ApplicationManager.systemProperties.pub1, 'pub1') + compare(ApplicationManager.systemProperties.pro1, 'pro1') + compare(ApplicationManager.systemProperties.pubandpro, 'pro2') + compare(ApplicationManager.systemProperties.pubandpri, 'pri3') + compare(ApplicationManager.systemProperties.proandpri, 'pri4') + compare(ApplicationManager.systemProperties.inall, 'private') + compare(ApplicationManager.systemProperties.nullTest, null) + } + + function test_package() { + compare(PackageManager.count, 2) + verify(simpleApplication.package !== capsApplication.package) + compare(simpleApplication.package, PackageManager.package("tld.test.simple1")) + compare(simpleApplication.package.id, "tld.test.simple1") + compare(simpleApplication.package.applications.length, 1) + compare(simpleApplication.package.applications[0], simpleApplication) + } + + function test_application() { + var id = simpleApplication.id; + compare(simpleApplication.id, "tld.test.simple1") + compare(simpleApplication.runtimeName, "qml") + compare(simpleApplication.icon.toString(), Qt.resolvedUrl("apps/tld.test.simple1/icon.png")) + compare(simpleApplication.documentUrl, "") + compare(simpleApplication.builtIn, true) + compare(simpleApplication.alias, false) + compare(simpleApplication.nonAliased, simpleApplication) + compare(simpleApplication.capabilities.length, 0) + compare(simpleApplication.supportedMimeTypes.length, 2) + compare(simpleApplication.categories.length, 0) + //Why is runtime null ? we should document this, as this is not really clear + compare(simpleApplication.runtime, null) + compare(simpleApplication.lastExitCode, 0) + compare(simpleApplication.lastExitStatus, Am.NormalExit) + compare(simpleApplication.version, "1.0") + + // Test the name getter and verify that it's returning the same object + compare(simpleApplication, ApplicationManager.application(id)) + } + + function test_applicationProperties() { + compare(simpleApplication.applicationProperties.ignored, undefined) + compare(simpleApplication.applicationProperties.pro1, "pro1") + compare(simpleApplication.applicationProperties.proandpri, "pro2") + compare(simpleApplication.applicationProperties.pri1, undefined) + } + + function test_indexOfApplication() { + // Test index of + compare(ApplicationManager.indexOfApplication(simpleApplication.id), 0) + compare(ApplicationManager.indexOfApplication(capsApplication.id), 1) + compare(ApplicationManager.indexOfApplication("error"), -1) + } + + function test_applicationIds() { + // Test app ids + var apps = ApplicationManager.applicationIds() + compare(apps.length, ApplicationManager.count) + compare(ApplicationManager.applicationIds()[0], simpleApplication.id) + compare(ApplicationManager.applicationIds()[1], capsApplication.id) + } + + function test_capabilities() { + var emptyCaps = ApplicationManager.capabilities(simpleApplication.id) + compare(emptyCaps.length, 0) + var caps = ApplicationManager.capabilities(capsApplication.id) + compare(caps.length, 2) + } + + function test_modelData() { + compare(listView.count, ApplicationManager.count) + listView.currentIndex = 0; + compare(listView.currentItem.modelData.application, simpleApplication) + compare(listView.currentItem.modelData.applicationId, simpleApplication.id) + compare(listView.currentItem.modelData.name, "Simple1") + compare(listView.currentItem.modelData.icon.toString(), Qt.resolvedUrl(simpleApplication.icon)) + compare(listView.currentItem.modelData.runtimeName, "qml") + compare(listView.currentItem.modelData.isRunning, false) + compare(listView.currentItem.modelData.isStartingUp, false) + compare(listView.currentItem.modelData.isShuttingDown, false) + compare(listView.currentItem.modelData.isBlocked, false) + compare(listView.currentItem.modelData.isUpdating, false) + compare(listView.currentItem.modelData.isRemovable, false) + compare(listView.currentItem.modelData.updateProgress, 0.0) + verify(listView.currentItem.modelData.codeFilePath.indexOf("apps/tld.test.simple1/app1.qml") !== -1) + compare(listView.currentItem.modelData.capabilities, simpleApplication.capabilities) + compare(listView.currentItem.modelData.version, "1.0") + } + + SignalSpy { + id: appModelCountSpy + target: appModel + signalName: "countChanged" + } + + function test_applicationModel() { + compare(appModel.count, 2); + compare(appModel.indexOfApplication(capsApplication.id), 0); + compare(appModel.indexOfApplication(simpleApplication.id), 1); + compare(appModel.mapToSource(0), ApplicationManager.indexOfApplication(capsApplication.id)); + compare(appModel.mapFromSource(ApplicationManager.indexOfApplication(simpleApplication.id)), 1); + + appModel.sortFunction = undefined; + compare(appModel.indexOfApplication(simpleApplication.id), + ApplicationManager.indexOfApplication(simpleApplication.id)); + compare(appModel.indexOfApplication(capsApplication.id), + ApplicationManager.indexOfApplication(capsApplication.id)); + + appModelCountSpy.clear(); + appModel.filterFunction = function(app) { return app.capabilities.indexOf("cameraAccess") >= 0; }; + appModelCountSpy.wait(1000); + compare(appModelCountSpy.count, 1); + compare(appModel.count, 1); + compare(appModel.indexOfApplication(capsApplication.id), 0); + compare(appModel.indexOfApplication(simpleApplication.id), -1); + + listView.model = appModel; + listView.currentIndex = 0; + compare(listView.currentItem.modelData.name, "Caps"); + listView.model = ApplicationManager; + + appModel.filterFunction = function() {}; + compare(appModel.count, 0); + + appModel.filterFunction = undefined; + compare(appModel.count, 2); + } + + function test_get_data() { + return [ + {tag: "get(row)", argument: 0 }, + {tag: "get(id)", argument: simpleApplication.id }, + ]; + } + + function test_get(data) { + var appData = ApplicationManager.get(data.argument); + + compare(appData.application, simpleApplication) + compare(appData.applicationId, simpleApplication.id) + compare(appData.name, "Simple1") + compare(appData.icon.toString(), Qt.resolvedUrl(simpleApplication.icon)) + compare(appData.runtimeName, "qml") + compare(appData.isRunning, false) + compare(appData.isStartingUp, false) + compare(appData.isShuttingDown, false) + compare(appData.isBlocked, false) + compare(appData.isUpdating, false) + compare(appData.isRemovable, false) + compare(appData.updateProgress, 0.0) + verify(appData.codeFilePath.indexOf("apps/tld.test.simple1/app1.qml") !== -1) + compare(appData.capabilities, simpleApplication.capabilities) + compare(appData.version, "1.0") + } + + function test_application_object_ownership() { + // Check that the returned Application is not owned by javascript and deleted + // by the garbage collector + var app = ApplicationManager.application(0); + var id = app.id + app = null; + // app gets deleted now as there is now javascript reference anymore + gc() + // Test that the Application in the ApplicationManager is still valid + // by accessing one of it's properties + compare(id, ApplicationManager.application(0).id) + } + + SignalSpy { + id: runStateChangedSpy + target: ApplicationManager + signalName: "applicationRunStateChanged" + } + + function checkApplicationState(id, state) { + if (runStateChangedSpy.count < 1) + runStateChangedSpy.wait(10000); + verify(runStateChangedSpy.count) + compare(runStateChangedSpy.signalArguments[0][0], id) + compare(runStateChangedSpy.signalArguments[0][1], state) + compare(ApplicationManager.application(id).runState, state); + runStateChangedSpy.clear(); + } + + function test_startAndStopApplication_data() { + return [ + {tag: "StartStop", appId: "tld.test.simple1", index: 0, forceKill: false, + exitCode: 0, exitStatus: Am.NormalExit }, + {tag: "Debug", appId: "tld.test.simple1", index: 0, forceKill: false, + exitCode: 0, exitStatus: Am.NormalExit }, + {tag: "ForceKill", appId: "tld.test.simple2", index: 1, forceKill: true, + exitCode: Qt.platform.os !== 'windows' ? 9 : 0, + exitStatus: Qt.platform.os !== 'windows' ? Am.ForcedExit : Am.CrashExit }, + {tag: "AutoTerminate", appId: "tld.test.simple2", index: 1, forceKill: false, + exitCode: Qt.platform.os !== 'windows' ? 15 : 0, + exitStatus: Qt.platform.os !== 'windows' ? Am.ForcedExit : Am.CrashExit } + ]; + } + + function test_startAndStopApplication(data) { + compare(ApplicationManager.application(data.appId).runState, Am.NotRunning); + + var started = false; + if (data.tag === "Debug") { + if (singleProcess) + ignoreWarning("Using debug-wrappers is not supported when the application manager is running in single-process mode."); + started = ApplicationManager.debugApplication(data.appId, "%program% %arguments%"); + if (singleProcess) { + verify(!started); + return; + } + } else { + started = ApplicationManager.startApplication(data.appId); + } + verify(started); + + checkApplicationState(data.appId, Am.StartingUp); + listView.currentIndex = data.index; + compare(listView.currentItem.modelData.isStartingUp, true) + compare(listView.currentItem.modelData.isRunning, false) + compare(listView.currentItem.modelData.isShuttingDown, false) + checkApplicationState(data.appId, Am.Running); + compare(listView.currentItem.modelData.isStartingUp, false) + compare(listView.currentItem.modelData.isRunning, true) + compare(listView.currentItem.modelData.isShuttingDown, false) + + ApplicationManager.stopApplication(data.appId, data.forceKill); + + checkApplicationState(data.appId, Am.ShuttingDown); + compare(listView.currentItem.modelData.isStartingUp, false) + compare(listView.currentItem.modelData.isRunning, false) + compare(listView.currentItem.modelData.isShuttingDown, true) + checkApplicationState(data.appId, Am.NotRunning); + compare(listView.currentItem.modelData.isStartingUp, false) + compare(listView.currentItem.modelData.isRunning, false) + compare(listView.currentItem.modelData.isShuttingDown, false) + compare(listView.currentItem.modelData.application.lastExitCode, data.exitCode) + compare(listView.currentItem.modelData.application.lastExitStatus, data.exitStatus) + } + + function test_startAndStopAllApplications_data() { + return [ + {tag: "StopAllApplications", appId1: "tld.test.simple1", index1: 0, + appId2: "tld.test.simple2", index2: 1, forceKill: false, exitCode: 0, + exitStatus: Am.NormalExit } + ]; + } + + function test_startAndStopAllApplications(data) { + compare(ApplicationManager.application(data.appId1).runState, Am.NotRunning); + compare(ApplicationManager.application(data.appId2).runState, Am.NotRunning); + + var started = false; + + started = ApplicationManager.startApplication(data.appId1); + verify(started); + + + checkApplicationState(data.appId1, Am.StartingUp); + listView.currentIndex = data.index1; + compare(listView.currentItem.modelData.isStartingUp, true) + compare(listView.currentItem.modelData.isRunning, false) + compare(listView.currentItem.modelData.isShuttingDown, false) + checkApplicationState(data.appId1, Am.Running); + compare(listView.currentItem.modelData.isStartingUp, false) + compare(listView.currentItem.modelData.isRunning, true) + compare(listView.currentItem.modelData.isShuttingDown, false) + + started = ApplicationManager.startApplication(data.appId2); + verify(started); + + checkApplicationState(data.appId2, Am.StartingUp); + listView.currentIndex = data.index2; + compare(listView.currentItem.modelData.isStartingUp, true) + compare(listView.currentItem.modelData.isRunning, false) + compare(listView.currentItem.modelData.isShuttingDown, false) + checkApplicationState(data.appId2, Am.Running); + compare(listView.currentItem.modelData.isStartingUp, false) + compare(listView.currentItem.modelData.isRunning, true) + compare(listView.currentItem.modelData.isShuttingDown, false) + + ApplicationManager.stopAllApplications(data.forceKill); + + while (runStateChangedSpy.count < 4) + runStateChangedSpy.wait(10000); + + var args = runStateChangedSpy.signalArguments + + for (var i = 0; i < 4; ++i) { + var id = args[i][0] + var state = args[i][1] + + var atPos = id.indexOf('@') + if (atPos >= 0) + id = id.substring(0, atPos) + + // not perfect, but the basic signal sequence is already tested in test_startAndStopApplication + verify(id === data.appId1 || id === data.appId2, "id = " + id) + verify(state === Am.ShuttingDown || state === Am.NotRunning) + } + runStateChangedSpy.clear() + } + + function test_errors() { + ignoreWarning("ApplicationManager::application(index): invalid index: -1"); + verify(!ApplicationManager.application(-1)); + verify(!ApplicationManager.application("invalidApplication")); + ignoreWarning("ApplicationManager::get(index): invalid index: -1"); + compare(ApplicationManager.get(-1), {}); + compare(ApplicationManager.get("invalidApplication"), {}); + compare(ApplicationManager.applicationRunState("invalidApplication"), Am.NotRunning); + + ignoreWarning("Cannot start application: id 'invalidApplication' is not known"); + verify(!ApplicationManager.startApplication("invalidApplication")) + + //All following tests don't work in single-process mode + if (singleProcess) + return; + + ignoreWarning("Tried to start application tld.test.simple1 using an invalid debug-wrapper specification: "); + verify(!ApplicationManager.debugApplication(simpleApplication.id, " ")) + + verify(ApplicationManager.startApplication(simpleApplication.id)); + checkApplicationState(simpleApplication.id, Am.StartingUp); + checkApplicationState(simpleApplication.id, Am.Running); + ignoreWarning("Application tld.test.simple1 is already running - cannot start with debug-wrapper: %program% %arguments%"); + verify(!ApplicationManager.debugApplication(simpleApplication.id, "%program% %arguments%")) + ApplicationManager.stopApplication(simpleApplication.id, true); + checkApplicationState(simpleApplication.id, Am.ShuttingDown); + checkApplicationState(simpleApplication.id, Am.NotRunning); + } + + function test_openUrl_data() { + return [ + {tag: "customMimeType", url: "x-test://12345", expectedApp: simpleApplication.id }, + {tag: "text/plain", url: "file://text-file.txt", expectedApp: simpleApplication.id } + ]; + } + + function test_openUrl(data) { + verify(ApplicationManager.openUrl(data.url)); + checkApplicationState(data.expectedApp, Am.StartingUp); + checkApplicationState(data.expectedApp, Am.Running); + ApplicationManager.stopApplication(data.expectedApp, true); + checkApplicationState(data.expectedApp, Am.ShuttingDown); + checkApplicationState(data.expectedApp, Am.NotRunning); + + Qt.openUrlExternally(data.url); + checkApplicationState(data.expectedApp, Am.StartingUp); + checkApplicationState(data.expectedApp, Am.Running); + ApplicationManager.stopApplication(data.expectedApp, true); + checkApplicationState(data.expectedApp, Am.ShuttingDown); + checkApplicationState(data.expectedApp, Am.NotRunning); + } + + property bool containerSelectionCalled: false + property string containerSelectionAppId + property string containerSelectionConId + function containerSelection(appId, containerId) { + containerSelectionCalled = true; + containerSelectionAppId = appId; + containerSelectionConId = containerId; + + return containerId; + } + + function test_containerSelectionFunction() { + if (singleProcess) + skip("The containerSelectionFunction doesn't work in single-process mode"); + + compare(ApplicationManager.containerSelectionFunction, undefined); + ApplicationManager.containerSelectionFunction = containerSelection; + ApplicationManager.startApplication(simpleApplication.id); + checkApplicationState(simpleApplication.id, Am.StartingUp); + checkApplicationState(simpleApplication.id, Am.Running); + ApplicationManager.stopApplication(simpleApplication.id, true); + checkApplicationState(simpleApplication.id, Am.ShuttingDown); + checkApplicationState(simpleApplication.id, Am.NotRunning); + verify(containerSelectionCalled); + compare(containerSelectionAppId, simpleApplication.id); + compare(containerSelectionConId, "process"); + } +} diff --git a/tests/auto/qml/windowitem/CMakeLists.txt b/tests/auto/qml/windowitem/CMakeLists.txt new file mode 100644 index 00000000..271566ee --- /dev/null +++ b/tests/auto/qml/windowitem/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from windowitem.pro. + +##################################################################### +## windowitem Binary: +##################################################################### + +qt_internal_add_executable(windowitem + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:windowitem.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "test.windowitem.app" "test.windowitem.multiwin" +# TEST_FILES = "tst_windowitem.qml" diff --git a/tests/auto/qml/windowitem/am-config.yaml b/tests/auto/qml/windowitem/am-config.yaml new file mode 100644 index 00000000..aa82540b --- /dev/null +++ b/tests/auto/qml/windowitem/am-config.yaml @@ -0,0 +1,13 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + installationDir: "/tmp/am/apps" + documentDir: "/tmp/am/docs" + +# Workaround for a crash in the mesa software renderer (llvmpipe) +runtimes: + qml: + environmentVariables: + QT_QUICK_BACKEND: "software" diff --git a/tests/auto/qml/windowitem/apps/test.windowitem.app/icon.png b/tests/auto/qml/windowitem/apps/test.windowitem.app/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowitem/apps/test.windowitem.app/icon.png diff --git a/tests/auto/qml/windowitem/apps/test.windowitem.app/info.yaml b/tests/auto/qml/windowitem/apps/test.windowitem.app/info.yaml new file mode 100644 index 00000000..1ef6fd93 --- /dev/null +++ b/tests/auto/qml/windowitem/apps/test.windowitem.app/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.windowitem.app' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'WindowItem Test App' diff --git a/tests/auto/qml/windowitem/apps/test.windowitem.app/main.qml b/tests/auto/qml/windowitem/apps/test.windowitem.app/main.qml new file mode 100644 index 00000000..0fec1364 --- /dev/null +++ b/tests/auto/qml/windowitem/apps/test.windowitem.app/main.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + id: root + color: "red" + + width: 123 + height: 321 + + MouseArea { + id: mouseArea + property int clickCount: 0 + anchors.fill: parent + onClicked: { + clickCount += 1; + root.setWindowProperty("clickCount", clickCount); + } + } + + // A way for test code to trigger ApplicationManagerWindow's size changes from + // the client side + onWindowPropertyChanged: { + if (name === "requestedWidth") + root.width = value; + else if (name === "requestedHeight") + root.height = value; + } + + Component.onCompleted: { + root.setWindowProperty("clickCount", mouseArea.clickCount); + } +} diff --git a/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/icon.png b/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/icon.png diff --git a/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/info.yaml b/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/info.yaml new file mode 100644 index 00000000..2c44d5d3 --- /dev/null +++ b/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.windowitem.multiwin' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'Multiple Windows Test App' diff --git a/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/main.qml b/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/main.qml new file mode 100644 index 00000000..c3e0607d --- /dev/null +++ b/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/main.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +/* + A simple test app that displays two windows +*/ +QtObject { + property var blueWindow: ApplicationManagerWindow { + color: "blue" + width: 123 + height: 321 + } + property var orangeWindow: ApplicationManagerWindow { + color: "orange" + width: 321 + height: 123 + } +} diff --git a/tests/auto/qml/windowitem/tst_windowitem.qml b/tests/auto/qml/windowitem/tst_windowitem.qml new file mode 100644 index 00000000..1ad7d319 --- /dev/null +++ b/tests/auto/qml/windowitem/tst_windowitem.qml @@ -0,0 +1,482 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +Item { + id: root + width: 500 + height: 500 + visible: true + + Repeater { + id: windowItemsRepeater + model: ListModel { id: windowItemsModel } + delegate: WindowItem { + id: windowItem + window: model.window + + property int clickCount: 0 + property alias mouseAreaVisible: mouseArea.visible + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: windowItem.clickCount += 1; + z: -100 // some arbitrary, negative, Z + } + } + } + Repeater { + id: sizedWindowItemsRepeater + model: ListModel { id: sizedWindowItemsModel } + delegate: WindowItem { + width: 200 + height: 100 + window: model.window + } + } + Repeater { + id: noResizeWindowItemsRepeater + model: ListModel { id: noResizeWindowItemsModel } + delegate: WindowItem { + width: 200 + height: 100 + objectFollowsItemSize: false + window: model.window + } + } + property var chosenModel + Connections { + target: WindowManager + function onWindowAdded(window) { + root.chosenModel.append({"window":window}); + } + } + + // Force redraws so that pings and other events are quickly processed between + // wayland client and server. + Rectangle { + width: 10 + height: 10 + color: "brown" + RotationAnimator on rotation { + from: 0; to: 360; duration: 1000 + loops: Animation.Infinite + running: true + } + } + + SignalSpy { +        id: objectDestroyedSpy +        target: AmTest +        signalName: "objectDestroyed" +    } + + TestCase { + id: testCase + when: windowShown + name: "WindowItem" + + property var app: null + + function init() { + compare(windowItemsModel.count, 0); + compare(sizedWindowItemsModel.count, 0); + compare(noResizeWindowItemsRepeater.count, 0); + compare(WindowManager.count, 0); + } + + function cleanup() { + windowItemsModel.clear(); + sizedWindowItemsModel.clear(); + noResizeWindowItemsModel.clear(); + + if (app) + app.stop(); + + waitUntilAllAppsAreStopped(); + } + + function waitUntilAllAppsAreStopped() { + while (true) { + var numRunningApps = 0; + for (var i = 0; i < ApplicationManager.count; i++) { + var app = ApplicationManager.application(i); + if (app.runState !== Am.NotRunning) + numRunningApps += 1; + } + + if (numRunningApps > 0) { + wait(50); + } else + break; + } + } + + function initWindowItemsModel() { + root.chosenModel = windowItemsModel; + startAppAndCheckWindow(); + } + + function initSizedWindowItemsModel() { + root.chosenModel = sizedWindowItemsModel; + startAppAndCheckWindow(); + } + + function startAppAndCheckWindow() { + app = ApplicationManager.application("test.windowitem.app"); + app.start(); + + tryCompare(root.chosenModel, "count", 1); + tryCompare(WindowManager, "count", 1); + } + + /* + The first WindowItem showing a window have primary==true, from the + second onwards they should have primary==false + */ + function test_onlyFirstItemIsPrimary() { + initWindowItemsModel(); + var firstWindowItem = windowItemsRepeater.itemAt(0); + compare(firstWindowItem.primary, true); + + // Add a second view for the same WindowObject + windowItemsModel.append({"window":firstWindowItem.window}); + + var secondWindowItem = windowItemsRepeater.itemAt(1); + compare(secondWindowItem.primary, false); + } + + /* + Turn a secondary WindowItem (primary == false) into the primary one. + + Check that the previously primary is now secondary and vice-versa. + */ + function test_turnSecondaryIntoPrimary() { + initWindowItemsModel(); + var firstWindowItem = windowItemsRepeater.itemAt(0); + + // Add a second view for the same WindowObject + windowItemsModel.append({"window":firstWindowItem.window}); + + var secondWindowItem = windowItemsRepeater.itemAt(1); + + compare(firstWindowItem.primary, true); + compare(secondWindowItem.primary, false); + + // give primary role to the second WindowItem + secondWindowItem.makePrimary(); + + compare(firstWindowItem.primary, false); + compare(secondWindowItem.primary, true); + + // and take it back + firstWindowItem.makePrimary(); + + compare(firstWindowItem.primary, true); + compare(secondWindowItem.primary, false); + } + + /* + You have two WindowItems for the same WindowObject + + Check that once the primary WindowItem is destroyed, + the remaining one takes over the primary role. + */ + function test_destroyPrimaryRemainingTakesOver() { + initWindowItemsModel(); + var firstWindowItem = windowItemsRepeater.itemAt(0); + + // Add a second view for the same WindowObject + windowItemsModel.append({"window":firstWindowItem.window}); + + var secondWindowItem = windowItemsRepeater.itemAt(1); + + compare(firstWindowItem.primary, true); + compare(secondWindowItem.primary, false); + + compare(windowItemsModel.count, 2); + + // destroy the first WindowItem + windowItemsModel.remove(0 /*index*/, 1 /*count*/); + firstWindowItem = null; + + compare(windowItemsModel.count, 1); + + // And the remaining item takes over the primary role. + tryCompare(secondWindowItem, "primary", true); + } + + /* + Check that a WindowObject with state NoSurface is destroyed + only once all WindowItems using it are gone. + */ + function test_surfacelessObjectStaysUntilAllItemsAreGone() { + initWindowItemsModel(); + var firstWindowItem = windowItemsRepeater.itemAt(0); + + // Add a second view for the same WindowObject + windowItemsModel.append({"window":firstWindowItem.window}); + + var secondWindowItem = windowItemsRepeater.itemAt(1); + + var window = WindowManager.get(0).window; + objectDestroyedSpy.clear(); + var destroyId = AmTest.observeObjectDestroyed(window); + + compare(window.contentState, WindowObject.SurfaceWithContent); + app.stop(); + + // The WindowObject should still exist, albeit without a surface, even though + // no longer present in WindowManager's model. + tryCompare(WindowManager, "count", 0); + compare(objectDestroyedSpy.count, 0) + tryCompare(window, "contentState", WindowObject.NoSurface); + + // Destroy all WindowItems + firstWindowItem = null; + secondWindowItem = null; + windowItemsModel.clear(); + + // Now that there are no WindowItems using that WindowObject anymore, it should + // eventually be deleted by WindowManager + objectDestroyedSpy.wait(); + compare(objectDestroyedSpy.signalArguments[0][0], destroyId); + } + + /* + Checks that the implicit size of a WindowItem matches the explicit size of the client's ApplicationManagerWindow + */ + function test_implicitSize() { + initWindowItemsModel(); + var windowItem = windowItemsRepeater.itemAt(0); + var window = windowItem.window + + // Must match the ApplicationManagerWindow size defined in apps/test.windowitem.app/mail.qml + compare(window.size.width, 123); + compare(window.size.height, 321); + compare(windowItem.width, 123); + compare(windowItem.height, 321); + + // Avoid a race condition where the item's implicit size (and therefore the item's + // size itself as no explicit width or height was assigned) would be set to match + // the window's size while at the same time an item's resize would make the item resize the window. + // In short: item size depending on window size while at the same time window size is + // depending on item size. + windowItem.objectFollowsItemSize = false; + + var width = 130; + var height = 330; + var i; + for (i = 0; i < 20; i += 1) { + window.setWindowProperty("requestedWidth", width); + window.setWindowProperty("requestedHeight", height); + + tryCompare(window, "size", Qt.size(width,height)); + tryCompare(windowItem, "width", width); + tryCompare(windowItem, "height", height); + + width += 5; + height += 5; + wait(10); + } + } + + /* + Checks that once a Window is assinged to a WindowItem its underlying surface + gets resized to match that WindowItem's size (considering it's the first, + primary, one) + */ + function test_initialResize() { + initSizedWindowItemsModel(); + var windowItem = sizedWindowItemsRepeater.itemAt(0); + var window = windowItem.window + + tryCompare(window, "size", Qt.size(windowItem.width, windowItem.height)); + } + + /* + By default a WindowItem will resize the WindowObject it's displaying to match his own size. + So resizing a WindowItem will cause his WindowObject to follow suit, so that both always + have matching sizes. + */ + function test_objectFollowsItemSize() { + initSizedWindowItemsModel(); + var windowItem = sizedWindowItemsRepeater.itemAt(0); + var window = windowItem.window; + + windowItem.width = 200; + windowItem.height = 100; + tryCompare(window, "size", Qt.size(200, 100)); + + windowItem.width = 201; + windowItem.height = 101; + tryCompare(window, "size", Qt.size(201, 101)); + + windowItem.width = 202; + windowItem.height = 102; + tryCompare(window, "size", Qt.size(202, 102)); + } + + /* + When WindowItem.objectFollowsItemSize is false, resizing the WindowItem will have no effect + over the WindowObject's size. + */ + function test_windowDoesNotFolowItemSize() { + root.chosenModel = noResizeWindowItemsModel; + startAppAndCheckWindow(); + + var windowItem = noResizeWindowItemsRepeater.itemAt(0); + var window = windowItem.window; + + windowItem.width = 200; + windowItem.height = 100; + tryCompare(window, "size", Qt.size(123, 321)); + + windowItem.width = 201; + windowItem.height = 101; + tryCompare(window, "size", Qt.size(123, 321)); + + windowItem.width = 202; + windowItem.height = 102; + tryCompare(window, "size", Qt.size(123, 321)); + } + + /* + Checks that calling close() on an empty WindowObject won't cause a crash (particularly + in multi-process) + */ + function test_closeEmptyWindow() { + initWindowItemsModel(); + + var windowItem = windowItemsRepeater.itemAt(0); + var window = windowItem.window; + + app.stop(); + + tryCompare(window, "contentState", WindowObject.NoSurface); + + window.close(); + } + + /* + Regression test for https://bugreports.qt.io/browse/AUTOSUITE-652 + + - Start an application that has two windows. + - Call close() on the first one. It should vanish. Application should keep running normally. + - Call close() on the second one. It should vanish as well and, being the app's last window, it should + also cause the application to quit. + */ + function test_closeWindows() { + root.chosenModel = windowItemsModel; + + app = ApplicationManager.application("test.windowitem.multiwin"); + app.start(); + + tryCompare(windowItemsModel, "count", 2); + tryCompare(WindowManager, "count", 2); + + var firstWindow = windowItemsModel.get(0).window; + var secondWindow = windowItemsModel.get(1).window; + + compare(app.runState, Am.Running); + compare(firstWindow.contentState, WindowObject.SurfaceWithContent); + + firstWindow.close(); + + tryCompare(firstWindow, "contentState", WindowObject.NoSurface); + windowItemsModel.remove(0); + firstWindow = null; + + wait(100); + + compare(app.runState, Am.Running); + compare(secondWindow.contentState, WindowObject.SurfaceWithContent); + + secondWindow.close(); + + tryCompare(secondWindow, "contentState", WindowObject.NoSurface); + tryCompare(app, "runState", Am.NotRunning); + } + + /* + Children added by System UI code must always stay in front of WindowItem's own private children. + */ + function test_childrenZOrder() { + initWindowItemsModel(); + + var windowItem = windowItemsRepeater.itemAt(0); + var window = windowItem.window; + + windowItem.mouseAreaVisible = false; + + touchEvent(windowItem).press(0).commit(); + touchEvent(windowItem).release(0).commit(); + + // There's nothing in front of the wayland item (at least nothing visible). + // The touch event will reach it. + tryVerify(function() { return window.windowProperty("clickCount") === 1; }); + compare(windowItem.clickCount, 0); + + windowItem.mouseAreaVisible = true; + + touchEvent(windowItem).press(0).commit(); + touchEvent(windowItem).release(0).commit(); + + // Since a visible MouseArea is now in front of WindowItem's internal wayland item + // the second touch event was caught by that MouseArea instead. + tryCompare(windowItem, "clickCount", 1); + compare(window.windowProperty("clickCount"), 1); + + } + + // Checks that window properties are kept even when contentState is WindowObject.NoSurface + // Regression test for https://bugreports.qt.io/browse/AUTOSUITE-694 + function test_window_keep_properties_when_nosurface() { + initWindowItemsModel(); + + var window = windowItemsModel.get(0).window; + + compare(window.contentState, WindowObject.SurfaceWithContent); + + window.setWindowProperty("foo", "bar"); + compare(window.windowProperty("foo"), "bar"); + + app.stop(); + + tryCompare(window, "contentState", WindowObject.NoSurface); + compare(window.windowProperty("foo"), "bar"); + } + } +} diff --git a/tests/auto/qml/windowitem/windowitem.pro b/tests/auto/qml/windowitem/windowitem.pro new file mode 100644 index 00000000..fcc1b1f6 --- /dev/null +++ b/tests/auto/qml/windowitem/windowitem.pro @@ -0,0 +1,5 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_windowitem.qml +TEST_APPS = test.windowitem.app test.windowitem.multiwin + +load(am-qml-testcase) diff --git a/tests/auto/qml/windowitem2/CMakeLists.txt b/tests/auto/qml/windowitem2/CMakeLists.txt new file mode 100644 index 00000000..482405d9 --- /dev/null +++ b/tests/auto/qml/windowitem2/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from windowitem2.pro. + +##################################################################### +## windowitem2 Binary: +##################################################################### + +qt_internal_add_executable(windowitem2 + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:windowitem2.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "test.windowitem2.app" +# TEST_FILES = "tst_windowitem2.qml" diff --git a/tests/auto/qml/windowitem2/am-config.yaml b/tests/auto/qml/windowitem2/am-config.yaml new file mode 100644 index 00000000..5333dae3 --- /dev/null +++ b/tests/auto/qml/windowitem2/am-config.yaml @@ -0,0 +1,11 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +# Workaround for a crash in the mesa software renderer (llvmpipe) +runtimes: + qml: + environmentVariables: + QT_QUICK_BACKEND: "software" diff --git a/tests/auto/qml/windowitem2/apps/test.windowitem2.app/icon.png b/tests/auto/qml/windowitem2/apps/test.windowitem2.app/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowitem2/apps/test.windowitem2.app/icon.png diff --git a/tests/auto/qml/windowitem2/apps/test.windowitem2.app/info.yaml b/tests/auto/qml/windowitem2/apps/test.windowitem2.app/info.yaml new file mode 100644 index 00000000..34198c88 --- /dev/null +++ b/tests/auto/qml/windowitem2/apps/test.windowitem2.app/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.windowitem2.app' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'WindowItem2 Test App' diff --git a/tests/auto/qml/windowitem2/apps/test.windowitem2.app/main.qml b/tests/auto/qml/windowitem2/apps/test.windowitem2.app/main.qml new file mode 100644 index 00000000..a0c453aa --- /dev/null +++ b/tests/auto/qml/windowitem2/apps/test.windowitem2.app/main.qml @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + color: "red" + + width: 123 + height: 321 +} diff --git a/tests/auto/qml/windowitem2/tst_windowitem2.qml b/tests/auto/qml/windowitem2/tst_windowitem2.qml new file mode 100644 index 00000000..51759539 --- /dev/null +++ b/tests/auto/qml/windowitem2/tst_windowitem2.qml @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +Item { + id: root + width: 500 + height: 500 + visible: true + + WindowItem { + id: windowItem + } + + Connections { + target: WindowManager + function onWindowAdded(window) { + windowItem.window = window; + } + + function onWindowAboutToBeRemoved(window) { + if (window === windowItem.window) { + windowItem.window = null; + } + } + } + + // Force redraws so that pings and other events are quickly processed between + // wayland client and server. + Rectangle { + width: 10 + height: 10 + color: "brown" + RotationAnimator on rotation { + from: 0; to: 360; duration: 1000 + loops: Animation.Infinite + running: true + } + } + + TestCase { + id: testCase + when: windowShown + name: "WindowItem2" + + function init() { + } + + function cleanup() { + waitUntilAllAppsAreStopped(); + } + + function waitUntilAllAppsAreStopped() { + while (true) { + var numRunningApps = 0; + for (var i = 0; i < ApplicationManager.count; i++) { + var app = ApplicationManager.application(i); + if (app.runState !== Am.NotRunning) + numRunningApps += 1; + } + + if (numRunningApps > 0) { + wait(50); + } else + break; + } + } + + /* + The sequence below only takes place if WindowManager has direct connections to + Window::isBeingDisplayedChanged and Window::contentStateChanged and those connections call + WindowManager::removeWindow: + + 1. When Window.contentState changes to Window::NoSurface, WindowManager calls removeWindow() on it. + 2. Inside WindowManager::removeWindow, windowAboutToBeRemoved gets emitted. + 3. The onWindowAboutToBeRemoved signal handler above is called and sets WindowItem.window to null + 4. That causes Window.isBeingDisplayedChanged to turn to false, which in turn gets + WindowManager to call removeWindow() on that same window once again. + 5. The innermost removeWindow() from 4 successfuly executes and removes the window from WindowManager's + internal vector + 6. once the outermost removeWindow() from 1. finally goes past the signal emission from 2. and tries + to remove the item from for index it previously collected, that index is no longer valid as the vector + already changed. Then you either are removing the wrong item or trying to remove a now invalid index, + which causes a crash. + + This is a regression test for such crash + */ + function test_triggerNestedRemoval() { + var app = ApplicationManager.application("test.windowitem2.app"); + app.start(); + + tryVerify(function() { return windowItem.window !== null }); + wait(50); + + app.stop(); + } + + } +} diff --git a/tests/auto/qml/windowitem2/windowitem2.pro b/tests/auto/qml/windowitem2/windowitem2.pro new file mode 100644 index 00000000..022ea053 --- /dev/null +++ b/tests/auto/qml/windowitem2/windowitem2.pro @@ -0,0 +1,5 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_windowitem2.qml +TEST_APPS = test.windowitem2.app + +load(am-qml-testcase) diff --git a/tests/auto/qml/windowmanager/CMakeLists.txt b/tests/auto/qml/windowmanager/CMakeLists.txt new file mode 100644 index 00000000..4f0d2d91 --- /dev/null +++ b/tests/auto/qml/windowmanager/CMakeLists.txt @@ -0,0 +1,15 @@ +# Generated from windowmanager.pro. + +##################################################################### +## windowmanager Binary: +##################################################################### + +qt_internal_add_executable(windowmanager + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:windowmanager.pro:<TRUE>: +# OTHER_FILES = "IviApplicationExtension.qml" +# TEST_FILES = "tst_windowmanager.qml" diff --git a/tests/auto/qml/windowmanager/IviApplicationExtension.qml b/tests/auto/qml/windowmanager/IviApplicationExtension.qml new file mode 100644 index 00000000..08486dc4 --- /dev/null +++ b/tests/auto/qml/windowmanager/IviApplicationExtension.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 +import QtApplicationManager.SystemUI 2.0 +import QtWayland.Compositor 1.1 + +QtObject { + property Component iviComp: Component { + IviApplication {} + } + + function addExtension() { + return WindowManager.addExtension(iviComp); + } +} diff --git a/tests/auto/qml/windowmanager/tst_windowmanager.qml b/tests/auto/qml/windowmanager/tst_windowmanager.qml new file mode 100644 index 00000000..5927fae1 --- /dev/null +++ b/tests/auto/qml/windowmanager/tst_windowmanager.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.11 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +TestCase { + id: testCase + when: windowShown + name: "WindowManager" + visible: true + + Component { + id: textComp + Text {} + } + + SignalSpy { + id: windowManagerCompositorReadyChangedSpy + target: ApplicationManager + signalName: "windowManagerCompositorReadyChanged" + } + + function test_addExtension() { + if (!ApplicationManager.singleProcess) { + if (!ApplicationManager.windowManagerCompositorReady) { + var extnull = Qt.createComponent("IviApplicationExtension.qml").createObject(null).addExtension(); + compare(extnull, null); + windowManagerCompositorReadyChangedSpy.wait(2000); + verify(ApplicationManager.windowManagerCompositorReady); + } + var extension = Qt.createComponent("IviApplicationExtension.qml").createObject(null).addExtension(); + verify(extension); + verify(extension.hasOwnProperty('iviSurfaceCreated')); + } + compare(WindowManager.addExtension(textComp), null); + } +} diff --git a/tests/auto/qml/windowmanager/windowmanager.pro b/tests/auto/qml/windowmanager/windowmanager.pro new file mode 100644 index 00000000..21cd8a91 --- /dev/null +++ b/tests/auto/qml/windowmanager/windowmanager.pro @@ -0,0 +1,5 @@ +TEST_FILES = tst_windowmanager.qml + +OTHER_FILES += IviApplicationExtension.qml + +load(am-qml-testcase) diff --git a/tests/auto/qml/windowmapping/CMakeLists.txt b/tests/auto/qml/windowmapping/CMakeLists.txt new file mode 100644 index 00000000..d071c8e1 --- /dev/null +++ b/tests/auto/qml/windowmapping/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from windowmapping.pro. + +##################################################################### +## windowmapping Binary: +##################################################################### + +qt_internal_add_executable(windowmapping + GUI + PUBLIC_LIBRARIES + Qt::Gui +) + +#### Keys ignored in scope 1:.:.:windowmapping.pro:<TRUE>: +# AM_CONFIG = "am-config.yaml" +# TEST_APPS = "test.winmap.amwin" "test.winmap.amwin2" "test.winmap.loader" "test.winmap.ping" "test.winmap.qtobject" "test.winmap.rectangle" "test.winmap.window" +# TEST_FILES = "tst_windowmapping.qml" diff --git a/tests/auto/qml/windowmapping/am-config.yaml b/tests/auto/qml/windowmapping/am-config.yaml new file mode 100644 index 00000000..5333dae3 --- /dev/null +++ b/tests/auto/qml/windowmapping/am-config.yaml @@ -0,0 +1,11 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +# Workaround for a crash in the mesa software renderer (llvmpipe) +runtimes: + qml: + environmentVariables: + QT_QUICK_BACKEND: "software" diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.amwin/amwin.qml b/tests/auto/qml/windowmapping/apps/test.winmap.amwin/amwin.qml new file mode 100644 index 00000000..05e56276 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.amwin/amwin.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + id: root + + ApplicationManagerWindow { + id: sub + visible: false + Component.onCompleted: setWindowProperty("type", "sub"); + } + + Connections { + target: ApplicationInterface + function onOpenDocument(documentUrl) { + switch (documentUrl) { + case "show-main": root.visible = true; root.setWindowProperty("key1", "val1"); break; + case "hide-main": root.visible = false; break; + case "show-sub": sub.visible = true; break; + case "hide-sub": sub.visible = false; break; + } + } + } + + Component.onCompleted: setWindowProperty("objectName", 42); +} diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.amwin/icon.png b/tests/auto/qml/windowmapping/apps/test.winmap.amwin/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.amwin/icon.png diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.amwin/info.yaml b/tests/auto/qml/windowmapping/apps/test.winmap.amwin/info.yaml new file mode 100644 index 00000000..78c26e03 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.amwin/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.winmap.amwin' +icon: 'icon.png' +code: 'amwin.qml' +runtime: 'qml' +name: + en: 'ApplicationManagerWindow Root' diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/amwin2.qml b/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/amwin2.qml new file mode 100644 index 00000000..f6d58131 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/amwin2.qml @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + id: root + visible: false + + ApplicationManagerWindow { + id: sub + visible: false + Component.onCompleted: setWindowProperty("type", "sub"); + } + + Rectangle { + anchors.fill: parent + visible: false + + ApplicationManagerWindow { + id: sub2 + visible: false + Component.onCompleted: setWindowProperty("type", "sub2"); + } + } + + Connections { + target: ApplicationInterface + function onOpenDocument(documentUrl) { + switch (documentUrl) { + case "show-main": root.visible = true; break; + case "hide-main": root.visible = false; break; + case "show-sub": sub.visible = true; break; + case "hide-sub": sub.visible = false; break; + case "show-sub2": sub2.visible = true; break; + case "hide-sub2": sub2.visible = false; break; + } + } + } +} diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/icon.png b/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/icon.png diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/info.yaml b/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/info.yaml new file mode 100644 index 00000000..041524a8 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.winmap.amwin2' +icon: 'icon.png' +code: 'amwin2.qml' +runtime: 'qml' +name: + en: 'ApplicationManagerWindow Advanced' diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.loader/SubWin.qml b/tests/auto/qml/windowmapping/apps/test.winmap.loader/SubWin.qml new file mode 100644 index 00000000..a39ebc53 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.loader/SubWin.qml @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + Component.onCompleted: setWindowProperty("type", "sub"); +} diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.loader/icon.png b/tests/auto/qml/windowmapping/apps/test.winmap.loader/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.loader/icon.png diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.loader/info.yaml b/tests/auto/qml/windowmapping/apps/test.winmap.loader/info.yaml new file mode 100644 index 00000000..6e486078 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.loader/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.winmap.loader' +icon: 'icon.png' +code: 'loader.qml' +runtime: 'qml' +name: + en: 'Dynamic loading' diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.loader/loader.qml b/tests/auto/qml/windowmapping/apps/test.winmap.loader/loader.qml new file mode 100644 index 00000000..d1394ce4 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.loader/loader.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + id: root + visible: true + + Loader { + id: ldr + active: false + source: "SubWin.qml" + } + + Connections { + target: ApplicationInterface + function onOpenDocument(documentUrl) { + switch (documentUrl) { + case "show-sub": ldr.active = true; break; + case "hide-sub": ldr.active = false; break; + } + } + } +} diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.ping/icon.png b/tests/auto/qml/windowmapping/apps/test.winmap.ping/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.ping/icon.png diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.ping/info.yaml b/tests/auto/qml/windowmapping/apps/test.winmap.ping/info.yaml new file mode 100644 index 00000000..3f4e822e --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.ping/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.winmap.ping' +icon: 'icon.png' +code: 'ping.qml' +runtime: 'qml' +name: + en: 'Wayland ping-pong' diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.ping/ping.qml b/tests/auto/qml/windowmapping/apps/test.winmap.ping/ping.qml new file mode 100644 index 00000000..c6e6c02d --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.ping/ping.qml @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +ApplicationManagerWindow { + Timer { + id: tim + interval: 100 + running: true + onTriggered: { + while (true); // application hangs + } + } +} diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/icon.png b/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/icon.png diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/info.yaml b/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/info.yaml new file mode 100644 index 00000000..98926746 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.winmap.qtobject' +icon: 'icon.png' +code: 'qtobject.qml' +runtime: 'qml' +name: + en: 'QtObject Root' diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/qtobject.qml b/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/qtobject.qml new file mode 100644 index 00000000..203b0f56 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/qtobject.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +QtObject { + id: root + + property ApplicationManagerWindow mainWin: ApplicationManagerWindow { + id: main + visible: false + } + + property ApplicationManagerWindow subWin: ApplicationManagerWindow { + id: sub + visible: false + Component.onCompleted: setWindowProperty("type", "sub"); + } + + property Connections con: Connections { + target: ApplicationInterface + function onOpenDocument(documentUrl) { + switch (documentUrl) { + case "show-main": main.visible = true; break; + case "hide-main": main.visible = false; break; + case "show-sub": sub.visible = true; break; + case "hide-sub": sub.visible = false; break; + } + } + } +} diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/icon.png b/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/icon.png diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/info.yaml b/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/info.yaml new file mode 100644 index 00000000..0981b53d --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.winmap.rectangle' +icon: 'icon.png' +code: 'rectangle.qml' +runtime: 'qml' +name: + en: 'Rectangle Root' diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/rectangle.qml b/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/rectangle.qml new file mode 100644 index 00000000..8e11e72c --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/rectangle.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 2.0 + +Rectangle { + id: root + + ApplicationManagerWindow { + id: sub + visible: false + Component.onCompleted: setWindowProperty("type", "sub"); + } + + Connections { + target: ApplicationInterface + function onOpenDocument(documentUrl) { + switch (documentUrl) { + case "show-main": root.visible = true; break; + case "hide-main": root.visible = false; break; + case "show-sub": sub.visible = true; break; + case "hide-sub": sub.visible = false; break; + } + } + } +} diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.window/icon.png b/tests/auto/qml/windowmapping/apps/test.winmap.window/icon.png Binary files differnew file mode 100644 index 00000000..c1397153 --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.window/icon.png diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.window/info.yaml b/tests/auto/qml/windowmapping/apps/test.winmap.window/info.yaml new file mode 100644 index 00000000..c527475d --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.window/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'test.winmap.window' +icon: 'icon.png' +code: 'window.qml' +runtime: 'qml' +name: + en: 'Window Root' diff --git a/tests/auto/qml/windowmapping/apps/test.winmap.window/window.qml b/tests/auto/qml/windowmapping/apps/test.winmap.window/window.qml new file mode 100644 index 00000000..588f33ce --- /dev/null +++ b/tests/auto/qml/windowmapping/apps/test.winmap.window/window.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Window 2.0 +import QtApplicationManager.Application 2.0 + +Window { + id: root + visible: true // explicitly set, since false by default + + ApplicationManagerWindow { + id: sub + visible: false + Component.onCompleted: setWindowProperty("type", "sub"); + } + + Connections { + target: ApplicationInterface + function onOpenDocument(documentUrl) { + switch (documentUrl) { + case "show-main": root.visible = true; break; + case "hide-main": root.visible = false; break; + case "show-sub": sub.visible = true; break; + case "hide-sub": sub.visible = false; break; + } + } + } +} diff --git a/tests/auto/qml/windowmapping/tst_windowmapping.qml b/tests/auto/qml/windowmapping/tst_windowmapping.qml new file mode 100644 index 00000000..140665c5 --- /dev/null +++ b/tests/auto/qml/windowmapping/tst_windowmapping.qml @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 +import QtApplicationManager.SystemUI 2.0 + +TestCase { + id: testCase + when: windowShown + name: "WindowMapping" + visible: true + + property var lastWindowAdded; + + WindowItem { + id: chrome + anchors.fill: parent + + Connections { + target: chrome.window + function onContentStateChanged() { + if (chrome.window.contentState === WindowObject.NoSurface) + chrome.window = null; + } + } + + WindowItem { + id: subChrome + anchors.fill: parent + Connections { + target: subChrome.window + function onContentStateChanged() { + if (subChrome.window.contentState === WindowObject.NoSurface) + subChrome.window = null; + } + } + } + } + + Connections { + target: WindowManager + function onWindowAdded(window) { + if (window.windowProperty("type") === "sub") + subChrome.window = window; + else + chrome.window = window; + + testCase.lastWindowAdded = window; + } + } + + + SignalSpy { + id: windowAddedSpy + target: WindowManager + signalName: "windowAdded" + } + + SignalSpy { + id: windowAboutToBeRemovedSpy + target: WindowManager + signalName: "windowAboutToBeRemoved" + } + + SignalSpy { + id: windowPropertyChangedSpy + target: WindowManager + signalName: "windowPropertyChanged" + } + +    SignalSpy { +        id: runStateChangedSpy +        target: ApplicationManager +        signalName: "applicationRunStateChanged" +    } + + function cleanup() { + runStateChangedSpy.clear(); + ApplicationManager.stopAllApplications(); + + while (true) { + var numRunningApps = 0; + for (var i = 0; i < ApplicationManager.count; i++) { + var app = ApplicationManager.application(i); + if (app.runState !== Am.NotRunning) + numRunningApps += 1; + } + + if (numRunningApps > 0) { + wait(2000); + } else + break; + } + + windowAddedSpy.clear(); + windowAboutToBeRemovedSpy.clear(); + } + + function test_windowmanager_added_removed_signals() { + var app = ApplicationManager.application("test.winmap.amwin"); + + compare(windowAddedSpy.count, 0); + app.start("show-main"); + tryCompare(windowAddedSpy, "count", 1); + + compare(windowAboutToBeRemovedSpy.count, 0); + app.stop(); + tryCompare(windowAboutToBeRemovedSpy, "count", 1); + } + + function test_amwin_advanced() { + var app = ApplicationManager.application("test.winmap.amwin2"); + app.start("show-sub"); + wait(2000); + compare(WindowManager.count, 0); + + app.start("show-main"); + tryCompare(WindowManager, "count", 2); + } + + function test_amwin_loader() { + tryCompare(WindowManager, "count", 0); + + var app = ApplicationManager.application("test.winmap.loader"); + + app.start("show-sub"); + tryCompare(WindowManager, "count", 2); + + app.start("hide-sub"); + tryCompare(WindowManager, "count", 1); + + app.start("show-sub"); + tryCompare(WindowManager, "count", 2); + } + + function test_amwin_peculiarities() { + var app = ApplicationManager.application("test.winmap.amwin2"); + + tryCompare(WindowManager, "count", 0); + + app.start("show-main"); + tryCompare(WindowManager, "count", 1); + + app.start("show-sub"); + tryCompare(WindowManager, "count", 2); + + // Single- vs. multiprocess difference: + app.start("show-sub2"); + var expectedWindowCount; + // A Window's effective visible state solely depends on Window hierarchy. + expectedWindowCount = 3; + tryCompare(WindowManager, "count", expectedWindowCount); + + app.start("hide-sub"); + expectedWindowCount -= 1; + tryCompare(WindowManager, "count", expectedWindowCount); + + // Make child (sub) window visible again, parent (main) window is still visible + app.start("show-sub"); + expectedWindowCount += 1; + tryCompare(WindowManager, "count", expectedWindowCount); + + // This is weird Window behavior: a child window becomes only visible, when the parent + // window is visible, but when you change the parent window back to invisible, the child + // will NOT become invisible. + app.start("hide-main"); + expectedWindowCount -= 1; + tryCompare(WindowManager, "count", expectedWindowCount); + + // Single- vs. multiprocess difference: + app.start("hide-sub"); + if (ApplicationManager.singleProcess) { + expectedWindowCount -= 1; + } else { + // This is even more weird Window behavior: when the parent window is invisible, it is + // not possible any more to explicitly set the child window to invisible. + wait(50); + } + tryCompare(WindowManager, "count", expectedWindowCount); + } + + function test_default_data() { + return [ { tag: "ApplicationManagerWindow", appId: "test.winmap.amwin" }, + // skipping QtObject, as it doesn't show anything + { tag: "Rectangle", appId: "test.winmap.rectangle" }, + { tag: "Window", appId: "test.winmap.window" } ]; + } + + function test_default(data) { + if (ApplicationManager.singleProcess && data.tag === "Window") + skip("Window root element is not properly supported in single process mode."); + + compare(WindowManager.count, 0); + + var app = ApplicationManager.application(data.appId); + verify(chrome.window === null); + app.start(); + tryCompare(WindowManager, "count", 1); + tryVerify(function () { return chrome.window !== null }); + + app.stop(); + tryCompare(WindowManager, "count", 0); + } + + function test_mapping_data() { + return [ { tag: "ApplicationManagerWindow", appId: "test.winmap.amwin" }, + { tag: "QtObject", appId: "test.winmap.qtobject" }, + { tag: "Rectangle", appId: "test.winmap.rectangle" }, + { tag: "Window", appId: "test.winmap.window" } ]; + } + + function test_mapping(data) { + if (ApplicationManager.singleProcess && data.tag === "Window") + skip("Window root element is not properly supported in single process mode."); + + var app = ApplicationManager.application(data.appId); + + compare(WindowManager.count, 0); + + app.start("show-main"); + tryCompare(WindowManager, "count", 1); + + app.start("show-sub"); + tryCompare(WindowManager, "count", 2); + + app.start("hide-sub"); + tryCompare(WindowManager, "count", 1); + + app.stop(); + tryCompare(WindowManager, "count", 0); + } + + function test_wayland_ping_pong() { + var app = ApplicationManager.application("test.winmap.ping"); + + if (ApplicationManager.singleProcess) + skip("Wayland ping-pong is only supported in multi-process mode"); + + AmTest.ignoreMessage(AmTest.CriticalMsg, /Stopping application.*because we did not receive a Wayland-Pong/); + app.start(); + tryCompare(app, "runState", Am.Running); + runStateChangedSpy.clear(); + wait(2200); + runStateChangedSpy.wait(2000); + compare(runStateChangedSpy.signalArguments[0][1], Am.ShuttingDown); + runStateChangedSpy.wait(2000); + compare(runStateChangedSpy.signalArguments[1][1], Am.NotRunning); + } + + function test_window_properties() { + var app = ApplicationManager.application("test.winmap.amwin"); + + windowPropertyChangedSpy.clear(); + app.start(); + tryCompare(WindowManager, "count", 1); + + app.start("show-main"); + windowPropertyChangedSpy.wait(2000); + compare(windowPropertyChangedSpy.count, 1); + + compare(lastWindowAdded.windowProperty("key1"), "val1"); + compare(lastWindowAdded.windowProperty("objectName"), 42); + + lastWindowAdded.setWindowProperty("key2", "val2"); + windowPropertyChangedSpy.wait(2000); + compare(windowPropertyChangedSpy.count, 2); + + var allProps = lastWindowAdded.windowProperties() + compare(Object.keys(allProps).length, 3); + compare(allProps.key1, "val1"); + compare(allProps.key2, "val2"); + compare(allProps.objectName, 42); + } + + // Checks that window properties survive show/hide cycles + // Regression test for https://bugreports.qt.io/browse/AUTOSUITE-447 + function test_window_properties_survive_show_hide() { + var app = ApplicationManager.application("test.winmap.amwin"); + + app.start("show-main"); + tryCompare(WindowManager, "count", 1); + + compare(lastWindowAdded.windowProperty("objectName"), 42); + + app.start("hide-main"); + tryCompare(WindowManager, "count", 0); + app.start("show-main"); + tryCompare(WindowManager, "count", 1); + + compare(lastWindowAdded.windowProperty("objectName"), 42); + } +} diff --git a/tests/auto/qml/windowmapping/windowmapping.pro b/tests/auto/qml/windowmapping/windowmapping.pro new file mode 100644 index 00000000..e29aba28 --- /dev/null +++ b/tests/auto/qml/windowmapping/windowmapping.pro @@ -0,0 +1,12 @@ +AM_CONFIG = am-config.yaml +TEST_FILES = tst_windowmapping.qml +TEST_APPS = \ + test.winmap.amwin \ + test.winmap.amwin2 \ + test.winmap.loader \ + test.winmap.ping \ + test.winmap.qtobject \ + test.winmap.rectangle \ + test.winmap.window \ + +load(am-qml-testcase) diff --git a/tests/auto/runtime/CMakeLists.txt b/tests/auto/runtime/CMakeLists.txt new file mode 100644 index 00000000..d3973556 --- /dev/null +++ b/tests/auto/runtime/CMakeLists.txt @@ -0,0 +1,22 @@ + +qt_internal_add_test(tst_runtime + SOURCES + ../error-checking.h + tst_runtime.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::Qml + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManManagerPrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_runtime CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/runtime/runtime.pro b/tests/auto/runtime/runtime.pro new file mode 100644 index 00000000..64c550cc --- /dev/null +++ b/tests/auto/runtime/runtime.pro @@ -0,0 +1,11 @@ +TARGET = tst_runtime + +include($$PWD/../tests.pri) + +QT *= qml +QT *= \ + appman_common-private \ + appman_application-private \ + appman_manager-private \ + +SOURCES += tst_runtime.cpp diff --git a/tests/auto/runtime/tst_runtime.cpp b/tests/auto/runtime/tst_runtime.cpp new file mode 100644 index 00000000..ee62e5d4 --- /dev/null +++ b/tests/auto/runtime/tst_runtime.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtTest> +#include <QQmlEngine> + +#include "application.h" +#include "package.h" +#include "abstractruntime.h" +#include "runtimefactory.h" +#include "exception.h" + +QT_USE_NAMESPACE_AM + +class tst_Runtime : public QObject +{ + Q_OBJECT + +public: + tst_Runtime(); + +private slots: + void factory(); +}; + +class TestRuntimeManager; + +class TestRuntime : public AbstractRuntime +{ + Q_OBJECT + +public: + explicit TestRuntime(AbstractContainer *container, Application *app, AbstractRuntimeManager *manager) + : AbstractRuntime(container, app, manager) + { } + + void setSlowAnimations(bool) override {} + + qint64 applicationProcessId() const override + { + return m_state == Am::Running ? 1 : 0; + } + +public slots: + bool start() override + { + m_state = Am::Running; + return true; + } + + void stop(bool forceKill) override + { + Q_UNUSED(forceKill); + m_state = Am::NotRunning; + } +}; + +class TestRuntimeManager : public AbstractRuntimeManager +{ + Q_OBJECT + +public: + TestRuntimeManager(const QString &id, QObject *parent) + : AbstractRuntimeManager(id, parent) + { } + + static QString defaultIdentifier() { return qSL("foo"); } + + bool inProcess() const override + { + return !AbstractRuntimeManager::inProcess(); + } + + TestRuntime *create(AbstractContainer *container, Application *app) override + { + return new TestRuntime(container, app, this); + } +}; + + +tst_Runtime::tst_Runtime() +{ } + +void tst_Runtime::factory() +{ + RuntimeFactory *rf = RuntimeFactory::instance(); + + QVERIFY(rf); + QVERIFY(rf == RuntimeFactory::instance()); + QVERIFY(rf->runtimeIds().isEmpty()); + + QVERIFY(rf->registerRuntime(new TestRuntimeManager(qSL("foo"), qApp))); + QVERIFY(rf->runtimeIds() == QStringList() << qSL("foo")); + + QVERIFY(!rf->create(nullptr, nullptr)); + + QByteArray yaml = + "formatVersion: 1\n" + "formatType: am-application\n" + "---\n" + "id: com.pelagicore.test\n" + "name: { en_US: 'Test' }\n" + "icon: icon.png\n" + "code: test.foo\n" + "runtime: foo\n"; + + QTemporaryFile temp; + QVERIFY(temp.open()); + QCOMPARE(temp.write(yaml), yaml.size()); + temp.close(); + + Application *a = nullptr; + try { + PackageInfo *pi = PackageInfo::fromManifest(temp.fileName()); + QVERIFY(pi); + Package *p = new Package(pi); + a = new Application(pi->applications().first(), p); + } catch (const Exception &e) { + QVERIFY2(false, qPrintable(e.errorString())); + } + QVERIFY(a); + + AbstractRuntime *r = rf->create(nullptr, a); + QVERIFY(r); + QVERIFY(r->application() == a); + QVERIFY(r->manager()->inProcess()); + QVERIFY(r->state() == Am::NotRunning); + QVERIFY(r->applicationProcessId() == 0); + { + QScopedPointer<QQmlEngine> engine(new QQmlEngine()); + QVERIFY(!r->inProcessQmlEngine()); + r->setInProcessQmlEngine(engine.data()); + QVERIFY(r->inProcessQmlEngine() == engine.data()); + r->setInProcessQmlEngine(nullptr); + } + QVERIFY(r->start()); + QVERIFY(r->state() == Am::Running); + QVERIFY(r->applicationProcessId() == 1); + r->stop(); + QVERIFY(r->state() == Am::NotRunning); + QVERIFY(!r->securityToken().isEmpty()); + + delete r; + delete rf; + delete a; +} + +QTEST_MAIN(tst_Runtime) + +#include "tst_runtime.moc" diff --git a/tests/auto/signature/CMakeLists.txt b/tests/auto/signature/CMakeLists.txt new file mode 100644 index 00000000..4a102b45 --- /dev/null +++ b/tests/auto/signature/CMakeLists.txt @@ -0,0 +1,41 @@ + +qt_internal_add_test(tst_signature + SOURCES + ../error-checking.h + tst_signature.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManCommonPrivate + Qt::AppManCryptoPrivate +) + +# Resources: +set(tst_signature_resource_files + "signature-openssl.p7" + "signature-securityframework.p7" + "signature-wincrypt.p7" + "signing-no-key.p12" + "signing.p12" + "verifying.crt" +) + +qt_internal_add_resource(tst_signature "tst_signature" + PREFIX + "/" + FILES + ${tst_signature_resource_files} +) + + +#### Keys ignored in scope 1:.:.:signature.pro:<TRUE>: +# OTHER_FILES = "create-test-data.sh" + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_signature CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/signature/create-test-data.sh b/tests/auto/signature/create-test-data.sh new file mode 100644 index 00000000..68f05575 --- /dev/null +++ b/tests/auto/signature/create-test-data.sh @@ -0,0 +1,40 @@ +#!/bin/sh +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the QtApplicationManager module of the Qt Toolkit. +## +## $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$ +## +############################################################################# + +echo "Recreating test data" + +certdir="../data/certificates/" + +[ -f $certdir/dev1.p12 ] || { echo "Please generate test certificates in $certdir first"; exit 1; } + +cp $certdir/dev1.p12 signing.p12 +openssl pkcs12 -export -out signing-no-key.p12 -password pass:password -inkey $certdir/dev1-priv.key -nodes -certfile $certdir/ca.crt -in $certdir/dev1.crt -name "Developer 1 Certificate (no key)" -nokeys +cat $certdir/ca.crt $certdir/devca.crt >verifying.crt diff --git a/tests/auto/signature/signature-openssl.p7 b/tests/auto/signature/signature-openssl.p7 Binary files differnew file mode 100644 index 00000000..294310cf --- /dev/null +++ b/tests/auto/signature/signature-openssl.p7 diff --git a/tests/auto/signature/signature-securityframework.p7 b/tests/auto/signature/signature-securityframework.p7 Binary files differnew file mode 100644 index 00000000..ded48dd8 --- /dev/null +++ b/tests/auto/signature/signature-securityframework.p7 diff --git a/tests/auto/signature/signature-wincrypt.p7 b/tests/auto/signature/signature-wincrypt.p7 Binary files differnew file mode 100644 index 00000000..e3ef40d3 --- /dev/null +++ b/tests/auto/signature/signature-wincrypt.p7 diff --git a/tests/auto/signature/signature.pro b/tests/auto/signature/signature.pro new file mode 100644 index 00000000..43bb3d47 --- /dev/null +++ b/tests/auto/signature/signature.pro @@ -0,0 +1,11 @@ +TARGET = tst_signature + +include($$PWD/../tests.pri) + +QT *= appman_common-private appman_crypto-private + +SOURCES += tst_signature.cpp + +OTHER_FILES += create-test-data.sh + +RESOURCES += tst_signature.qrc diff --git a/tests/auto/signature/signing-no-key.p12 b/tests/auto/signature/signing-no-key.p12 Binary files differnew file mode 100644 index 00000000..57705d79 --- /dev/null +++ b/tests/auto/signature/signing-no-key.p12 diff --git a/tests/auto/signature/signing.p12 b/tests/auto/signature/signing.p12 Binary files differnew file mode 100644 index 00000000..5d752759 --- /dev/null +++ b/tests/auto/signature/signing.p12 diff --git a/tests/auto/signature/tst_signature.cpp b/tests/auto/signature/tst_signature.cpp new file mode 100644 index 00000000..ea4f471d --- /dev/null +++ b/tests/auto/signature/tst_signature.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore/QtCore> +#include <QtTest/QtTest> + +#include "global.h" +#include "signature.h" + +QT_USE_NAMESPACE_AM + +class tst_Signature : public QObject +{ + Q_OBJECT + +public: + tst_Signature(); + +private slots: + void initTestCase(); + void check(); + void crossPlatform(); + +private: + QByteArray m_signingP12; + QByteArray m_signingNoKeyP12; + QByteArray m_signingPassword; + QList<QByteArray> m_verifyingPEM; +}; + +tst_Signature::tst_Signature() +{ } + +void tst_Signature::initTestCase() +{ + QFile s(qSL(":/signing.p12")); + QVERIFY(s.open(QIODevice::ReadOnly)); + m_signingP12 = s.readAll(); + QVERIFY(!m_signingP12.isEmpty()); + + QFile snk(qSL(":/signing-no-key.p12")); + QVERIFY(snk.open(QIODevice::ReadOnly)); + m_signingNoKeyP12 = snk.readAll(); + QVERIFY(!m_signingNoKeyP12.isEmpty()); + + QFile v(qSL(":/verifying.crt")); + QVERIFY(v.open(QIODevice::ReadOnly)); + m_verifyingPEM << v.readAll(); + QVERIFY(!m_verifyingPEM.first().isEmpty()); + + m_signingPassword = "password"; +} + +void tst_Signature::check() +{ + QByteArray hash("foo"); + Signature s(hash); + QVERIFY(s.errorString().isEmpty()); + QByteArray signature = s.create(m_signingP12, m_signingPassword); + QVERIFY2(!signature.isEmpty(), qPrintable(s.errorString())); + + Signature s2(hash + "bar"); + QByteArray signature2 = s2.create(m_signingP12, m_signingPassword); + QVERIFY2(!signature2.isEmpty(), qPrintable(s2.errorString())); + QVERIFY(signature != signature2); + + QVERIFY2(s.verify(signature, m_verifyingPEM), qPrintable(s.errorString())); + QVERIFY2(s2.verify(signature2, m_verifyingPEM), qPrintable(s2.errorString())); + QVERIFY(!s.verify(signature2, m_verifyingPEM)); + QVERIFY(!s2.verify(signature, m_verifyingPEM)); + + QVERIFY(s.create(m_signingP12, m_signingPassword + "not").isEmpty()); + QVERIFY2(s.errorString().contains(qSL("not parse")), qPrintable(s.errorString())); + + QVERIFY(s.create(QByteArray(), m_signingPassword).isEmpty()); + QVERIFY2(s.errorString().contains(qSL("not read")), qPrintable(s.errorString())); + + Signature s3(QByteArray(4096, 'x')); + QVERIFY(!s3.create(m_signingP12, m_signingPassword).isEmpty()); + + QVERIFY(!s.verify(signature, QList<QByteArray>())); + QVERIFY2(s.errorString().contains(qSL("Failed to verify")), qPrintable(s.errorString())); + QVERIFY(!s.verify(signature, QList<QByteArray>() << m_signingP12)); + QVERIFY2(s.errorString().contains(qSL("not load")), qPrintable(s.errorString())); + QVERIFY(!s.verify(hash, QList<QByteArray>() << m_signingP12)); + QVERIFY2(s.errorString().contains(qSL("not read")), qPrintable(s.errorString())); + + Signature s4 { QByteArray() }; + QVERIFY(s4.create(m_signingP12, m_signingPassword).isEmpty()); + + QVERIFY(s.create(m_signingNoKeyP12, m_signingPassword).isEmpty()); + QVERIFY2(s.errorString().contains(qSL("private key")), qPrintable(s.errorString())); +} + +void tst_Signature::crossPlatform() +{ + QByteArray hash = "hello\nworld!"; + + QFile fileOpenSsl(qSL(":/signature-openssl.p7")); + QFile fileWinCrypt(qSL(":/signature-wincrypt.p7")); + QFile fileSecurityFramework(qSL(":/signature-securityframework.p7")); + + if (qEnvironmentVariableIsSet("AM_CREATE_SIGNATURE_FILE")) { + QFile *nativeFile = nullptr; +#if defined(AM_USE_LIBCRYPTO) + nativeFile = &fileOpenSsl; +#elif defined(Q_OS_WIN) + nativeFile = &fileWinCrypt; +#elif defined(Q_OS_OSX) + nativeFile = &fileSecurityFramework; +#endif + QVERIFY(nativeFile); + QFile f(qL1S(AM_TESTDATA_DIR "/../signature") + nativeFile->fileName().mid(1)); + QVERIFY2(f.open(QFile::WriteOnly | QFile::Truncate), qPrintable(f.errorString())); + + Signature s(hash); + QByteArray signature = s.create(m_signingP12, m_signingPassword); + QVERIFY2(!signature.isEmpty(), qPrintable(s.errorString())); + QCOMPARE(f.write(signature), signature.size()); + + qInfo() << "Only creating signature file" << f.fileName() << "because $AM_CREATE_SIGNATURE_FILE is set."; + return; + } + + QVERIFY(fileOpenSsl.open(QIODevice::ReadOnly)); + QByteArray sigOpenSsl = fileOpenSsl.readAll(); + QVERIFY(!sigOpenSsl.isEmpty()); + QVERIFY(fileWinCrypt.open(QIODevice::ReadOnly)); + QByteArray sigWinCrypt = fileWinCrypt.readAll(); + QVERIFY(!sigWinCrypt.isEmpty()); + QVERIFY(fileSecurityFramework.open(QIODevice::ReadOnly)); + QByteArray sigSecurityFramework = fileSecurityFramework.readAll(); + QVERIFY(!sigSecurityFramework.isEmpty()); + + Signature s(hash); + QVERIFY2(s.verify(sigOpenSsl, m_verifyingPEM), qPrintable(s.errorString())); + QVERIFY2(s.verify(sigWinCrypt, m_verifyingPEM), qPrintable(s.errorString())); + QVERIFY2(s.verify(sigSecurityFramework, m_verifyingPEM), qPrintable(s.errorString())); +} + +QTEST_APPLESS_MAIN(tst_Signature) + +#include "tst_signature.moc" + diff --git a/tests/auto/signature/tst_signature.qrc b/tests/auto/signature/tst_signature.qrc new file mode 100644 index 00000000..f063bca7 --- /dev/null +++ b/tests/auto/signature/tst_signature.qrc @@ -0,0 +1,10 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>signing.p12</file> + <file>signing-no-key.p12</file> + <file>verifying.crt</file> + <file>signature-openssl.p7</file> + <file>signature-wincrypt.p7</file> + <file>signature-securityframework.p7</file> +</qresource> +</RCC> diff --git a/tests/auto/signature/verifying.crt b/tests/auto/signature/verifying.crt new file mode 100644 index 00000000..de7dcc07 --- /dev/null +++ b/tests/auto/signature/verifying.crt @@ -0,0 +1,105 @@ +-----BEGIN CERTIFICATE----- +MIID4TCCAsmgAwIBAgIJAOGN7P3anRxmMA0GCSqGSIb3DQEBCwUAMH8xCzAJBgNV +BAYTAkRFMRYwFAYDVQQKDA1QZWxhZ2ljb3JlIEFHMRIwEAYDVQQLDAlBcHAgU3Rv +cmUxIDAeBgNVBAMMF1BlbGFnaWNvcmUgQXBwIFN0b3JlIENBMSIwIAYJKoZIhvcN +AQkBFhNpbmZvQHBlbGFnaWNvcmUuY29tMB4XDTE1MDMxMTAxMTk0NloXDTI1MDMw +ODAxMTk0NlowfzELMAkGA1UEBhMCREUxFjAUBgNVBAoMDVBlbGFnaWNvcmUgQUcx +EjAQBgNVBAsMCUFwcCBTdG9yZTEgMB4GA1UEAwwXUGVsYWdpY29yZSBBcHAgU3Rv +cmUgQ0ExIjAgBgkqhkiG9w0BCQEWE2luZm9AcGVsYWdpY29yZS5jb20wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkAz3WPxmzXt53bDimMy3ieAVXYtV1 +tJTzBBpAv7emrsa1JxOp/NsaTt18cXCrpNnBiGnlcdFtkULj53U8BRwd2CNmraRG +QR09uq0H98vF1NwPQ4vFZ3NEOdofjtAgDipqcAHSH+T0lxj2xvWLP44fo7UvmG4Q +CUAA7JhIwjFDJugZ3JFcTQl9eCt2CfvysvHC4sBmZTRAk+sL/oBZvasS4NAgqSRX +WCZKXIoSdgir1BAvB/u/mF/RT+zkP4ntoeBSB/yjqWOmghA6nbCQk33nGT4cBMD5 +0QPliXioSqjIrIhIrKzqtW5yqOcUhKc9g8oWKSekMg25hcq6XF08uS7ZAgMBAAGj +YDBeMB0GA1UdDgQWBBT0CdSIj6UvzW6R6tFaBeOL6yijwTAfBgNVHSMEGDAWgBT0 +CdSIj6UvzW6R6tFaBeOL6yijwTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAQEAYXr+Eans7BbTMRvf0iZdCdTAneNCrF5oz7V0 +xwWqJBzL1rwhvHbruKvF7I0bSoURmSVd+P9Ge0rYvW+HXuLlgmwAfl9n4zWsHohs +zDFSPQIfb97TP5HE21DrCGCauQirUGaA91inGDmGW80RWbzL1gogWZYpP7IOwu5G +nr+3AgxqZVOS8H0Ppf8aMqtiuamR63SCE5OoHJCNeQ6OmoFrnW8vF9eMScoc1Txb +F4uMqSPny6kgmarwfGnurhM2NOn8OpELEU9mwd7XpzpHpvYVm76tZpd5gMhYFsjp +lyDFPVFQ5pVv5Cc7xhxzYrcn7JQt/GYhYuChM0us5a5urV/l+g== +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=DE, O=Pelagicore AG, OU=App Store, CN=Pelagicore App Store CA/emailAddress=info@pelagicore.com + Validity + Not Before: Mar 11 01:19:46 2015 GMT + Not After : Mar 8 01:19:46 2025 GMT + Subject: C=DE, O=Pelagicore AG, OU=Developer Relations, CN=Pelagicore Developer CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d7:88:7d:35:92:04:9c:58:b1:c9:7e:e0:eb:09: + 10:78:6f:cb:78:f6:1b:38:5a:54:8b:5e:b5:b2:92: + 45:9c:b7:6c:01:0f:b2:16:66:66:24:0a:ed:37:83: + ad:79:27:b0:e8:ff:49:db:68:f6:26:b5:61:d9:7b: + ea:46:7a:e2:55:df:fc:cc:af:2e:45:ce:f7:59:52: + 19:9c:6c:39:d6:5c:68:9e:73:45:71:ac:b2:1d:e6: + 98:0e:7f:18:06:cd:09:49:c7:d6:b6:5c:e1:e9:4f: + 6b:80:ac:4a:c7:36:21:c7:2a:e8:bc:ae:87:f2:49: + 05:62:f9:01:24:c0:93:5b:85:1a:bb:d1:81:19:aa: + 31:09:cd:5b:e2:5a:61:76:4d:50:8d:45:60:70:ca: + 64:64:bc:1f:25:bf:64:2e:65:16:95:1c:ee:29:bb: + a7:31:ec:20:39:81:eb:48:df:93:ca:48:6c:be:9e: + df:5e:05:8a:db:8e:c4:07:cc:fc:c8:08:16:1e:e3: + 04:51:d9:0b:6f:28:1a:e4:38:93:05:00:59:49:60: + 81:12:bc:48:f9:81:08:ff:fb:59:95:ad:f3:54:85: + 1b:70:5f:cf:eb:0f:ea:45:20:39:b0:74:55:0c:94: + d7:3f:88:85:b8:71:ac:b0:e7:99:70:cc:f5:c2:89: + 6b:af + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 8B:2D:DE:5A:18:A5:71:A9:73:03:54:69:56:87:A0:17:BF:C2:73:4E + X509v3 Authority Key Identifier: + keyid:F4:09:D4:88:8F:A5:2F:CD:6E:91:EA:D1:5A:05:E3:8B:EB:28:A3:C1 + + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: + Certificate Sign, CRL Sign + Signature Algorithm: sha256WithRSAEncryption + 3a:5f:21:dc:b5:f8:a7:e5:23:89:fa:53:e2:cd:b7:e1:51:53: + d6:0d:e7:78:f7:3c:cc:85:b3:83:be:76:c8:3e:0d:e2:ab:ca: + ca:aa:41:c9:23:cc:d6:8c:53:71:72:70:43:04:48:85:28:54: + ab:b0:2a:e2:08:89:22:d5:ce:0b:79:e5:3d:61:42:db:4a:70: + c4:64:21:48:07:c4:a9:33:e4:0a:c9:a0:d6:0d:73:ef:d0:80: + d8:8c:59:c4:51:fa:5a:54:79:94:0f:ca:ff:28:0a:7e:b9:99: + 2d:8e:ca:5d:d8:4c:8f:fe:6f:e6:fd:ea:17:7f:90:f7:b1:76: + de:7f:14:82:20:c3:23:0f:ca:b5:1c:2c:34:b9:de:9f:ce:06: + 04:14:ff:6c:0b:67:ab:7c:b3:5a:51:dc:0c:66:37:56:bf:a7: + ab:12:33:03:98:c3:40:e2:4d:8d:ad:3f:19:18:41:cc:e3:ec: + 6d:5f:9e:90:df:d9:84:5b:ad:60:93:dc:24:1a:42:ce:8b:15: + 8f:42:21:f9:35:68:a6:cc:f7:4e:8d:c2:6b:b9:e6:20:30:d1: + c6:1d:86:f8:33:3b:40:cc:9f:4c:2d:5d:0a:ca:66:1c:8d:bc: + 87:3b:0b:39:e4:17:73:34:35:85:ba:22:31:a2:8b:d0:65:54: + cc:c2:c6:32 +-----BEGIN CERTIFICATE----- +MIIDvzCCAqegAwIBAgIBAjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJERTEW +MBQGA1UECgwNUGVsYWdpY29yZSBBRzESMBAGA1UECwwJQXBwIFN0b3JlMSAwHgYD +VQQDDBdQZWxhZ2ljb3JlIEFwcCBTdG9yZSBDQTEiMCAGCSqGSIb3DQEJARYTaW5m +b0BwZWxhZ2ljb3JlLmNvbTAeFw0xNTAzMTEwMTE5NDZaFw0yNTAzMDgwMTE5NDZa +MGUxCzAJBgNVBAYTAkRFMRYwFAYDVQQKDA1QZWxhZ2ljb3JlIEFHMRwwGgYDVQQL +DBNEZXZlbG9wZXIgUmVsYXRpb25zMSAwHgYDVQQDDBdQZWxhZ2ljb3JlIERldmVs +b3BlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANeIfTWSBJxY +scl+4OsJEHhvy3j2GzhaVItetbKSRZy3bAEPshZmZiQK7TeDrXknsOj/Sdto9ia1 +Ydl76kZ64lXf/MyvLkXO91lSGZxsOdZcaJ5zRXGssh3mmA5/GAbNCUnH1rZc4elP +a4CsSsc2Iccq6Lyuh/JJBWL5ASTAk1uFGrvRgRmqMQnNW+JaYXZNUI1FYHDKZGS8 +HyW/ZC5lFpUc7im7pzHsIDmB60jfk8pIbL6e314FituOxAfM/MgIFh7jBFHZC28o +GuQ4kwUAWUlggRK8SPmBCP/7WZWt81SFG3Bfz+sP6kUgObB0VQyU1z+IhbhxrLDn +mXDM9cKJa68CAwEAAaNgMF4wHQYDVR0OBBYEFIst3loYpXGpcwNUaVaHoBe/wnNO +MB8GA1UdIwQYMBaAFPQJ1IiPpS/NbpHq0VoF44vrKKPBMA8GA1UdEwEB/wQFMAMB +Af8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQA6XyHctfin5SOJ+lPi +zbfhUVPWDed49zzMhbODvnbIPg3iq8rKqkHJI8zWjFNxcnBDBEiFKFSrsCriCIki +1c4LeeU9YULbSnDEZCFIB8SpM+QKyaDWDXPv0IDYjFnEUfpaVHmUD8r/KAp+uZkt +jspd2EyP/m/m/eoXf5D3sXbefxSCIMMjD8q1HCw0ud6fzgYEFP9sC2erfLNaUdwM +ZjdWv6erEjMDmMNA4k2NrT8ZGEHM4+xtX56Q39mEW61gk9wkGkLOixWPQiH5NWim +zPdOjcJrueYgMNHGHYb4MztAzJ9MLV0KymYcjbyHOws55BdzNDWFuiIxoovQZVTM +wsYy +-----END CERTIFICATE----- diff --git a/tests/auto/sudo/CMakeLists.txt b/tests/auto/sudo/CMakeLists.txt new file mode 100644 index 00000000..5f64939c --- /dev/null +++ b/tests/auto/sudo/CMakeLists.txt @@ -0,0 +1,23 @@ + +qt_internal_add_test(tst_sudo + SOURCES + ../error-checking.h + tst_sudo.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManCommonPrivate + Qt::AppManManagerPrivate +) + +#### Keys ignored in scope 1:.:.:sudo.pro:<TRUE>: +# COVERAGE_RUNTIME = "sudo" + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_sudo CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/sudo/sudo.pro b/tests/auto/sudo/sudo.pro new file mode 100644 index 00000000..95be319b --- /dev/null +++ b/tests/auto/sudo/sudo.pro @@ -0,0 +1,11 @@ +TARGET = tst_sudo + +COVERAGE_RUNTIME = sudo + +include($$PWD/../tests.pri) + +QT *= \ + appman_common-private \ + appman_manager-private \ + +SOURCES += tst_sudo.cpp diff --git a/tests/auto/sudo/tst_sudo.cpp b/tests/auto/sudo/tst_sudo.cpp new file mode 100644 index 00000000..c6472152 --- /dev/null +++ b/tests/auto/sudo/tst_sudo.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtTest> + +#if !defined(Q_OS_LINUX) +# error "This test is Linux specific!" +#endif + +#include "utilities.h" +#include "sudo.h" + +QT_USE_NAMESPACE_AM + +static int processTimeout = 3000; + +static bool startedSudoServer = false; +static QString sudoServerError; + +// sudo RAII style +class ScopedRootPrivileges +{ +public: + ScopedRootPrivileges() + { + m_uid = getuid(); + m_gid = getgid(); + if (setresuid(0, 0, 0) || setresgid(0, 0, 0)) + QFAIL("cannot re-gain root privileges"); + } + ~ScopedRootPrivileges() + { + if (setresgid(m_gid, m_gid, 0) || setresuid(m_uid, m_uid, 0)) + QFAIL("cannot drop root privileges"); + } +private: + uid_t m_uid; + gid_t m_gid; +}; + + +class tst_Sudo : public QObject +{ + Q_OBJECT + +public: + tst_Sudo(QObject *parent = nullptr); + ~tst_Sudo(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void privileges(); + +private: + SudoClient *m_sudo = nullptr; +}; + +tst_Sudo::tst_Sudo(QObject *parent) + : QObject(parent) +{ } + +tst_Sudo::~tst_Sudo() +{ } + +void tst_Sudo::initTestCase() +{ + processTimeout *= timeoutFactor(); + + QVERIFY2(startedSudoServer, qPrintable(sudoServerError)); + m_sudo = SudoClient::instance(); + QVERIFY(m_sudo); + if (m_sudo->isFallbackImplementation()) + QSKIP("Not running with root privileges - neither directly, or SUID-root, or sudo"); +} + +void tst_Sudo::privileges() +{ + ScopedRootPrivileges sudo; +} + +void tst_Sudo::cleanupTestCase() +{ + // the real cleanup happens in ~tst_Installer, since we also need + // to call this cleanup from the crash handler +} + +static tst_Sudo *tstSudo = nullptr; + +int main(int argc, char **argv) +{ + try { + Sudo::forkServer(Sudo::DropPrivilegesRegainable); + startedSudoServer = true; + } catch (...) { } + + QCoreApplication a(argc, argv); + tstSudo = new tst_Sudo(&a); + +#ifdef Q_OS_LINUX + auto crashHandler = [](int sigNum) -> void { + // we are doing very unsafe things from a within a signal handler, but + // we've crashed anyway at this point and the alternative is that we are + // leaking mounts and attached loopback devices. + + tstSudo->~tst_Sudo(); + + if (sigNum != -1) + exit(1); + }; + + signal(SIGABRT, crashHandler); + signal(SIGSEGV, crashHandler); + signal(SIGINT, crashHandler); +#endif // Q_OS_LINUX + + return QTest::qExec(tstSudo, argc, argv); +} + +#include "tst_sudo.moc" diff --git a/tests/auto/systemreader/CMakeLists.txt b/tests/auto/systemreader/CMakeLists.txt new file mode 100644 index 00000000..ca78937f --- /dev/null +++ b/tests/auto/systemreader/CMakeLists.txt @@ -0,0 +1,23 @@ + +qt_internal_add_test(tst_systemreader + SOURCES + ../error-checking.h + tst_systemreader.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManApplicationPrivate + Qt::AppManCommonPrivate + Qt::AppManManagerPrivate + Qt::AppManMonitorPrivate + Qt::AppManWindowPrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_systemreader CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/systemreader/root/proc/1234/cgroup b/tests/auto/systemreader/root/proc/1234/cgroup new file mode 100644 index 00000000..cd5fc256 --- /dev/null +++ b/tests/auto/systemreader/root/proc/1234/cgroup @@ -0,0 +1,13 @@ +12:devices:/system.slice/run-u5853.scope +11:freezer:/ +10:memory:/system.slice/run-u5853.scope +9:net_cls,net_prio:/ +8:cpuset:/ +7:rdma:/ +6:blkio:/ +5:hugetlb:/ +4:cpu,cpuacct:/ +3:perf_event:/ +2:pids:/system.slice/run-u5853.scope +1:name=systemd:/system.slice/run-u5853.scope +0::/system.slice/run-u5853.scope diff --git a/tests/auto/systemreader/root/sys/fs/cgroup/memory/system.slice/run-u5853.scope/memory.limit_in_bytes b/tests/auto/systemreader/root/sys/fs/cgroup/memory/system.slice/run-u5853.scope/memory.limit_in_bytes new file mode 100644 index 00000000..469ca93f --- /dev/null +++ b/tests/auto/systemreader/root/sys/fs/cgroup/memory/system.slice/run-u5853.scope/memory.limit_in_bytes @@ -0,0 +1 @@ +524288000 diff --git a/tests/auto/systemreader/root/sys/fs/cgroup/memory/system.slice/run-u5853.scope/memory.stat b/tests/auto/systemreader/root/sys/fs/cgroup/memory/system.slice/run-u5853.scope/memory.stat new file mode 100644 index 00000000..e5fe0114 --- /dev/null +++ b/tests/auto/systemreader/root/sys/fs/cgroup/memory/system.slice/run-u5853.scope/memory.stat @@ -0,0 +1,33 @@ +cache 10346496 +rss 66809856 +rss_huge 0 +shmem 10067968 +mapped_file 7139328 +dirty 0 +writeback 0 +pgpgin 20165 +pgpgout 1328 +pgfault 23446 +pgmajfault 2 +inactive_anon 10047488 +active_anon 66793472 +inactive_file 278528 +active_file 0 +unevictable 0 +hierarchical_memory_limit 524288000 +total_cache 10346496 +total_rss 66809856 +total_rss_huge 0 +total_shmem 10067968 +total_mapped_file 7139328 +total_dirty 0 +total_writeback 0 +total_pgpgin 20165 +total_pgpgout 1328 +total_pgfault 23446 +total_pgmajfault 2 +total_inactive_anon 10047488 +total_active_anon 66793472 +total_inactive_file 278528 +total_active_file 0 +total_unevictable 0 diff --git a/tests/auto/systemreader/systemreader.pro b/tests/auto/systemreader/systemreader.pro new file mode 100644 index 00000000..5955d5f1 --- /dev/null +++ b/tests/auto/systemreader/systemreader.pro @@ -0,0 +1,11 @@ +TARGET = tst_systemreader + +include($$PWD/../tests.pri) + +QT *= appman_monitor-private \ + appman_manager-private \ + appman_window-private \ + appman_application-private \ + appman_common-private + +SOURCES += tst_systemreader.cpp diff --git a/tests/auto/systemreader/tst_systemreader.cpp b/tests/auto/systemreader/tst_systemreader.cpp new file mode 100644 index 00000000..6f83409a --- /dev/null +++ b/tests/auto/systemreader/tst_systemreader.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> + +#include "systemreader.h" + +QT_USE_NAMESPACE_AM + +class tst_SystemReader : public QObject +{ + Q_OBJECT + +public: + tst_SystemReader(); + +private slots: + void cgroupProcessInfo(); + void memoryReaderReadUsedValue(); + void memoryReaderGroupLimit(); +}; + +tst_SystemReader::tst_SystemReader() +{ + g_systemRootDir = qL1S(AM_TESTDATA_DIR "/root"); +} + +void tst_SystemReader::cgroupProcessInfo() +{ + auto map = fetchCGroupProcessInfo(1234); + QCOMPARE(map["memory"], QByteArray("/system.slice/run-u5853.scope")); +} + +void tst_SystemReader::memoryReaderReadUsedValue() +{ + MemoryReader memoryReader(qSL("/system.slice/run-u5853.scope")); + quint64 value = memoryReader.readUsedValue(); + QCOMPARE(value, Q_UINT64_C(66809856)); +} + +void tst_SystemReader::memoryReaderGroupLimit() +{ + MemoryReader memoryReader(qSL("/system.slice/run-u5853.scope")); + quint64 value = memoryReader.groupLimit(); + QCOMPARE(value, Q_UINT64_C(524288000)); +} + +QTEST_APPLESS_MAIN(tst_SystemReader) + +#include "tst_systemreader.moc" diff --git a/tests/auto/tests.pri b/tests/auto/tests.pri new file mode 100644 index 00000000..64ec6309 --- /dev/null +++ b/tests/auto/tests.pri @@ -0,0 +1,11 @@ +load(am-config) + +CONFIG *= console testcase +QT = core network testlib +qtHaveModule(dbus):QT *= dbus + +DEFINES *= AM_TESTDATA_DIR=\\\"$$PWD/data/\\\" +DEFINES -= QT_NO_CAST_FROM_ASCII + +HEADERS += \ + $$PWD/error-checking.h \ diff --git a/tests/auto/tests.pro b/tests/auto/tests.pro new file mode 100644 index 00000000..3bfd9083 --- /dev/null +++ b/tests/auto/tests.pro @@ -0,0 +1,56 @@ +TEMPLATE = subdirs + +load(am-config) +requires(!disable-installer) + +SUBDIRS = \ + manual \ + application \ + applicationinfo \ + main \ + runtime \ + cryptography \ + signature \ + utilities \ + installationreport \ + packagecreator \ + packageextractor \ + packager-tool \ + applicationinstaller \ + debugwrapper \ + qml \ + yaml \ + configuration \ + +linux*:SUBDIRS += \ + sudo \ + processreader \ + systemreader \ + +OTHER_FILES += \ + tests.pri \ + data/create-test-packages.sh \ + data/certificates/create-test-certificates.sh \ + data/utilities.sh \ + +# sadly, the appman-packager is too complex to build as a host tool +!cross_compile { + prepareRecursiveTarget(check) + qtPrepareTool(APPMAN_PACKAGER, appman-packager) + + unix { + macos:ctype=UTF-8 + else:ctype=C.UTF-8 + + # create test data on the fly - this is needed for the CI server + testdata.target = testdata + testdata.depends = $$PWD/data/create-test-packages.sh $$APPMAN_PACKAGER_EXE + testdata.commands = (cd $$PWD/data ; LC_CTYPE=$$ctype ./create-test-packages.sh $$APPMAN_PACKAGER) + QMAKE_EXTRA_TARGETS += testdata + + # qmake would create a default check target implicitly, but since we need 'testdata' as an + # dependency, we have to set it up explicitly + check.depends = testdata $$check.depends + } + QMAKE_EXTRA_TARGETS *= check +} diff --git a/tests/auto/utilities/CMakeLists.txt b/tests/auto/utilities/CMakeLists.txt new file mode 100644 index 00000000..7e533078 --- /dev/null +++ b/tests/auto/utilities/CMakeLists.txt @@ -0,0 +1,19 @@ + +qt_internal_add_test(tst_utilities + SOURCES + ../error-checking.h + tst_utilities.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManCommonPrivate +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_utilities CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/utilities/tst_utilities.cpp b/tests/auto/utilities/tst_utilities.cpp new file mode 100644 index 00000000..94487932 --- /dev/null +++ b/tests/auto/utilities/tst_utilities.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> + +#include "utilities.h" + +QT_USE_NAMESPACE_AM + +class tst_Utilities : public QObject +{ + Q_OBJECT + +public: + tst_Utilities(); + +private slots: +}; + + +tst_Utilities::tst_Utilities() +{ } + +QTEST_APPLESS_MAIN(tst_Utilities) + +#include "tst_utilities.moc" diff --git a/tests/auto/utilities/utilities.pro b/tests/auto/utilities/utilities.pro new file mode 100644 index 00000000..9a7c2bc0 --- /dev/null +++ b/tests/auto/utilities/utilities.pro @@ -0,0 +1,7 @@ +TARGET = tst_utilities + +include($$PWD/../tests.pri) + +QT *= appman_common-private + +SOURCES += tst_utilities.cpp diff --git a/tests/auto/yaml/CMakeLists.txt b/tests/auto/yaml/CMakeLists.txt new file mode 100644 index 00000000..74d63318 --- /dev/null +++ b/tests/auto/yaml/CMakeLists.txt @@ -0,0 +1,34 @@ + +qt_internal_add_test(tst_yaml + SOURCES + ../error-checking.h + tst_yaml.cpp + DEFINES + AM_TESTDATA_DIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../data/\\\" + PUBLIC_LIBRARIES + Qt::Network + Qt::AppManCommonPrivate +) + +# Resources: +set(qmake_immediate_resource_files + "data/cache1.yaml" + "data/cache2.yaml" + "data/test.yaml" +) + +qt_internal_add_resource(tst_yaml "qmake_immediate" + PREFIX + "/" + FILES + ${qmake_immediate_resource_files} +) + + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_yaml CONDITION TARGET Qt::DBus + PUBLIC_LIBRARIES + Qt::DBus +) diff --git a/tests/auto/yaml/data/cache1.yaml b/tests/auto/yaml/data/cache1.yaml new file mode 100644 index 00000000..b3fcddb9 --- /dev/null +++ b/tests/auto/yaml/data/cache1.yaml @@ -0,0 +1,13 @@ +name: cache1 +file: ${FILE} + +## content for a merge test +#bool: true +#list: [ 1, 2 ] +#map: +# key1: value1 +# key2: value2 +# key3: +# key31: value31 +# key32: value32 +# key4: 4 diff --git a/tests/auto/yaml/data/cache2.yaml b/tests/auto/yaml/data/cache2.yaml new file mode 100644 index 00000000..8ce7bae3 --- /dev/null +++ b/tests/auto/yaml/data/cache2.yaml @@ -0,0 +1,14 @@ +name: cache2 +file: ${FILE} + +## content for a merge test +#bool: false +#list: [ 3, 4, 5 ] +#map: +# key1: value1 +# key2: not-value2 +# key3: +# key31: [ 5, 6 ] +# key32: not-value32 +# key33: true +# key5: 5 diff --git a/tests/auto/yaml/data/test.yaml b/tests/auto/yaml/data/test.yaml new file mode 100644 index 00000000..b16e3d39 --- /dev/null +++ b/tests/auto/yaml/data/test.yaml @@ -0,0 +1,45 @@ +formatVersion: 42 +formatType: testfile +--- +dec: 10 +hex: 0x10 +oct: 010 +bin: 0b10 +float1: 10.10 +float2: 0.10 +float3: .10 +number-separators: 1_234_567 +bool-true: true +bool-yes: yes +bool-false: false +bool-no: no +null-literal: null +null-tilde: ~ +string-unquoted: unquoted +string-singlequoted: 'singlequoted' +string-doublequoted: "doublequoted" +list-int: +- 1 +- 2 +- 3 +list-mixed: +- 1 +- two +- [yes, ~] +map1: + a: 1 + b: two + c: [ 1, 2, 3] + +extended: + ext-string: 'ext string' + +stringlist-string: string +stringlist-list1: [ string ] +stringlist-list2: [ string1, string2 ] + +list-of-maps: +- index: 1 + name: '1' +- index: 2 + name: '2' diff --git a/tests/auto/yaml/tst_yaml.cpp b/tests/auto/yaml/tst_yaml.cpp new file mode 100644 index 00000000..b07b5274 --- /dev/null +++ b/tests/auto/yaml/tst_yaml.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtApplicationManager module of the Qt Toolkit. +** +** $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 <QtCore> +#include <QtTest> +#include <QThreadPool> + +#include "qtyaml.h" +#include "configcache.h" +#include "exception.h" +#include "global.h" + +QT_USE_NAMESPACE_AM + +class tst_Yaml : public QObject +{ + Q_OBJECT + +public: + tst_Yaml(); + +private slots: + void parser(); + void documentParser(); + void cache(); + void mergedCache(); + void parallel(); +}; + + +tst_Yaml::tst_Yaml() +{ } + +void tst_Yaml::parser() +{ + static const QVariant vnull = QVariant::fromValue(nullptr); + + QVector<QPair<const char *, QVariant>> tests = { + { "dec", QVariant::fromValue<int>(10) }, + { "hex", QVariant::fromValue<int>(16) }, + { "bin", QVariant::fromValue<int>(2) }, + { "oct", QVariant::fromValue<int>(8) }, + { "float1", QVariant::fromValue<double>(10.1) }, + { "float2", QVariant::fromValue<double>(.1) }, + { "float3", QVariant::fromValue<double>(.1) }, + { "number-separators", QVariant::fromValue<int>(1234567) }, + { "bool-true", true }, + { "bool-yes", true }, + { "bool-false", false }, + { "bool-no", false }, + { "null-literal", vnull }, + { "null-tilde", vnull }, + { "string-unquoted", QVariant::fromValue<QString>("unquoted") }, + { "string-singlequoted", QVariant::fromValue<QString>("singlequoted") }, + { "string-doublequoted", QVariant::fromValue<QString>("doublequoted") }, + { "list-int", QVariantList { 1, 2, 3 } }, + { "list-mixed", QVariantList { 1, "two", QVariantList { true, vnull } } }, + { "map1", QVariantMap { { "a", 1 }, { "b", "two" }, { "c", QVariantList { 1, 2, 3 } } } } + }; + + try { + QFile f(":/data/test.yaml"); + QVERIFY2(f.open(QFile::ReadOnly), qPrintable(f.errorString())); + QByteArray ba = f.readAll(); + QVERIFY(!ba.isEmpty()); + YamlParser p(ba); + auto header = p.parseHeader(); + + QCOMPARE(header.first, "testfile"); + QCOMPARE(header.second, 42); + + QVERIFY(p.nextDocument()); + + YamlParser::Fields fields; + for (const auto &pair : tests) { + YamlParser::FieldType type = YamlParser::Scalar; + if (pair.second.metaType() == QMetaType::fromType<QVariantList>()) + type = YamlParser::List; + else if (pair.second.metaType() == QMetaType::fromType<QVariantMap>()) + type = YamlParser::Map; + QVariant value = pair.second; + + fields.emplace_back(pair.first, true, type, [type, value](YamlParser *p) { + switch (type) { + case YamlParser::Scalar: { + QVERIFY(p->isScalar()); + QVariant v = p->parseScalar(); + QCOMPARE(int(v.metaType().id()), value.metaType().id()); + QVERIFY(v == value); + break; + } + case YamlParser::List: { + QVERIFY(p->isList()); + QVariantList vl = p->parseList(); + QVERIFY(vl == value.toList()); + break; + } + case YamlParser::Map: { + QVERIFY(p->isMap()); + QVariantMap vm = p->parseMap(); + QVERIFY(vm == value.toMap()); + break; + } + } + }); + } + fields.emplace_back("extended", true, YamlParser::Map, [](YamlParser *p) { + YamlParser::Fields extFields = { + { "ext-string", true, YamlParser::Scalar, [](YamlParser *p) { + QVERIFY(p->isScalar()); + QVariant v = p->parseScalar(); + QCOMPARE(v.metaType(), QMetaType::fromType<QString>()); + QCOMPARE(v.toString(), "ext string"); + } } + }; + p->parseFields(extFields); + }); + + fields.emplace_back("stringlist-string", true, YamlParser::Scalar | YamlParser::List, [](YamlParser *p) { + QCOMPARE(p->parseStringOrStringList(), QStringList { "string" }); + }); + fields.emplace_back("stringlist-list1", true, YamlParser::Scalar | YamlParser::List, [](YamlParser *p) { + QCOMPARE(p->parseStringOrStringList(), QStringList { "string" }); + }); + fields.emplace_back("stringlist-list2", true, YamlParser::Scalar | YamlParser::List, [](YamlParser *p) { + QCOMPARE(p->parseStringOrStringList(), QStringList({ "string1", "string2" })); + }); + + fields.emplace_back("list-of-maps", true, YamlParser::List, [](YamlParser *p) { + int index = 0; + p->parseList([&index](YamlParser *p) { + ++index; + YamlParser::Fields lomFields = { + { "index", true, YamlParser::Scalar, [&index](YamlParser *p) { + QCOMPARE(p->parseScalar().toInt(), index); + } }, + { "name", true, YamlParser::Scalar, [&index](YamlParser *p) { + QCOMPARE(p->parseScalar().toString(), QString::number(index)); + } } + }; + p->parseFields(lomFields); + }); + QCOMPARE(index, 2); + }); + + p.parseFields(fields); + + QVERIFY(!p.nextDocument()); + + } catch (const Exception &e) { + QVERIFY2(false, e.what()); + } +} + +static const QVariant vnull = QVariant::fromValue(nullptr); + +static const QVariantMap testHeaderDoc = { + { "formatVersion", 42 }, { "formatType", "testfile" } +}; + +static const QVariantMap testMainDoc = { + { "dec", 10 }, + { "hex", 16 }, + { "bin", 2 }, + { "oct", 8 }, + { "float1", 10.1 }, + { "float2", .1 }, + { "float3", .1 }, + { "number-separators", 1234567 }, + { "bool-true", true }, + { "bool-yes", true }, + { "bool-false", false }, + { "bool-no", false }, + { "null-literal", vnull }, + { "null-tilde", vnull }, + { "string-unquoted", "unquoted" }, + { "string-singlequoted", "singlequoted" }, + { "string-doublequoted", "doublequoted" }, + { "list-int", QVariantList { 1, 2, 3 } }, + { "list-mixed", QVariantList { 1, qSL("two"), QVariantList { true, vnull } } }, + { "map1", QVariantMap { { "a", 1 }, { "b", "two" }, { "c", QVariantList { 1, 2, 3 } } } }, + + + { "extended", QVariantMap { { "ext-string", "ext string" } } }, + + { "stringlist-string", "string" }, + { "stringlist-list1", QVariantList { "string" } }, + { "stringlist-list2", QVariantList { "string1", "string2" } }, + + { "list-of-maps", QVariantList { QVariantMap { { "index", 1 }, { "name", "1" } }, + QVariantMap { { "index", 2 }, { "name", "2" } } } } +}; + +void tst_Yaml::documentParser() +{ + try { + QFile f(":/data/test.yaml"); + QVERIFY2(f.open(QFile::ReadOnly), qPrintable(f.errorString())); + QByteArray ba = f.readAll(); + QVERIFY(!ba.isEmpty()); + QVector<QVariant> docs = YamlParser::parseAllDocuments(ba); + QCOMPARE(docs.size(), 2); + QCOMPARE(docs.at(0).toMap().size(), 2); + + QCOMPARE(testHeaderDoc, docs.at(0).toMap()); + QCOMPARE(testMainDoc, docs.at(1).toMap()); + + } catch (const Exception &e) { + QVERIFY2(false, e.what()); + } +} +struct CacheTest +{ + QString name; + QString file; +}; + +// GCC < 7 bug, currently still in RHEL7, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 +// this should simply be: +// template<> class QT_PREPEND_NAMESPACE_AM(ConfigCacheAdaptor<CacheTest>) + +QT_BEGIN_NAMESPACE_AM +template<> class ConfigCacheAdaptor<CacheTest> +{ +public: + CacheTest *loadFromSource(QIODevice *source, const QString &fileName) + { + std::unique_ptr<CacheTest> ct(new CacheTest); + YamlParser p(source->readAll(), fileName); + p.nextDocument(); + p.parseFields({ { "name", true, YamlParser::Scalar, [&ct](YamlParser *p) { + ct->name = p->parseScalar().toString(); } }, + { "file", true, YamlParser::Scalar, [&ct](YamlParser *p) { + ct->file = p->parseScalar().toString(); } } + }); + return ct.release(); + } + CacheTest *loadFromCache(QDataStream &ds) + { + CacheTest *ct = new CacheTest; + ds >> ct->name >> ct->file; + return ct; + } + void saveToCache(QDataStream &ds, const CacheTest *ct) + { + ds << ct->name << ct->file; + } + + void merge(CacheTest *ct1, const CacheTest *ct2) + { + ct1->name = ct2->name; + ct1->file = ct1->file + qSL(",") + ct2->file; + } + void preProcessSourceContent(QByteArray &sourceContent, const QString &fileName) + { + sourceContent.replace("${FILE}", fileName.toUtf8()); + } +}; +QT_END_NAMESPACE_AM + +void tst_Yaml::cache() +{ + QStringList files = { ":/data/cache1.yaml", ":/data/cache2.yaml" }; + + for (int step = 0; step < 2; ++step) { + try { + ConfigCache<CacheTest> cache(files, "cache-test", "CTST", 1, + step == 0 ? AbstractConfigCache::ClearCache + : AbstractConfigCache::None); + cache.parse(); + QVERIFY(cache.parseReadFromCache() == (step == 1)); + QVERIFY(cache.parseWroteToCache() == (step == 0)); + CacheTest *ct1 = cache.takeResult(0); + QVERIFY(ct1); + QCOMPARE(ct1->name, "cache1"); + QCOMPARE(ct1->file, ":/data/cache1.yaml"); + CacheTest *ct2 = cache.takeResult(1); + QVERIFY(ct2); + QCOMPARE(ct2->name, "cache2"); + QCOMPARE(ct2->file, ":/data/cache2.yaml"); + } catch (const Exception &e) { + QVERIFY2(false, e.what()); + } + } + + ConfigCache<CacheTest> wrongVersion(files, "cache-test", "CTST", 2, AbstractConfigCache::None); + QTest::ignoreMessage(QtWarningMsg, "Failed to read cache: failed to parse cache header"); + wrongVersion.parse(); + QVERIFY(!wrongVersion.parseReadFromCache()); + + ConfigCache<CacheTest> wrongType(files, "cache-test", "XTST", 1, AbstractConfigCache::None); + QTest::ignoreMessage(QtWarningMsg, "Failed to read cache: failed to parse cache header"); + wrongType.parse(); + QVERIFY(!wrongType.parseReadFromCache()); +} + +void tst_Yaml::mergedCache() +{ + QStringList files = { ":/data/cache1.yaml", ":/data/cache2.yaml" }; + + for (int step = 0; step < 4; ++step) { + AbstractConfigCache::Options options = AbstractConfigCache::MergedResult; + if (step % 2 == 0) + options |= AbstractConfigCache::ClearCache; + if (step == 2) + std::reverse(files.begin(), files.end()); + + try { + ConfigCache<CacheTest> cache(files, "cache-test", "MTST", 1, options); + cache.parse(); + QVERIFY(cache.parseReadFromCache() == (step % 2 == 1)); + QVERIFY(cache.parseWroteToCache() == (step % 2 == 0)); + CacheTest *ct = cache.takeMergedResult(); + QVERIFY(ct); + QCOMPARE(ct->name, QFileInfo(files.last()).baseName()); + QCOMPARE(ct->file, files.join(qSL(","))); + } catch (const Exception &e) { + QVERIFY2(false, e.what()); + } + } +} + +class YamlRunnable : public QRunnable +{ +public: + YamlRunnable(const QByteArray &yaml, QAtomicInt &success, QAtomicInt &fail) + : m_yaml(yaml) + , m_success(success) + , m_fail(fail) + { } + + void run() override + { + QVector<QVariant> docs; + try { + docs = YamlParser::parseAllDocuments(m_yaml); + } catch (...) { + docs.clear(); + } + if ((docs.size() == 2) + && (docs.at(0).toMap().size() == 2) + && (testHeaderDoc == docs.at(0).toMap()) + && (testMainDoc == docs.at(1).toMap())) { + m_success.fetchAndAddOrdered(1); + } else { + m_fail.fetchAndAddOrdered(1); + } + } +private: + const QByteArray m_yaml; + QAtomicInt &m_success; + QAtomicInt &m_fail; +}; + +void tst_Yaml::parallel() +{ + QFile f(":/data/test.yaml"); + QVERIFY2(f.open(QFile::ReadOnly), qPrintable(f.errorString())); + QByteArray ba = f.readAll(); + QVERIFY(!ba.isEmpty()); + + constexpr int threadCount = 16; + + QAtomicInt success; + QAtomicInt fail; + + QThreadPool tp; + if (tp.maxThreadCount() < threadCount) + tp.setMaxThreadCount(threadCount); + + for (int i = 0; i < threadCount; ++i) + tp.start(new YamlRunnable(ba, success, fail)); + + QVERIFY(tp.waitForDone(5000)); + QCOMPARE(fail.loadAcquire(), 0); + QCOMPARE(success.loadAcquire(), threadCount); +} + +QTEST_MAIN(tst_Yaml) + +#include "tst_yaml.moc" diff --git a/tests/auto/yaml/yaml.pro b/tests/auto/yaml/yaml.pro new file mode 100644 index 00000000..5cc4bbb5 --- /dev/null +++ b/tests/auto/yaml/yaml.pro @@ -0,0 +1,12 @@ +TARGET = tst_yaml + +include($$PWD/../tests.pri) + +QT *= appman_common-private + +SOURCES += tst_yaml.cpp + +RESOURCES += \ + data/test.yaml \ + data/cache1.yaml \ + data/cache2.yaml \ |