QtQuickView statuslistener CI tests
This test adds basic loading status tests for QtQuickView on Android. The test uses a primary QtQuickView which has the TestCases, and a Communicator QML_ELEMENT C++ class to load other QML files into a secondary QtQuickView contained in the hosting layout. The hosting activity listens to QtQuickView status changed signals, and sends those directly to the Communicator object, which emits a signal with the status information to the TestCases. Task-number: QTBUG-133439 Change-Id: I01a3a1dc3124036d8290a248b9b2d1bb4010033d Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
parent
58272fe764
commit
e4971b51f8
|
@ -1,2 +1,3 @@
|
||||||
add_subdirectory(basic)
|
add_subdirectory(basic)
|
||||||
add_subdirectory(signallistener)
|
add_subdirectory(signallistener)
|
||||||
|
add_subdirectory(statuslistener)
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
project(tst_qtquickview_statuslistener LANGUAGES CXX)
|
||||||
|
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Quick)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
qt_standard_project_setup()
|
||||||
|
|
||||||
|
# Dummy test - just so this can be detected by ctest.
|
||||||
|
# Actual test implementation is under ${CMAKE_CURRENT_SOURCE_DIR}/android/qml/
|
||||||
|
# Add a source file, otherwise no test target is actually created.
|
||||||
|
qt_internal_add_test(tst_qtquickview_statuslistener
|
||||||
|
SOURCES
|
||||||
|
empty.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(tst_qtquickview_statuslistener
|
||||||
|
PROPERTIES
|
||||||
|
QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android
|
||||||
|
)
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.qtproject.qt.android.qtquickview_statuslistener"
|
||||||
|
android:installLocation="auto"
|
||||||
|
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
||||||
|
android:versionName="-- %%INSERT_VERSION_NAME%% --">
|
||||||
|
|
||||||
|
<supports-screens
|
||||||
|
android:anyDensity="true"
|
||||||
|
android:largeScreens="true"
|
||||||
|
android:normalScreens="true"
|
||||||
|
android:smallScreens="true" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:label="-- %%INSERT_APP_NAME%% --"
|
||||||
|
android:icon="-- %%INSERT_APP_ICON%% --"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:fullBackupOnly="false">
|
||||||
|
<activity
|
||||||
|
android:name="org.qtproject.qt.android.qtquickview_statuslistener.TestActivity"
|
||||||
|
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:screenOrientation="unspecified"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,67 @@
|
||||||
|
plugins {
|
||||||
|
id 'com.android.application' version '8.3.0'
|
||||||
|
id 'org.qtproject.qt.gradleplugin' version '1.2'
|
||||||
|
}
|
||||||
|
|
||||||
|
def home = System.properties['user.home']
|
||||||
|
def ndkPath = System.getenv('ANDROID_NDK_ROOT')
|
||||||
|
QtBuild {
|
||||||
|
qtPath = file(home + '/work/install')
|
||||||
|
projectPath = file('qml/')
|
||||||
|
qtKitDir = file(home + '/work/install/target')
|
||||||
|
extraCMakeArguments = ['-DANDROID_NDK_ROOT=' + ndkPath]
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
/*******************************************************
|
||||||
|
* The following variables:
|
||||||
|
* - androidBuildToolsVersion,
|
||||||
|
* - androidCompileSdkVersion
|
||||||
|
* - qtAndroidDir - holds the path to qt android files
|
||||||
|
* needed to build any Qt application
|
||||||
|
* on Android.
|
||||||
|
* - qtGradlePluginType - whether to build an app or a library
|
||||||
|
*
|
||||||
|
* are defined in gradle.properties file. This file is
|
||||||
|
* updated by QtCreator and androiddeployqt tools.
|
||||||
|
* Changing them manually might break the compilation!
|
||||||
|
*******************************************************/
|
||||||
|
|
||||||
|
namespace androidPackageName
|
||||||
|
compileSdkVersion androidCompileSdkVersion
|
||||||
|
buildToolsVersion androidBuildToolsVersion
|
||||||
|
ndkVersion androidNdkVersion
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
|
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
|
||||||
|
res.srcDirs = [qtAndroidDir + '/res', 'res']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.incremental = true
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not compress Qt binary resources file
|
||||||
|
aaptOptions {
|
||||||
|
noCompress 'rcc'
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
resConfig "en"
|
||||||
|
minSdkVersion qtMinSdkVersion
|
||||||
|
targetSdkVersion qtTargetSdkVersion
|
||||||
|
ndk.abiFilters = qtTargetAbiList.split(",")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
|
project(tst_qtquickview_statuslistener_qml LANGUAGES CXX)
|
||||||
|
|
||||||
|
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Quick QuickTest QuickTestPrivate)
|
||||||
|
|
||||||
|
qt_standard_project_setup()
|
||||||
|
|
||||||
|
qt_add_executable(tst_qtquickview_statuslistener_qml
|
||||||
|
tst_qtquickview.cpp)
|
||||||
|
|
||||||
|
set_source_files_properties(TestView.qml PROPERTIES QT_QML_GENERATE_JAVA_CLASS TRUE)
|
||||||
|
set_source_files_properties(Invalid.qml PROPERTIES
|
||||||
|
QT_QML_SKIP_QMLDIR_ENTRY TRUE
|
||||||
|
QT_QML_SKIP_QMLLINT TRUE
|
||||||
|
QT_QML_SKIP_CACHEGEN TRUE)
|
||||||
|
|
||||||
|
qt_add_qml_module(tst_qtquickview_statuslistener_qml
|
||||||
|
URI TestViewModule
|
||||||
|
VERSION 1.0
|
||||||
|
QML_FILES
|
||||||
|
Valid.qml
|
||||||
|
Invalid.qml
|
||||||
|
TestView.qml
|
||||||
|
SOURCES
|
||||||
|
testactivitycommunicator.h testactivitycommunicator.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(tst_qtquickview_statuslistener_qml
|
||||||
|
PROPERTIES
|
||||||
|
QT_ANDROID_PACKAGE_NAME "org.qtproject.qt.android.tst_qtquickview_statuslistener_qml")
|
||||||
|
|
||||||
|
target_link_libraries(tst_qtquickview_statuslistener_qml PRIVATE
|
||||||
|
Qt::Core Qt::Quick Qt::QuickTest Qt::QuickTestPrivate
|
||||||
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
Hello, this is some text in the middle of your otherwise nicely formed QML file.
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtTest
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
TestActivityCommunicator {
|
||||||
|
id: communicator
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalSpy {
|
||||||
|
id: qtQuickViewStatusSpy
|
||||||
|
target: communicator
|
||||||
|
signalName: "qtQuickViewStatusChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase {
|
||||||
|
id: validQmlFile
|
||||||
|
name: "Valid QML file"
|
||||||
|
|
||||||
|
function test_valid() {
|
||||||
|
qtQuickViewStatusSpy.clear()
|
||||||
|
communicator.loadQtQuickView("qrc:/TestViewModule/Valid.qml");
|
||||||
|
qtQuickViewStatusSpy.wait()
|
||||||
|
compare(qtQuickViewStatusSpy.count, 1)
|
||||||
|
compare(qtQuickViewStatusSpy.signalArguments[0][0], 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase {
|
||||||
|
id: invalidQmlFile
|
||||||
|
name: "Invalid QML file"
|
||||||
|
|
||||||
|
function test_invalid() {
|
||||||
|
qtQuickViewStatusSpy.clear()
|
||||||
|
communicator.loadQtQuickView("qrc:/TestViewModule/Invalid.qml");
|
||||||
|
qtQuickViewStatusSpy.wait()
|
||||||
|
compare(qtQuickViewStatusSpy.count, 1)
|
||||||
|
compare(qtQuickViewStatusSpy.signalArguments[0][0], 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtTest
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "#00FF00"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "QML"
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
#include "testactivitycommunicator.h"
|
||||||
|
|
||||||
|
using namespace QtJniTypes;
|
||||||
|
|
||||||
|
static TestActivityCommunicator *s_instance;
|
||||||
|
|
||||||
|
TestActivityCommunicator::TestActivityCommunicator(QObject *parent)
|
||||||
|
: QObject{ parent },
|
||||||
|
m_activity(TestActivity::callStaticMethod<TestActivity>("instance"))
|
||||||
|
{
|
||||||
|
s_instance = this;
|
||||||
|
TestActivity::registerNativeMethods({
|
||||||
|
Q_JNI_NATIVE_SCOPED_METHOD(jni_onQtQuickViewStatusChanged, TestActivityCommunicator)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TestActivityCommunicator::~TestActivityCommunicator()
|
||||||
|
{
|
||||||
|
s_instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestActivityCommunicator *TestActivityCommunicator::instance()
|
||||||
|
{
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestActivityCommunicator::loadQtQuickView(const QString &qmlUri)
|
||||||
|
{
|
||||||
|
m_activity.callMethod<void>("loadQtQuickView", qmlUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestActivityCommunicator::jni_onQtQuickViewStatusChanged(JNIEnv *, jclass, jint status)
|
||||||
|
{
|
||||||
|
Q_ASSERT(s_instance);
|
||||||
|
emit s_instance->qtQuickViewStatusChanged(status);
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
#ifndef TESTACTIVITYCOMMUNICATOR_H
|
||||||
|
#define TESTACTIVITYCOMMUNICATOR_H
|
||||||
|
|
||||||
|
#include <QtCore/qobject.h>
|
||||||
|
#include <QtQml/qqmlengine.h>
|
||||||
|
#include <QtCore/qjnitypes.h>
|
||||||
|
|
||||||
|
Q_DECLARE_JNI_CLASS(TestActivity,
|
||||||
|
"org/qtproject/qt/android/qtquickview_statuslistener/TestActivity")
|
||||||
|
|
||||||
|
class TestActivityCommunicator : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
public:
|
||||||
|
explicit TestActivityCommunicator(QObject *parent = nullptr);
|
||||||
|
~TestActivityCommunicator();
|
||||||
|
|
||||||
|
static TestActivityCommunicator *instance();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void loadQtQuickView(const QString &qmlUri);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void qtQuickViewStatusChanged(int status);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QtJniTypes::TestActivity m_activity;
|
||||||
|
|
||||||
|
static void jni_onQtQuickViewStatusChanged(JNIEnv *, jclass, jint status);
|
||||||
|
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(jni_onQtQuickViewStatusChanged)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TESTACTIVITYCOMMUNICATOR_H
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
#include <QtQuickTest/6.10.0/QtQuickTest/private/quickforandroidtest_p.h>
|
||||||
|
|
||||||
|
QUICK_FOR_ANDROID_TEST_MAIN("tst_qtquickview_statuslistener")
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/qmlFrame"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/qmlFrame2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,15 @@
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
package org.qtproject.qt.android.qtquickview_statuslistener;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import org.qtproject.qt.android.QtQmlStatus;
|
||||||
|
import org.qtproject.qt.android.QtQmlStatusChangeListener;
|
||||||
|
import org.qtproject.qt.android.QtQuickView;
|
||||||
|
import org.qtproject.qt.android.QtQuickViewContent;
|
||||||
|
import org.qtproject.qt.android.tst_qtquickview_statuslistener_qml.TestViewModule.TestView;
|
||||||
|
|
||||||
|
public class TestActivity extends Activity implements QtQmlStatusChangeListener
|
||||||
|
{
|
||||||
|
native void jni_onQtQuickViewStatusChanged(int status);
|
||||||
|
|
||||||
|
private final TestView m_testView = new TestView();
|
||||||
|
private QtQuickView m_quickView;
|
||||||
|
private QtQuickView m_testingView;
|
||||||
|
private static TestActivity m_instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.main);
|
||||||
|
m_instance = this;
|
||||||
|
m_quickView = new QtQuickView(this);
|
||||||
|
|
||||||
|
final FrameLayout qmlFrame = findViewById(R.id.qmlFrame);
|
||||||
|
final ViewGroup.LayoutParams params = new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||||
|
qmlFrame.addView(m_quickView, params);
|
||||||
|
m_quickView.loadContent(m_testView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadQtQuickView(String qmlUri)
|
||||||
|
{
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
FrameLayout parent = findViewById(R.id.qmlFrame2);
|
||||||
|
if (m_testingView != null)
|
||||||
|
parent.removeView(m_testingView);
|
||||||
|
|
||||||
|
m_testingView = new QtQuickView(this, qmlUri, "tst_qtquickview_statuslistener_qml");
|
||||||
|
m_testingView.setStatusChangeListener(new QtQmlStatusChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onStatusChanged(QtQmlStatus status) {
|
||||||
|
jni_onQtQuickViewStatusChanged(status.ordinal());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
parent.addView(m_testingView,
|
||||||
|
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestView testView() { return m_testView; }
|
||||||
|
|
||||||
|
public static TestActivity instance() { return m_instance; }
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (C) 2025 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
// Left empty on purpose.
|
Loading…
Reference in New Issue