summaryrefslogtreecommitdiff
path: root/tests/auto
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto')
-rw-r--r--tests/auto/CMakeLists.txt23
-rw-r--r--tests/auto/application/CMakeLists.txt30
-rw-r--r--tests/auto/application/application.pro13
-rw-r--r--tests/auto/application/icon.pngbin0 -> 68 bytes
-rw-r--r--tests/auto/application/info.yaml10
-rw-r--r--tests/auto/application/tst_application.cpp135
-rw-r--r--tests/auto/application/tst_application.qrc5
-rw-r--r--tests/auto/applicationinfo/CMakeLists.txt21
-rw-r--r--tests/auto/applicationinfo/applicationinfo.pro10
-rw-r--r--tests/auto/applicationinfo/tst_applicationinfo.cpp219
-rw-r--r--tests/auto/applicationinstaller/CMakeLists.txt25
-rw-r--r--tests/auto/applicationinstaller/applicationinstaller.pro13
-rw-r--r--tests/auto/applicationinstaller/tst_applicationinstaller.cpp793
-rw-r--r--tests/auto/configuration/CMakeLists.txt31
-rw-r--r--tests/auto/configuration/configuration.pro12
-rw-r--r--tests/auto/configuration/data/config1.yaml98
-rw-r--r--tests/auto/configuration/data/config2.yaml88
-rw-r--r--tests/auto/configuration/data/empty.yaml3
-rw-r--r--tests/auto/configuration/tst_configuration.cpp523
-rw-r--r--tests/auto/cryptography/CMakeLists.txt20
-rw-r--r--tests/auto/cryptography/cryptography.pro7
-rw-r--r--tests/auto/cryptography/tst_cryptography.cpp62
-rw-r--r--tests/auto/debugwrapper/CMakeLists.txt19
-rw-r--r--tests/auto/debugwrapper/debugwrapper.pro8
-rw-r--r--tests/auto/debugwrapper/tst_debugwrapper.cpp151
-rw-r--r--tests/auto/error-checking.h45
-rw-r--r--tests/auto/installationreport/CMakeLists.txt21
-rw-r--r--tests/auto/installationreport/installationreport.pro10
-rw-r--r--tests/auto/installationreport/tst_installationreport.cpp118
-rw-r--r--tests/auto/main/CMakeLists.txt39
-rw-r--r--tests/auto/main/am-config.yaml10
-rw-r--r--tests/auto/main/builtin-apps/hello-world.red/icon.pngbin0 -> 1027 bytes
-rw-r--r--tests/auto/main/builtin-apps/hello-world.red/info.yaml24
-rw-r--r--tests/auto/main/builtin-apps/hello-world.red/main.qml11
-rw-r--r--tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/.installation-report.yaml14
-rw-r--r--tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/icon.pngbin0 -> 1027 bytes
-rw-r--r--tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/info.yaml9
-rw-r--r--tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/main.qml11
-rw-r--r--tests/auto/main/dir-with-update-already-installed/docs/hello-world.red/placeholder0
-rw-r--r--tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/icon.pngbin0 -> 1027 bytes
-rw-r--r--tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/info.yaml9
-rw-r--r--tests/auto/main/dummy.qml3
-rw-r--r--tests/auto/main/main.pro20
-rw-r--r--tests/auto/main/main.qrc5
-rw-r--r--tests/auto/main/tst_main.cpp384
-rw-r--r--tests/auto/packagecreator/CMakeLists.txt21
-rw-r--r--tests/auto/packagecreator/packagecreator.pro10
-rw-r--r--tests/auto/packagecreator/tst_packagecreator.cpp188
-rw-r--r--tests/auto/packageextractor/CMakeLists.txt21
-rw-r--r--tests/auto/packageextractor/packageextractor.pro10
-rw-r--r--tests/auto/packageextractor/tst_packageextractor.cpp312
-rw-r--r--tests/auto/packager-tool/CMakeLists.txt23
-rw-r--r--tests/auto/packager-tool/packager-tool.pro15
-rw-r--r--tests/auto/packager-tool/tst_packager-tool.cpp414
-rw-r--r--tests/auto/processreader/CMakeLists.txt23
-rw-r--r--tests/auto/processreader/advanced.smaps252
-rw-r--r--tests/auto/processreader/basic.smaps352
-rw-r--r--tests/auto/processreader/invalid.smaps16
-rw-r--r--tests/auto/processreader/processreader.pro11
-rw-r--r--tests/auto/processreader/tst_processreader.cpp144
-rw-r--r--tests/auto/qml/CMakeLists.txt18
-rw-r--r--tests/auto/qml/configs/CMakeLists.txt23
-rw-r--r--tests/auto/qml/configs/am-config-nodbus.yaml6
-rw-r--r--tests/auto/qml/configs/am-config.yaml11
-rw-r--r--tests/auto/qml/configs/apps/test.configs.app/app.qml78
-rw-r--r--tests/auto/qml/configs/apps/test.configs.app/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/configs/apps/test.configs.app/info.yaml9
-rw-r--r--tests/auto/qml/configs/configs.pro15
-rw-r--r--tests/auto/qml/configs/tst_configs.qml116
-rw-r--r--tests/auto/qml/crash/CMakeLists.txt17
-rw-r--r--tests/auto/qml/crash/am-config.yaml8
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/app.qml57
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/info.yaml12
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/terminator2/CMakeLists.txt30
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/terminator2/Terminator/qmldir2
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmldir2
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmlterminator2.cpp114
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/terminator2/qmlterminator2.h75
-rw-r--r--tests/auto/qml/crash/apps/tld.test.crash/terminator2/terminator2.pro18
-rw-r--r--tests/auto/qml/crash/crash.pro5
-rw-r--r--tests/auto/qml/crash/tst_crash.qml83
-rw-r--r--tests/auto/qml/installer/CMakeLists.txt16
-rw-r--r--tests/auto/qml/installer/am-config.yaml10
-rw-r--r--tests/auto/qml/installer/apps/hello-world.red/app1.qml35
-rw-r--r--tests/auto/qml/installer/apps/hello-world.red/icon1.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/installer/apps/hello-world.red/info.yaml10
-rw-r--r--tests/auto/qml/installer/installer.pro6
-rw-r--r--tests/auto/qml/installer/tst_installer.qml252
-rw-r--r--tests/auto/qml/intents/CMakeLists.txt23
-rw-r--r--tests/auto/qml/intents/am-config-quick.yaml6
-rw-r--r--tests/auto/qml/intents/am-config.yaml23
-rw-r--r--tests/auto/qml/intents/apps/cannot-start/cannot-start1
-rw-r--r--tests/auto/qml/intents/apps/cannot-start/icon.pngbin0 -> 5138 bytes
-rw-r--r--tests/auto/qml/intents/apps/cannot-start/info.yaml12
-rw-r--r--tests/auto/qml/intents/apps/intents1/icon.pngbin0 -> 5138 bytes
-rw-r--r--tests/auto/qml/intents/apps/intents1/info.yaml20
-rw-r--r--tests/auto/qml/intents/apps/intents1/intents1.qml59
-rw-r--r--tests/auto/qml/intents/apps/intents2/icon.pngbin0 -> 5138 bytes
-rw-r--r--tests/auto/qml/intents/apps/intents2/info.yaml16
-rw-r--r--tests/auto/qml/intents/apps/intents2/intents2.qml54
-rw-r--r--tests/auto/qml/intents/intents.pro12
-rw-r--r--tests/auto/qml/intents/tst_intents.qml241
-rw-r--r--tests/auto/qml/lifecycle/CMakeLists.txt16
-rw-r--r--tests/auto/qml/lifecycle/am-config.yaml8
-rw-r--r--tests/auto/qml/lifecycle/apps/tld.test.lifecycle/app.qml39
-rw-r--r--tests/auto/qml/lifecycle/apps/tld.test.lifecycle/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/lifecycle/apps/tld.test.lifecycle/info.yaml9
-rw-r--r--tests/auto/qml/lifecycle/lifecycle.pro5
-rw-r--r--tests/auto/qml/lifecycle/tst_lifecycle.qml134
-rw-r--r--tests/auto/qml/processtitle/CMakeLists.txt17
-rw-r--r--tests/auto/qml/processtitle/am-config-quick.yaml10
-rw-r--r--tests/auto/qml/processtitle/am-config.yaml5
-rw-r--r--tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/app.qml39
-rw-r--r--tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/info.yaml9
-rw-r--r--tests/auto/qml/processtitle/apps/test.processtitle.app/app.qml39
-rw-r--r--tests/auto/qml/processtitle/apps/test.processtitle.app/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/processtitle/apps/test.processtitle.app/info.yaml9
-rw-r--r--tests/auto/qml/processtitle/processtitle.pro7
-rw-r--r--tests/auto/qml/processtitle/tst_processtitle.qml105
-rw-r--r--tests/auto/qml/qml.pro20
-rw-r--r--tests/auto/qml/quicklaunch/CMakeLists.txt16
-rw-r--r--tests/auto/qml/quicklaunch/am-config.yaml9
-rw-r--r--tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/app.qml42
-rw-r--r--tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/info.yaml10
-rw-r--r--tests/auto/qml/quicklaunch/quicklaunch.pro5
-rw-r--r--tests/auto/qml/quicklaunch/tst_quicklaunch.qml89
-rw-r--r--tests/auto/qml/resources/CMakeLists.txt37
-rw-r--r--tests/auto/qml/resources/am-config.yaml22
-rw-r--r--tests/auto/qml/resources/appcommon/CMakeLists.txt2
-rw-r--r--tests/auto/qml/resources/appcommon/Quicklaunch.qml36
-rw-r--r--tests/auto/qml/resources/appcommon/appcommon.pro7
-rw-r--r--tests/auto/qml/resources/appcommon/appcommonfile.qrc7
-rw-r--r--tests/auto/qml/resources/appcommon/qml/common/CommonObj.qml35
-rw-r--r--tests/auto/qml/resources/appcommon/qml/common/qmldir2
-rw-r--r--tests/auto/qml/resources/apps/app1/CMakeLists.txt32
-rw-r--r--tests/auto/qml/resources/apps/app1/app1.pro11
-rw-r--r--tests/auto/qml/resources/apps/app1/app1.qml39
-rw-r--r--tests/auto/qml/resources/apps/app1/app1file.qrc7
-rw-r--r--tests/auto/qml/resources/apps/app1/app1plugin.qrc5
-rw-r--r--tests/auto/qml/resources/apps/app1/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/resources/apps/app1/info.yaml17
-rw-r--r--tests/auto/qml/resources/apps/app1/qml/forms/AquaRect.qml35
-rw-r--r--tests/auto/qml/resources/apps/app1/qml/forms/YellowRect.qml35
-rw-r--r--tests/auto/qml/resources/apps/app1/qml/forms/qmldir4
-rw-r--r--tests/auto/qml/resources/apps/app2/BlueRect.qml35
-rw-r--r--tests/auto/qml/resources/apps/app2/CMakeLists.txt2
-rw-r--r--tests/auto/qml/resources/apps/app2/app2.pro8
-rw-r--r--tests/auto/qml/resources/apps/app2/app2.qml46
-rw-r--r--tests/auto/qml/resources/apps/app2/app2.qrc6
-rw-r--r--tests/auto/qml/resources/apps/app2/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/resources/apps/app2/info.yaml15
-rw-r--r--tests/auto/qml/resources/apps/app2/qml/appwidgets/GreenRect.qml35
-rw-r--r--tests/auto/qml/resources/apps/app2/qml/appwidgets/qmldir2
-rw-r--r--tests/auto/qml/resources/qml/widgets/MagentaRect.qml35
-rw-r--r--tests/auto/qml/resources/qml/widgets/RedRect.qml35
-rw-r--r--tests/auto/qml/resources/qml/widgets/qmldir3
-rw-r--r--tests/auto/qml/resources/relative/elements/LinenRect.qml35
-rw-r--r--tests/auto/qml/resources/relative/elements/qmldir2
-rw-r--r--tests/auto/qml/resources/resources.pro5
-rw-r--r--tests/auto/qml/resources/systemuifile.qrc6
-rw-r--r--tests/auto/qml/resources/systemuiplugin.qrc5
-rw-r--r--tests/auto/qml/resources/test.pro18
-rw-r--r--tests/auto/qml/resources/tst_resource.qml81
-rw-r--r--tests/auto/qml/simple/CMakeLists.txt16
-rw-r--r--tests/auto/qml/simple/am-config.yaml56
-rw-r--r--tests/auto/qml/simple/apps/tld.test.simple1/app1.qml50
-rw-r--r--tests/auto/qml/simple/apps/tld.test.simple1/icon.pngbin0 -> 5138 bytes
-rw-r--r--tests/auto/qml/simple/apps/tld.test.simple1/info.yaml23
-rw-r--r--tests/auto/qml/simple/apps/tld.test.simple2/app.qml50
-rw-r--r--tests/auto/qml/simple/apps/tld.test.simple2/icon.pngbin0 -> 5138 bytes
-rw-r--r--tests/auto/qml/simple/apps/tld.test.simple2/info.yaml13
-rw-r--r--tests/auto/qml/simple/simple.pro5
-rw-r--r--tests/auto/qml/simple/text-file.txt1
-rw-r--r--tests/auto/qml/simple/tst_applicationmanager.qml487
-rw-r--r--tests/auto/qml/windowitem/CMakeLists.txt16
-rw-r--r--tests/auto/qml/windowitem/am-config.yaml13
-rw-r--r--tests/auto/qml/windowitem/apps/test.windowitem.app/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowitem/apps/test.windowitem.app/info.yaml9
-rw-r--r--tests/auto/qml/windowitem/apps/test.windowitem.app/main.qml64
-rw-r--r--tests/auto/qml/windowitem/apps/test.windowitem.multiwin/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowitem/apps/test.windowitem.multiwin/info.yaml9
-rw-r--r--tests/auto/qml/windowitem/apps/test.windowitem.multiwin/main.qml49
-rw-r--r--tests/auto/qml/windowitem/tst_windowitem.qml482
-rw-r--r--tests/auto/qml/windowitem/windowitem.pro5
-rw-r--r--tests/auto/qml/windowitem2/CMakeLists.txt16
-rw-r--r--tests/auto/qml/windowitem2/am-config.yaml11
-rw-r--r--tests/auto/qml/windowitem2/apps/test.windowitem2.app/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowitem2/apps/test.windowitem2.app/info.yaml9
-rw-r--r--tests/auto/qml/windowitem2/apps/test.windowitem2.app/main.qml40
-rw-r--r--tests/auto/qml/windowitem2/tst_windowitem2.qml130
-rw-r--r--tests/auto/qml/windowitem2/windowitem2.pro5
-rw-r--r--tests/auto/qml/windowmanager/CMakeLists.txt15
-rw-r--r--tests/auto/qml/windowmanager/IviApplicationExtension.qml43
-rw-r--r--tests/auto/qml/windowmanager/tst_windowmanager.qml66
-rw-r--r--tests/auto/qml/windowmanager/windowmanager.pro5
-rw-r--r--tests/auto/qml/windowmapping/CMakeLists.txt16
-rw-r--r--tests/auto/qml/windowmapping/am-config.yaml11
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.amwin/amwin.qml57
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.amwin/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.amwin/info.yaml9
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.amwin2/amwin2.qml69
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.amwin2/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.amwin2/info.yaml9
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.loader/SubWin.qml37
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.loader/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.loader/info.yaml9
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.loader/loader.qml54
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.ping/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.ping/info.yaml9
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.ping/ping.qml44
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.qtobject/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.qtobject/info.yaml9
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.qtobject/qtobject.qml60
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.rectangle/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.rectangle/info.yaml9
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.rectangle/rectangle.qml55
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.window/icon.pngbin0 -> 1486 bytes
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.window/info.yaml9
-rw-r--r--tests/auto/qml/windowmapping/apps/test.winmap.window/window.qml57
-rw-r--r--tests/auto/qml/windowmapping/tst_windowmapping.qml321
-rw-r--r--tests/auto/qml/windowmapping/windowmapping.pro12
-rw-r--r--tests/auto/runtime/CMakeLists.txt22
-rw-r--r--tests/auto/runtime/runtime.pro11
-rw-r--r--tests/auto/runtime/tst_runtime.cpp177
-rw-r--r--tests/auto/signature/CMakeLists.txt41
-rw-r--r--tests/auto/signature/create-test-data.sh40
-rw-r--r--tests/auto/signature/signature-openssl.p7bin0 -> 2555 bytes
-rw-r--r--tests/auto/signature/signature-securityframework.p7bin0 -> 3210 bytes
-rw-r--r--tests/auto/signature/signature-wincrypt.p7bin0 -> 2316 bytes
-rw-r--r--tests/auto/signature/signature.pro11
-rw-r--r--tests/auto/signature/signing-no-key.p12bin0 -> 2144 bytes
-rw-r--r--tests/auto/signature/signing.p12bin0 -> 3628 bytes
-rw-r--r--tests/auto/signature/tst_signature.cpp171
-rw-r--r--tests/auto/signature/tst_signature.qrc10
-rw-r--r--tests/auto/signature/verifying.crt105
-rw-r--r--tests/auto/sudo/CMakeLists.txt23
-rw-r--r--tests/auto/sudo/sudo.pro11
-rw-r--r--tests/auto/sudo/tst_sudo.cpp148
-rw-r--r--tests/auto/systemreader/CMakeLists.txt23
-rw-r--r--tests/auto/systemreader/root/proc/1234/cgroup13
-rw-r--r--tests/auto/systemreader/root/sys/fs/cgroup/memory/system.slice/run-u5853.scope/memory.limit_in_bytes1
-rw-r--r--tests/auto/systemreader/root/sys/fs/cgroup/memory/system.slice/run-u5853.scope/memory.stat33
-rw-r--r--tests/auto/systemreader/systemreader.pro11
-rw-r--r--tests/auto/systemreader/tst_systemreader.cpp78
-rw-r--r--tests/auto/tests.pri11
-rw-r--r--tests/auto/tests.pro56
-rw-r--r--tests/auto/utilities/CMakeLists.txt19
-rw-r--r--tests/auto/utilities/tst_utilities.cpp54
-rw-r--r--tests/auto/utilities/utilities.pro7
-rw-r--r--tests/auto/yaml/CMakeLists.txt34
-rw-r--r--tests/auto/yaml/data/cache1.yaml13
-rw-r--r--tests/auto/yaml/data/cache2.yaml14
-rw-r--r--tests/auto/yaml/data/test.yaml45
-rw-r--r--tests/auto/yaml/tst_yaml.cpp408
-rw-r--r--tests/auto/yaml/yaml.pro12
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
new file mode 100644
index 00000000..909c66db
--- /dev/null
+++ b/tests/auto/application/icon.png
Binary files differ
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
new file mode 100644
index 00000000..04ca44dd
--- /dev/null
+++ b/tests/auto/main/builtin-apps/hello-world.red/icon.png
Binary files differ
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
new file mode 100644
index 00000000..04ca44dd
--- /dev/null
+++ b/tests/auto/main/dir-with-update-already-installed/apps/hello-world.red/icon.png
Binary files differ
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
new file mode 100644
index 00000000..04ca44dd
--- /dev/null
+++ b/tests/auto/main/dir-with-update-already-installed/manifests/hello-world.red/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/configs/apps/test.configs.app/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/crash/apps/tld.test.crash/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/installer/apps/hello-world.red/icon1.png
Binary files differ
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
new file mode 100644
index 00000000..adb840ce
--- /dev/null
+++ b/tests/auto/qml/intents/apps/cannot-start/icon.png
Binary files differ
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
new file mode 100644
index 00000000..adb840ce
--- /dev/null
+++ b/tests/auto/qml/intents/apps/intents1/icon.png
Binary files differ
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
new file mode 100644
index 00000000..adb840ce
--- /dev/null
+++ b/tests/auto/qml/intents/apps/intents2/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/lifecycle/apps/tld.test.lifecycle/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/processtitle/apps/appappapp1appappapp2appappapp3appappapp4appappapp5appappapp6appappapp7/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/processtitle/apps/test.processtitle.app/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/quicklaunch/apps/tld.test.quicklaunch/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/resources/apps/app1/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/resources/apps/app2/icon.png
Binary files differ
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
new file mode 100644
index 00000000..adb840ce
--- /dev/null
+++ b/tests/auto/qml/simple/apps/tld.test.simple1/icon.png
Binary files differ
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
new file mode 100644
index 00000000..adb840ce
--- /dev/null
+++ b/tests/auto/qml/simple/apps/tld.test.simple2/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowitem/apps/test.windowitem.app/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowitem/apps/test.windowitem.multiwin/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowitem2/apps/test.windowitem2.app/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowmapping/apps/test.winmap.amwin/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowmapping/apps/test.winmap.amwin2/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowmapping/apps/test.winmap.loader/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowmapping/apps/test.winmap.ping/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowmapping/apps/test.winmap.qtobject/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowmapping/apps/test.winmap.rectangle/icon.png
Binary files differ
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
new file mode 100644
index 00000000..c1397153
--- /dev/null
+++ b/tests/auto/qml/windowmapping/apps/test.winmap.window/icon.png
Binary files differ
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
new file mode 100644
index 00000000..294310cf
--- /dev/null
+++ b/tests/auto/signature/signature-openssl.p7
Binary files differ
diff --git a/tests/auto/signature/signature-securityframework.p7 b/tests/auto/signature/signature-securityframework.p7
new file mode 100644
index 00000000..ded48dd8
--- /dev/null
+++ b/tests/auto/signature/signature-securityframework.p7
Binary files differ
diff --git a/tests/auto/signature/signature-wincrypt.p7 b/tests/auto/signature/signature-wincrypt.p7
new file mode 100644
index 00000000..e3ef40d3
--- /dev/null
+++ b/tests/auto/signature/signature-wincrypt.p7
Binary files differ
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
new file mode 100644
index 00000000..57705d79
--- /dev/null
+++ b/tests/auto/signature/signing-no-key.p12
Binary files differ
diff --git a/tests/auto/signature/signing.p12 b/tests/auto/signature/signing.p12
new file mode 100644
index 00000000..5d752759
--- /dev/null
+++ b/tests/auto/signature/signing.p12
Binary files differ
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 \