diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qjniobject')
3 files changed, 815 insertions, 34 deletions
diff --git a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt index b54cf4b4cc..98509dc0e5 100644 --- a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt @@ -1,10 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qjniobject Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qjniobject LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qjniobject SOURCES tst_qjniobject.cpp @@ -14,6 +20,4 @@ if(ANDROID) set_property(TARGET tst_qjniobject APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/testdata ) - # QTBUG-88840 # special case - qt_android_generate_deployment_settings(tst_qjniobject) # special case endif() diff --git a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java index dc55ad0f89..07a94d1cac 100644 --- a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java +++ b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only package org.qtproject.qt.android.testdatapackage; @@ -20,6 +20,9 @@ public class QtJniObjectTestClass // -------------------------------------------------------------------------------------------- + final int INT_FIELD = 123; + final boolean BOOL_FIELD = true; + byte BYTE_VAR; short SHORT_VAR; int INT_VAR; @@ -125,6 +128,9 @@ public class QtJniObjectTestClass public String stringMethod() { return staticStringMethod(); } // -------------------------------------------------------------------------------------------- + public static String staticEchoMethod(String value) { return value; } + + // -------------------------------------------------------------------------------------------- public static Throwable staticThrowableMethod() { return A_THROWABLE_OBJECT; } public Throwable throwableMethod() { return staticThrowableMethod(); } @@ -132,43 +138,208 @@ public class QtJniObjectTestClass public static Object[] staticObjectArrayMethod() { Object[] array = { new Object(), new Object(), new Object() }; return array; } public Object[] objectArrayMethod() { return staticObjectArrayMethod(); } + public static Object[] staticReverseObjectArray(Object[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + Object old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public Object[] reverseObjectArray(Object[] array) + { return staticReverseObjectArray(array); } // -------------------------------------------------------------------------------------------- public static boolean[] staticBooleanArrayMethod() { boolean[] array = { true, true, true }; return array; } public boolean[] booleanArrayMethod() { return staticBooleanArrayMethod(); } + public static boolean[] staticReverseBooleanArray(boolean[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + boolean old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public boolean[] reverseBooleanArray(boolean[] array) + { return staticReverseBooleanArray(array); } // -------------------------------------------------------------------------------------------- public static byte[] staticByteArrayMethod() { byte[] array = { 'a', 'b', 'c' }; return array; } public byte[] byteArrayMethod() { return staticByteArrayMethod(); } + public static byte[] staticReverseByteArray(byte[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + byte old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public byte[] reverseByteArray(byte[] array) + { return staticReverseByteArray(array); } // -------------------------------------------------------------------------------------------- public static char[] staticCharArrayMethod() { char[] array = { 'a', 'b', 'c' }; return array; } public char[] charArrayMethod() { return staticCharArrayMethod(); } + public static char[] staticReverseCharArray(char[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + char old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public char[] reverseCharArray(char[] array) + { return staticReverseCharArray(array); } // -------------------------------------------------------------------------------------------- public static short[] staticShortArrayMethod() { short[] array = { 3, 2, 1 }; return array; } public short[] shortArrayMethod() { return staticShortArrayMethod(); } + public static short[] staticReverseShortArray(short[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + short old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public short[] reverseShortArray(short[] array) + { return staticReverseShortArray(array); } // -------------------------------------------------------------------------------------------- public static int[] staticIntArrayMethod() { int[] array = { 3, 2, 1 }; return array; } public int[] intArrayMethod() { return staticIntArrayMethod(); } + public static int[] staticReverseIntArray(int[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + int old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public int[] reverseIntArray(int[] array) + { return staticReverseIntArray(array); } // -------------------------------------------------------------------------------------------- public static long[] staticLongArrayMethod() { long[] array = { 3, 2, 1 }; return array; } public long[] longArrayMethod() { return staticLongArrayMethod(); } + public static long[] staticReverseLongArray(long[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + long old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public long[] reverseLongArray(long[] array) + { return staticReverseLongArray(array); } // -------------------------------------------------------------------------------------------- public static float[] staticFloatArrayMethod() { float[] array = { 1.0f, 2.0f, 3.0f }; return array; } public float[] floatArrayMethod() { return staticFloatArrayMethod(); } + public static float[] staticReverseFloatArray(float[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + float old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public float[] reverseFloatArray(float[] array) + { return staticReverseFloatArray(array); } // -------------------------------------------------------------------------------------------- public static double[] staticDoubleArrayMethod() { double[] array = { 3.0, 2.0, 1.0 }; return array; } public double[] doubleArrayMethod() { return staticDoubleArrayMethod(); } -} + public static double[] staticReverseDoubleArray(double[] array) + { + for (int i = 0; i < array.length / 2; ++i) { + double old = array[array.length - i - 1]; + array[array.length - i - 1] = array[i]; + array[i] = old; + } + return array; + } + public double[] reverseDoubleArray(double[] array) + { return staticReverseDoubleArray(array); } + // -------------------------------------------------------------------------------------------- + native public int callbackWithObject(QtJniObjectTestClass that); + native public int callbackWithObjectRef(QtJniObjectTestClass that); + native public int callbackWithString(String string); + native public int callbackWithByte(byte value); + native public int callbackWithBoolean(boolean value); + native public int callbackWithInt(int value); + native public int callbackWithDouble(double value); + native public int callbackWithJniArray(double[] value); + native public int callbackWithQList(double[] value); + native public int callbackWithStringList(String[] value); + + public int callMeBackWithObject(QtJniObjectTestClass that) + { + return callbackWithObject(that); + } + + public int callMeBackWithObjectRef(QtJniObjectTestClass that) + { + return callbackWithObjectRef(that); + } + + public int callMeBackWithString(String string) + { + return callbackWithString(string); + } + + public int callMeBackWithByte(byte value) + { + return callbackWithByte(value); + } + + public int callMeBackWithBoolean(boolean value) + { + return callbackWithBoolean(value); + } + + public int callMeBackWithInt(int value) + { + return callbackWithInt(value); + } + + public int callMeBackWithDouble(double value) + { + return callbackWithDouble(value); + } + public int callMeBackWithJniArray(double[] value) + { + return callbackWithJniArray(value); + } + public int callMeBackWithQList(double[] value) + { + return callbackWithQList(value); + } + public int callMeBackWithStringList(String[] value) + { + return callbackWithStringList(value); + } + + public Object callMethodThrowsException() throws Exception { + throw new Exception(); + } + + public static Object callStaticMethodThrowsException() throws Exception { + throw new Exception(); + } +} diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index 7ea6b1e121..64b464e002 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <jni.h> @@ -8,8 +8,11 @@ #include <QtCore/QJniObject> #include <QtTest> -static const char testClassName[] = "org/qtproject/qt/android/testdatapackage/QtJniObjectTestClass"; +using namespace Qt::StringLiterals; + +static constexpr const char testClassName[] = "org/qtproject/qt/android/testdatapackage/QtJniObjectTestClass"; Q_DECLARE_JNI_CLASS(QtJniObjectTestClass, testClassName) +using TestClass = QtJniTypes::QtJniObjectTestClass; static const jbyte A_BYTE_VALUE = 127; static const jshort A_SHORT_VALUE = 32767; @@ -33,12 +36,16 @@ public: private slots: void initTestCase(); + void init(); void ctor(); void callMethodTest(); + void callMethodThrowsException(); void callObjectMethodTest(); void stringConvertionTest(); void compareOperatorTests(); + void className(); + void callStaticMethodThrowsException(); void callStaticObjectMethodClassName(); void callStaticObjectMethod(); void callStaticObjectMethodById(); @@ -72,6 +79,7 @@ private slots: void getStaticIntField(); void getStaticByteFieldClassName(); void getStaticByteField(); + void getStaticBooleanField(); void getStaticLongFieldClassName(); void getStaticLongField(); void getStaticDoubleFieldClassName(); @@ -107,6 +115,11 @@ private slots: void templateApiCheck(); void isClassAvailable(); void fromLocalRef(); + void largeObjectArray(); + + void callback_data(); + void callback(); + void callStaticOverloadResolution(); void cleanupTestCase(); }; @@ -119,6 +132,14 @@ void tst_QJniObject::initTestCase() { } +void tst_QJniObject::init() +{ + // Unless explicitly ignored to test error handling, warning messages + // in this test about a failure to look up a field, method, or class + // make the test fail. + QTest::failOnWarning(QRegularExpression("java.lang.NoSuch.*Error")); +} + void tst_QJniObject::cleanupTestCase() { } @@ -141,6 +162,16 @@ void tst_QJniObject::ctor() } { + // from Qt 6.7 on we can construct declared classes through the helper type + QJniObject object = TestClass::construct(); + QVERIFY(object.isValid()); + + // or even directly + TestClass testObject; + QVERIFY(testObject.isValid()); + } + + { QJniObject string = QJniObject::fromString(QLatin1String("Hello, Java")); QJniObject object("java/lang/String", "(Ljava/lang/String;)V", string.object<jstring>()); QVERIFY(object.isValid()); @@ -185,8 +216,10 @@ void tst_QJniObject::ctor() void tst_QJniObject::callMethodTest() { { - QJniObject jString1 = QJniObject::fromString(QLatin1String("Hello, Java")); - QJniObject jString2 = QJniObject::fromString(QLatin1String("hELLO, jAVA")); + const QString qString1 = u"Hello, Java"_s; + const QString qString2 = u"hELLO, jAVA"_s; + QJniObject jString1 = QJniObject::fromString(qString1); + QJniObject jString2 = QJniObject::fromString(qString2); QVERIFY(jString1 != jString2); const jboolean isEmpty = jString1.callMethod<jboolean>("isEmpty"); @@ -199,6 +232,10 @@ void tst_QJniObject::callMethodTest() ret = jString1.callMethod<jint>("compareToIgnoreCase", jString2.object<jstring>()); QVERIFY(0 == ret); + + // as of Qt 6.7, we can pass QString directly + ret = jString1.callMethod<jint>("compareToIgnoreCase", qString2); + QVERIFY(0 == ret); } { @@ -217,9 +254,25 @@ void tst_QJniObject::callMethodTest() QJniObject subString = jString.callMethod<jstring>("substring", 0, 4); QCOMPARE(subString.toString(), qString.mid(0, 4)); + + // and as of Qt 6.7, we can return and take QString directly + QCOMPARE(jString.callMethod<QString>("substring", 0, 4), qString.mid(0, 4)); + + QCOMPARE(jString.callMethod<jstring>("substring", 0, 7) + .callMethod<jstring>("toUpperCase") + .callMethod<QString>("concat", u"C++"_s), u"HELLO, C++"_s); } } +void tst_QJniObject::callMethodThrowsException() +{ + QtJniTypes::QtJniObjectTestClass instance; + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.Exception")); + auto res = instance.callMethod<jobject>("callMethodThrowsException"); + QVERIFY(!res.isValid()); + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); +} + void tst_QJniObject::callObjectMethodTest() { const QString qString = QLatin1String("Hello, Java"); @@ -267,7 +320,7 @@ void tst_QJniObject::compareOperatorTests() QJniObject stringObject2 = QJniObject::fromString(str); QVERIFY(stringObject != stringObject2); - jstring jstrobj = 0; + jstring jstrobj = nullptr; QJniObject invalidStringObject; QVERIFY(invalidStringObject == jstrobj); @@ -276,6 +329,36 @@ void tst_QJniObject::compareOperatorTests() QVERIFY(!invalidStringObject.isValid()); } +void tst_QJniObject::className() +{ + const QString str("Hello!"); + QJniObject jString = QJniObject::fromString(str); + { + QCOMPARE(jString.className(), "java/lang/String"); + QCOMPARE(jString.toString(), str); + } + + { + QJniObject strObject = QJniObject("java/lang/String", str); + QCOMPARE(strObject.className(), "java/lang/String"); + QCOMPARE(strObject.toString(), str); + } + + { + TestClass test; + QCOMPARE(test.className(), testClassName); + } +} + +void tst_QJniObject::callStaticMethodThrowsException() +{ + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.Exception")); + auto res = QtJniTypes::QtJniObjectTestClass::callStaticMethod<jobject>( + "callStaticMethodThrowsException"); + QVERIFY(!res.isValid()); + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); +} + void tst_QJniObject::callStaticObjectMethodClassName() { QJniObject formatString = QJniObject::fromString(QLatin1String("test format")); @@ -310,7 +393,8 @@ void tst_QJniObject::callStaticObjectMethod() jclass cls = env->FindClass("java/lang/String"); QVERIFY(cls != 0); - QJniObject formatString = QJniObject::fromString(QLatin1String("test format")); + const QString string = u"test format"_s; + QJniObject formatString = QJniObject::fromString(string); QVERIFY(formatString.isValid()); QJniObject returnValue = QJniObject::callStaticObjectMethod(cls, @@ -319,20 +403,14 @@ void tst_QJniObject::callStaticObjectMethod() formatString.object<jstring>(), jobjectArray(0)); QVERIFY(returnValue.isValid()); - - QString returnedString = returnValue.toString(); - - QCOMPARE(returnedString, QString::fromLatin1("test format")); + QCOMPARE(returnValue.toString(), string); returnValue = QJniObject::callStaticObjectMethod<jstring>(cls, "format", formatString.object<jstring>(), jobjectArray(0)); QVERIFY(returnValue.isValid()); - - returnedString = returnValue.toString(); - - QCOMPARE(returnedString, QString::fromLatin1("test format")); + QCOMPARE(returnValue.toString(), string); // from 6.4 on we can use callStaticMethod returnValue = QJniObject::callStaticMethod<jstring>(cls, @@ -340,10 +418,20 @@ void tst_QJniObject::callStaticObjectMethod() formatString.object<jstring>(), jobjectArray(0)); QVERIFY(returnValue.isValid()); + QCOMPARE(returnValue.toString(), string); - returnedString = returnValue.toString(); + // from 6.7 we can use callStaticMethod without specifying the class string + returnValue = QJniObject::callStaticMethod<jstring, jstring>("format", + formatString.object<jstring>(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + QCOMPARE(returnValue.toString(), string); - QCOMPARE(returnedString, QString::fromLatin1("test format")); + // from 6.7 we can pass QString directly, both as parameters and return type + QString result = QJniObject::callStaticMethod<jstring, QString>("format", + string, + jobjectArray(0)); + QCOMPARE(result, string); } void tst_QJniObject::callStaticObjectMethodById() @@ -845,6 +933,10 @@ void tst_QJniObject::getStaticIntField() jint i = QJniObject::getStaticField<jint>(cls, "SIZE"); QCOMPARE(i, 64); + + enum class Enum { SIZE = 64 }; + Enum e = QJniObject::getStaticField<Enum>(cls, "SIZE"); + QCOMPARE(e, Enum::SIZE); } void tst_QJniObject::getStaticByteFieldClassName() @@ -861,6 +953,16 @@ void tst_QJniObject::getStaticByteField() jbyte i = QJniObject::getStaticField<jbyte>(cls, "MAX_VALUE"); QCOMPARE(i, jbyte(127)); + + enum class Enum : jbyte { MAX_VALUE = 127 }; + Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE"); + QCOMPARE(e, Enum::MAX_VALUE); +} + +void tst_QJniObject::getStaticBooleanField() +{ + QCOMPARE(TestClass::getStaticField<jboolean>("S_BOOLEAN_VAR"), + TestClass::getStaticField<bool>("S_BOOLEAN_VAR")); } void tst_QJniObject::getStaticLongFieldClassName() @@ -877,6 +979,10 @@ void tst_QJniObject::getStaticLongField() jlong i = QJniObject::getStaticField<jlong>(cls, "MAX_VALUE"); QCOMPARE(i, jlong(9223372036854775807L)); + + enum class Enum : jlong { MAX_VALUE = 9223372036854775807L }; + Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE"); + QCOMPARE(e, Enum::MAX_VALUE); } void tst_QJniObject::getStaticDoubleFieldClassName() @@ -929,6 +1035,9 @@ void tst_QJniObject::getStaticShortField() jshort i = QJniObject::getStaticField<jshort>(cls, "MAX_VALUE"); QCOMPARE(i, jshort(32767)); + enum class Enum : jshort { MAX_VALUE = 32767 }; + Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE"); + QCOMPARE(e, Enum::MAX_VALUE); } void tst_QJniObject::getStaticCharFieldClassName() @@ -945,24 +1054,29 @@ void tst_QJniObject::getStaticCharField() jchar i = QJniObject::getStaticField<jchar>(cls, "MAX_VALUE"); QCOMPARE(i, jchar(0xffff)); + + enum class Enum : jchar { MAX_VALUE = 0xffff }; + Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE"); + QCOMPARE(e, Enum::MAX_VALUE); } void tst_QJniObject::getBooleanField() { - QJniObject obj("org/qtproject/qt/android/QtActivityDelegate"); + QJniObject obj(testClassName); QVERIFY(obj.isValid()); - QVERIFY(!obj.getField<jboolean>("m_fullScreen")); + QVERIFY(obj.getField<jboolean>("BOOL_FIELD")); + QVERIFY(obj.getField<bool>("BOOL_FIELD")); } void tst_QJniObject::getIntField() { - QJniObject obj("org/qtproject/qt/android/QtActivityDelegate"); + QJniObject obj(testClassName); QVERIFY(obj.isValid()); - jint res = obj.getField<jint>("m_currentRotation"); - QCOMPARE(res, -1); + jint res = obj.getField<jint>("INT_FIELD"); + QCOMPARE(res, 123); } template <typename T> @@ -980,16 +1094,22 @@ void setField(const char *fieldName, T testValue) void tst_QJniObject::setIntField() { setField("INT_VAR", 555); + enum class Enum : jint { VALUE = 555 }; + setField("INT_VAR", Enum::VALUE); } void tst_QJniObject::setByteField() { - setField("BYTE_VAR", jbyte(555)); + setField("BYTE_VAR", jbyte(123)); + enum class Enum : jbyte { VALUE = 123 }; + setField("BYTE_VAR", Enum::VALUE); } void tst_QJniObject::setLongField() { setField("LONG_VAR", jlong(9223372036847758232L)); + enum class Enum : jlong { VALUE = 9223372036847758232L }; + setField("LONG_VAR", Enum::VALUE); } void tst_QJniObject::setDoubleField() @@ -1004,17 +1124,22 @@ void tst_QJniObject::setFloatField() void tst_QJniObject::setShortField() { - setField("SHORT_VAR", jshort(123)); + setField("SHORT_VAR", jshort(555)); + enum class Enum : jshort { VALUE = 555 }; + setField("SHORT_VAR", Enum::VALUE); } void tst_QJniObject::setCharField() { setField("CHAR_VAR", jchar('A')); + enum class Enum : jchar { VALUE = 'A' }; + setField("CHAR_VAR", Enum::VALUE); } void tst_QJniObject::setBooleanField() { setField("BOOLEAN_VAR", jboolean(true)); + setField("BOOLEAN_VAR", true); } void tst_QJniObject::setObjectField() @@ -1022,11 +1147,16 @@ void tst_QJniObject::setObjectField() QJniObject obj(testClassName); QVERIFY(obj.isValid()); - QJniObject testValue = QJniObject::fromString(QStringLiteral("Hello")); + const QString qString = u"Hello"_s; + QJniObject testValue = QJniObject::fromString(qString); obj.setField("STRING_OBJECT_VAR", testValue.object<jstring>()); QJniObject res = obj.getObjectField<jstring>("STRING_OBJECT_VAR"); QCOMPARE(res.toString(), testValue.toString()); + + // as of Qt 6.7, we can set and get strings directly + obj.setField("STRING_OBJECT_VAR", qString); + QCOMPARE(obj.getField<QString>("STRING_OBJECT_VAR"), qString); } template <typename T> @@ -1039,24 +1169,30 @@ void setStaticField(const char *fieldName, T testValue) // use template overload to reset to default T defaultValue = {}; - QJniObject::setStaticField<QtJniTypes::QtJniObjectTestClass, T>(fieldName, defaultValue); - res = QJniObject::getStaticField<QtJniTypes::QtJniObjectTestClass, T>(fieldName); + TestClass::setStaticField(fieldName, defaultValue); + res = TestClass::getStaticField<T>(fieldName); QCOMPARE(res, defaultValue); } void tst_QJniObject::setStaticIntField() { setStaticField("S_INT_VAR", 555); + enum class Enum : jint { VALUE = 555 }; + setStaticField("S_INT_VAR", Enum::VALUE); } void tst_QJniObject::setStaticByteField() { - setStaticField("S_BYTE_VAR", jbyte(555)); + setStaticField("S_BYTE_VAR", jbyte(123)); + enum class Enum : jbyte { VALUE = 123 }; + setStaticField("S_BYTE_VAR", Enum::VALUE); } void tst_QJniObject::setStaticLongField() { setStaticField("S_LONG_VAR", jlong(9223372036847758232L)); + enum class Enum : jlong { VALUE = 9223372036847758232L }; + setStaticField("S_LONG_VAR", Enum::VALUE); } void tst_QJniObject::setStaticDoubleField() @@ -1071,26 +1207,37 @@ void tst_QJniObject::setStaticFloatField() void tst_QJniObject::setStaticShortField() { - setStaticField("S_SHORT_VAR", jshort(123)); + setStaticField("S_SHORT_VAR", jshort(555)); + enum class Enum : jshort { VALUE = 555 }; + setStaticField("S_SHORT_VAR", Enum::VALUE); } void tst_QJniObject::setStaticCharField() { setStaticField("S_CHAR_VAR", jchar('A')); + enum class Enum : jchar { VALUE = 'A' }; + setStaticField("S_CHAR_VAR", Enum::VALUE); } void tst_QJniObject::setStaticBooleanField() { setStaticField("S_BOOLEAN_VAR", jboolean(true)); + setStaticField("S_BOOLEAN_VAR", true); } void tst_QJniObject::setStaticObjectField() { - QJniObject testValue = QJniObject::fromString(QStringLiteral("Hello")); + const QString qString = u"Hello"_s; + QJniObject testValue = QJniObject::fromString(qString); QJniObject::setStaticField(testClassName, "S_STRING_OBJECT_VAR", testValue.object<jstring>()); QJniObject res = QJniObject::getStaticObjectField<jstring>(testClassName, "S_STRING_OBJECT_VAR"); QCOMPARE(res.toString(), testValue.toString()); + + // as of Qt 6.7, we can set and get strings directly + using namespace QtJniTypes; + QtJniObjectTestClass::setStaticField("S_STRING_OBJECT_VAR", qString); + QCOMPARE(QtJniObjectTestClass::getStaticField<QString>("S_STRING_OBJECT_VAR"), qString); } void tst_QJniObject::templateApiCheck() @@ -1377,11 +1524,42 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jobjectArray>(testClassName, "staticObjectArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jobject[]>("staticObjectArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + + QJniArray<jobject> newArray(QList<QJniObject>{QJniObject::fromString(u"one"_s), + QJniObject::fromString(u"two"_s), + QJniObject::fromString(u"three"_s)}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jobject[]>("staticReverseObjectArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.size(), 3); + QCOMPARE(QJniObject(reverse.at(0)).toString(), u"three"_s); + QCOMPARE(QJniObject(reverse.at(1)).toString(), u"two"_s); + QCOMPARE(QJniObject(reverse.at(2)).toString(), u"one"_s); } { QJniObject res = testClass.callObjectMethod<jobjectArray>("objectArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jobject[]>("objectArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + + QJniArray<jobject> newArray(QList<QJniObject>{QJniObject::fromString(u"one"_s), + QJniObject::fromString(u"two"_s), + QJniObject::fromString(u"three"_s)}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jobject[]>("reverseObjectArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.size(), 3); + // QJniArray::at returns a jobject that's a local reference; make sure we don't free it twice + QCOMPARE(QJniObject::fromLocalRef(reverse.at(0)).toString(), u"three"_s); + QCOMPARE(QJniObject::fromLocalRef(reverse.at(1)).toString(), u"two"_s); + QCOMPARE(QJniObject::fromLocalRef(reverse.at(2)).toString(), u"one"_s); } // jbooleanArray ------------------------------------------------------------------------------ @@ -1389,11 +1567,33 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jbooleanArray>(testClassName, "staticBooleanArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jboolean[]>("staticBooleanArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jboolean>{true, true, true})); + + QJniArray<jboolean> newArray(QList<jboolean>{true, false, false}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jboolean[]>("staticReverseBooleanArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jboolean>{false, false, true})); } { QJniObject res = testClass.callObjectMethod<jbooleanArray>("booleanArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jboolean[]>("booleanArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jboolean>{true, true, true})); + + QJniArray<jboolean> newArray(QList<jboolean>{true, false, false}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jboolean[]>("reverseBooleanArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jboolean>{false, false, true})); } // jbyteArray --------------------------------------------------------------------------------- @@ -1401,11 +1601,37 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jbyteArray>(testClassName, "staticByteArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jbyte[]>("staticByteArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), "abc"); + + QJniArray<jbyte> newArray(QByteArray{"cba"}); + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jbyte[]>("staticReverseByteArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), "abc"); + + const QByteArray reverse2 = TestClass::callStaticMethod<QByteArray>("staticReverseByteArray", + QByteArray("abc")); + QCOMPARE(reverse2, "cba"); } { QJniObject res = testClass.callObjectMethod<jbyteArray>("byteArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jbyte[]>("byteArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), "abc"); + + QJniArray<jbyte> newArray = QJniArrayBase::fromContainer(QByteArray{"cba"}); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jbyte[]>("reverseByteArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), "abc"); } // jcharArray --------------------------------------------------------------------------------- @@ -1413,11 +1639,37 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jcharArray>(testClassName, "staticCharArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jchar[]>("staticCharArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jchar>{u'a', u'b', u'c'})); + + QJniArray<jchar> newArray = {u'c', u'b', u'a'}; + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jchar[]>("staticReverseCharArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jchar>{u'a', u'b', u'c'})); + + const QList<jchar> reverse2 = TestClass::callStaticMethod<QList<jchar>>("staticReverseCharArray", + (QList<jchar>{u'c', u'b', u'a'})); + QCOMPARE(reverse2, (QList<jchar>{u'a', u'b', u'c'})); } { QJniObject res = testClass.callObjectMethod<jcharArray>("charArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jchar[]>("charArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jchar>{u'a', u'b', u'c'})); + + QJniArray<jchar> newArray = {u'c', u'b', u'a'}; + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jchar[]>("reverseCharArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jchar>{u'a', u'b', u'c'})); } // jshortArray -------------------------------------------------------------------------------- @@ -1425,11 +1677,38 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jshortArray>(testClassName, "staticShortArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jshort[]>("staticShortArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jshort>{3, 2, 1})); + + QJniArray<jshort> newArray = {3, 2, 1}; + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jshort[]>("staticReverseShortArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jshort>{1, 2, 3})); + + const QList<jshort> reverse2 = TestClass::callStaticMethod<QList<jshort>>("staticReverseShortArray", + (QList<jshort>{1, 2, 3})); + QCOMPARE(reverse2, (QList<jshort>{3, 2, 1})); } { QJniObject res = testClass.callObjectMethod<jshortArray>("shortArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jshort[]>("shortArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jshort>{3, 2, 1})); + + QJniArray<jshort> newArray = {3, 2, 1}; + static_assert(std::is_same_v<decltype(newArray)::Type, jshort>); + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jshort[]>("reverseShortArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jshort>{1, 2, 3})); } // jintArray ---------------------------------------------------------------------------------- @@ -1437,11 +1716,33 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jintArray>(testClassName, "staticIntArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jint[]>("staticIntArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jint>{3, 2, 1})); + + QJniArray<jint> newArray = {3, 2, 1}; + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jint[]>("staticReverseIntArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jint>{1, 2, 3})); } { QJniObject res = testClass.callObjectMethod<jintArray>("intArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jint[]>("intArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jint>{3, 2, 1})); + + QJniArray<jint> newArray = {3, 2, 1}; + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jint[]>("reverseIntArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jint>{1, 2, 3})); } // jlongArray --------------------------------------------------------------------------------- @@ -1449,11 +1750,33 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jlongArray>(testClassName, "staticLongArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jlong[]>("staticLongArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jlong>{3, 2, 1})); + + QJniArray<jlong> newArray = {3, 2, 1}; + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jlong[]>("staticReverseLongArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jlong>{1, 2, 3})); } { QJniObject res = testClass.callObjectMethod<jlongArray>("longArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jlong[]>("longArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jlong>{3, 2, 1})); + + QJniArray<jlong> newArray = {3, 2, 1}; + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jlong[]>("reverseLongArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jlong>{1, 2, 3})); } // jfloatArray -------------------------------------------------------------------------------- @@ -1461,11 +1784,33 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jfloatArray>(testClassName, "staticFloatArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jfloat[]>("staticFloatArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jfloat>{1.0f, 2.0f, 3.0f})); + + QJniArray<jfloat> newArray = {3.0f, 2.0f, 1.0f}; + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jfloat[]>("staticReverseFloatArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jfloat>{1.0f, 2.0f, 3.0f})); } { QJniObject res = testClass.callObjectMethod<jfloatArray>("floatArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jfloat[]>("floatArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jfloat>{1.0f, 2.0f, 3.0f})); + + QJniArray<jfloat> newArray = {3.0f, 2.0f, 1.0f}; + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jfloat[]>("reverseFloatArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jfloat>{1.0f, 2.0f, 3.0f})); } // jdoubleArray ------------------------------------------------------------------------------- @@ -1473,11 +1818,33 @@ void tst_QJniObject::templateApiCheck() QJniObject res = QJniObject::callStaticObjectMethod<jdoubleArray>(testClassName, "staticDoubleArrayMethod"); QVERIFY(res.isValid()); + + const auto array = TestClass::callStaticMethod<jdouble[]>("staticDoubleArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jdouble>{3.0, 2.0, 1.0})); + + QJniArray<jdouble> newArray = {3.0, 2.0, 1.0}; + QVERIFY(newArray.isValid()); + const auto reverse = TestClass::callStaticMethod<jdouble[]>("staticReverseDoubleArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jdouble>{1.0, 2.0, 3.0})); } { QJniObject res = testClass.callObjectMethod<jdoubleArray>("doubleArrayMethod"); QVERIFY(res.isValid()); + + const auto array = testClass.callMethod<jdouble[]>("doubleArrayMethod"); + QVERIFY(array.isValid()); + QCOMPARE(array.size(), 3); + QCOMPARE(array.toContainer(), (QList<jdouble>{3.0, 2.0, 1.0})); + + QJniArray<jdouble> newArray = {3.0, 2.0, 1.0}; + QVERIFY(newArray.isValid()); + const auto reverse = testClass.callMethod<jdouble[]>("reverseDoubleArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.toContainer(), (QList<jdouble>{1.0, 2.0, 3.0})); } } @@ -1485,6 +1852,7 @@ void tst_QJniObject::templateApiCheck() void tst_QJniObject::isClassAvailable() { QVERIFY(QJniObject::isClassAvailable("java/lang/String")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.ClassNotFoundException")); QVERIFY(!QJniObject::isClassAvailable("class/not/Available")); QVERIFY(QJniObject::isClassAvailable("org/qtproject/qt/android/QtActivityDelegate")); } @@ -1497,6 +1865,244 @@ void tst_QJniObject::fromLocalRef() QJniObject o = QJniObject::fromLocalRef(env->FindClass("java/lang/String")); } +void tst_QJniObject::largeObjectArray() +{ + QJniArray<jobject> newArray(QList<QJniObject>{QJniObject::fromString(u"one"_s), + QJniObject::fromString(u"two"_s), + QJniObject::fromString(u"three"_s)}); + QVERIFY(newArray.isValid()); + const QJniArray<QJniObject> reverse = TestClass::callStaticMethod<jobject[]>( + "staticReverseObjectArray", newArray); + QVERIFY(reverse.isValid()); + QCOMPARE(reverse.size(), 3); + + // make sure we don't leak local references + for (int i = 0; i < 10000; ++i) { + QVERIFY(reverse.at(0).isValid()); + QVERIFY(reverse.at(1).isValid()); + QVERIFY(reverse.at(2).isValid()); + } +} + +enum class CallbackParameterType +{ + Object, + ObjectRef, + String, + Byte, + Boolean, + Int, + Double, + JniArray, + QList, + QStringList, +}; + +static std::optional<TestClass> calledWithObject; +static int callbackWithObject(JNIEnv *, jobject, TestClass that) +{ + calledWithObject.emplace(that); + return int(CallbackParameterType::Object); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithObject) +static int callbackWithObjectRef(JNIEnv *, jobject, const TestClass &that) +{ + calledWithObject.emplace(that); + return int(CallbackParameterType::ObjectRef); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithObjectRef) + +static std::optional<QString> calledWithString; +static int callbackWithString(JNIEnv *, jobject, const QString &string) +{ + calledWithString.emplace(string); + return int(CallbackParameterType::String); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithString) + +static std::optional<jbyte> calledWithByte; +static int callbackWithByte(JNIEnv *, jobject, jbyte value) +{ + calledWithByte.emplace(value); + return int(CallbackParameterType::Byte); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithByte) + +static std::optional<jbyte> calledWithBoolean; +static int callbackWithBoolean(JNIEnv *, jobject, bool value) +{ + calledWithBoolean.emplace(value); + return int(CallbackParameterType::Boolean); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithBoolean) + +static std::optional<int> calledWithInt; +static int callbackWithInt(JNIEnv *, jobject, int value) +{ + calledWithInt.emplace(value); + return int(CallbackParameterType::Int); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithInt) + +static std::optional<double> calledWithDouble; +static int callbackWithDouble(JNIEnv *, jobject, double value) +{ + calledWithDouble.emplace(value); + return int(CallbackParameterType::Double); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithDouble) + +static std::optional<QJniArray<jdouble>> calledWithJniArray; +static int callbackWithJniArray(JNIEnv *, jobject, const QJniArray<jdouble> &value) +{ + calledWithJniArray.emplace(value); + return int(CallbackParameterType::JniArray); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithJniArray) + +static std::optional<QList<double>> calledWithQList; +static int callbackWithQList(JNIEnv *, jobject, const QList<double> &value) +{ + calledWithQList.emplace(value); + return int(CallbackParameterType::QList); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithQList) + +static std::optional<QStringList> calledWithStringList; +static int callbackWithStringList(JNIEnv *, jobject, const QStringList &value) +{ + calledWithStringList.emplace(value); + return int(CallbackParameterType::QStringList); +} +Q_DECLARE_JNI_NATIVE_METHOD(callbackWithStringList) + +void tst_QJniObject::callback_data() +{ + QTest::addColumn<CallbackParameterType>("parameterType"); + + QTest::addRow("Object") << CallbackParameterType::Object; + QTest::addRow("ObjectRef") << CallbackParameterType::ObjectRef; + QTest::addRow("String") << CallbackParameterType::String; + QTest::addRow("Byte") << CallbackParameterType::Byte; + QTest::addRow("Boolean") << CallbackParameterType::Boolean; + QTest::addRow("Int") << CallbackParameterType::Int; + QTest::addRow("Double") << CallbackParameterType::Double; + QTest::addRow("JniArray") << CallbackParameterType::JniArray; + QTest::addRow("QList") << CallbackParameterType::QList; + QTest::addRow("QStringList") << CallbackParameterType::QStringList; +} + +void tst_QJniObject::callback() +{ + QFETCH(const CallbackParameterType, parameterType); + + TestClass testObject; + int result = -1; + + switch (parameterType) { + case CallbackParameterType::Object: + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithObject) + })); + result = testObject.callMethod<int>("callMeBackWithObject", testObject); + QVERIFY(calledWithObject); + QCOMPARE(calledWithObject.value(), testObject); + break; + case CallbackParameterType::ObjectRef: + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithObjectRef) + })); + result = testObject.callMethod<int>("callMeBackWithObjectRef", testObject); + QVERIFY(calledWithObject); + QCOMPARE(calledWithObject.value(), testObject); + break; + case CallbackParameterType::String: + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithString) + })); + result = testObject.callMethod<int>("callMeBackWithString", QString::number(123)); + QVERIFY(calledWithString); + QCOMPARE(calledWithString.value(), "123"); + break; + case CallbackParameterType::Byte: + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithByte) + })); + result = testObject.callMethod<int>("callMeBackWithByte", jbyte(123)); + QVERIFY(calledWithByte); + QCOMPARE(calledWithByte.value(), 123); + break; + case CallbackParameterType::Boolean: + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithBoolean) + })); + result = testObject.callMethod<int>("callMeBackWithBoolean", true); + QVERIFY(calledWithBoolean); + QCOMPARE(calledWithBoolean.value(), true); + break; + case CallbackParameterType::Int: + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithInt) + })); + result = testObject.callMethod<int>("callMeBackWithInt", 12345); + QVERIFY(calledWithInt); + QCOMPARE(calledWithInt.value(), 12345); + break; + case CallbackParameterType::Double: + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithDouble) + })); + result = testObject.callMethod<int>("callMeBackWithDouble", 1.2345); + QVERIFY(calledWithDouble); + QCOMPARE(calledWithDouble.value(), 1.2345); + break; + case CallbackParameterType::JniArray: { + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithJniArray) + })); + const QJniArray<double> doubles = { 1.2, 3.4, 5.6 }; + result = testObject.callMethod<int>("callMeBackWithJniArray", doubles); + QVERIFY(calledWithJniArray); + QCOMPARE(calledWithJniArray, doubles); + break; + } + case CallbackParameterType::QList: { + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithQList) + })); + const QList<double> doubles = { 1.2, 3.4, 5.6 }; + result = testObject.callMethod<int>("callMeBackWithQList", doubles); + QVERIFY(calledWithQList); + QCOMPARE(calledWithQList.value(), doubles); + break; + } + case CallbackParameterType::QStringList: { + QVERIFY(TestClass::registerNativeMethods({ + Q_JNI_NATIVE_METHOD(callbackWithStringList) + })); + const QStringList strings = { "one", "two" }; + result = testObject.callMethod<int>("callMeBackWithStringList", strings); + QVERIFY(calledWithStringList); + QCOMPARE(calledWithStringList.value(), strings); + break; + } + } + QCOMPARE(result, int(parameterType)); +} + +// Make sure the new callStaticMethod overload taking a class, return type, +// and argument as template parameters, doesn't break overload resolution +// and that the class name doesn't get interpreted as the function name. +void tst_QJniObject::callStaticOverloadResolution() +{ + const QString value = u"Hello World"_s; + QJniObject str = QJniObject::fromString(value); + const auto result = QJniObject::callStaticMethod<jstring, jstring>( + QtJniTypes::Traits<TestClass>::className(), + "staticEchoMethod", str.object<jstring>()).toString(); + QCOMPARE(result, value); +} + QTEST_MAIN(tst_QJniObject) #include "tst_qjniobject.moc" |