diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp')
-rw-r--r-- | tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp | 867 |
1 files changed, 805 insertions, 62 deletions
diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index 4c0d9e1e6e..64b464e002 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <jni.h> @@ -33,7 +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; @@ -57,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(); @@ -96,6 +79,7 @@ private slots: void getStaticIntField(); void getStaticByteFieldClassName(); void getStaticByteField(); + void getStaticBooleanField(); void getStaticLongFieldClassName(); void getStaticLongField(); void getStaticDoubleFieldClassName(); @@ -131,6 +115,11 @@ private slots: void templateApiCheck(); void isClassAvailable(); void fromLocalRef(); + void largeObjectArray(); + + void callback_data(); + void callback(); + void callStaticOverloadResolution(); void cleanupTestCase(); }; @@ -143,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() { } @@ -160,6 +157,21 @@ void tst_QJniObject::ctor() } { + QJniObject object = QJniObject::construct<jstring>(); + QVERIFY(object.isValid()); + } + + { + // 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()); @@ -167,6 +179,13 @@ void tst_QJniObject::ctor() } { + QJniObject string = QJniObject::fromString(QLatin1String("Hello, Java")); + QJniObject object = QJniObject::construct<jstring>(string.object<jstring>()); + QVERIFY(object.isValid()); + QCOMPARE(string.toString(), object.toString()); + } + + { QJniEnvironment env; jclass javaStringClass = env->FindClass("java/lang/String"); QJniObject string(javaStringClass); @@ -182,21 +201,40 @@ void tst_QJniObject::ctor() QVERIFY(stringCpy.isValid()); QCOMPARE(qString, stringCpy.toString()); } + + { + QJniEnvironment env; + const QString qString = QLatin1String("Hello, Java"); + jclass javaStringClass = env->FindClass("java/lang/String"); + QJniObject string = QJniObject::fromString(qString); + QJniObject stringCpy(javaStringClass, string.object<jstring>()); + QVERIFY(stringCpy.isValid()); + QCOMPARE(qString, stringCpy.toString()); + } } 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"); QVERIFY(!isEmpty); - const jint ret = jString1.callMethod<jint>("compareToIgnoreCase", - "(Ljava/lang/String;)I", - jString2.object<jstring>()); + jint ret = jString1.callMethod<jint>("compareToIgnoreCase", + "(Ljava/lang/String;)I", + jString2.object<jstring>()); + QVERIFY(0 == ret); + + 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); } @@ -206,6 +244,33 @@ void tst_QJniObject::callMethodTest() jlong ret = longObject.callMethod<jlong>("longValue"); QCOMPARE(ret, jLong); } + + // as of Qt 6.4, callMethod works with an object type as well! + { + const QString qString = QLatin1String("Hello, Java"); + QJniObject jString = QJniObject::fromString(qString); + const QString qStringRet = jString.callMethod<jstring>("toUpperCase").toString(); + QCOMPARE(qString.toUpper(), qStringRet); + + 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() @@ -219,6 +284,10 @@ void tst_QJniObject::callObjectMethodTest() "(II)Ljava/lang/String;", 0, 4); QCOMPARE(subString.toString(), qString.mid(0, 4)); + + subString = jString.callObjectMethod<jstring>("substring", 0, 4); + QCOMPARE(subString.toString(), qString.mid(0, 4)); + } void tst_QJniObject::stringConvertionTest() @@ -251,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); @@ -260,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")); @@ -276,6 +375,16 @@ void tst_QJniObject::callStaticObjectMethodClassName() QString returnedString = returnValue.toString(); QCOMPARE(returnedString, QString::fromLatin1("test format")); + + returnValue = QJniObject::callStaticObjectMethod<jstring>("java/lang/String", + "format", + formatString.object<jstring>(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + + returnedString = returnValue.toString(); + + QCOMPARE(returnedString, QString::fromLatin1("test format")); } void tst_QJniObject::callStaticObjectMethod() @@ -284,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, @@ -293,10 +403,35 @@ void tst_QJniObject::callStaticObjectMethod() formatString.object<jstring>(), jobjectArray(0)); QVERIFY(returnValue.isValid()); + QCOMPARE(returnValue.toString(), string); - QString returnedString = returnValue.toString(); + returnValue = QJniObject::callStaticObjectMethod<jstring>(cls, + "format", + formatString.object<jstring>(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + QCOMPARE(returnValue.toString(), string); - QCOMPARE(returnedString, QString::fromLatin1("test format")); + // from 6.4 on we can use callStaticMethod + returnValue = QJniObject::callStaticMethod<jstring>(cls, + "format", + formatString.object<jstring>(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + QCOMPARE(returnValue.toString(), string); + + // 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); + + // 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() @@ -319,6 +454,15 @@ void tst_QJniObject::callStaticObjectMethodById() QString returnedString = returnValue.toString(); QCOMPARE(returnedString, QString::fromLatin1("test format")); + + // from Qt 6.4 on we can use callStaticMethod as well + returnValue = QJniObject::callStaticMethod<jstring>( + cls, id, formatString.object<jstring>(), jobjectArray(0)); + QVERIFY(returnValue.isValid()); + + returnedString = returnValue.toString(); + + QCOMPARE(returnedString, QString::fromLatin1("test format")); } void tst_QJniObject::callStaticBooleanMethod() @@ -336,6 +480,9 @@ void tst_QJniObject::callStaticBooleanMethod() "(Ljava/lang/String;)Z", parameter.object<jstring>()); QVERIFY(b); + + b = QJniObject::callStaticMethod<jboolean>(cls, "parseBoolean", parameter.object<jstring>()); + QVERIFY(b); } { @@ -347,6 +494,9 @@ void tst_QJniObject::callStaticBooleanMethod() "(Ljava/lang/String;)Z", parameter.object<jstring>()); QVERIFY(!b); + + b = QJniObject::callStaticMethod<jboolean>(cls, "parseBoolean", parameter.object<jstring>()); + QVERIFY(!b); } } @@ -387,6 +537,10 @@ void tst_QJniObject::callStaticBooleanMethodClassName() "(Ljava/lang/String;)Z", parameter.object<jstring>()); QVERIFY(b); + b = QJniObject::callStaticMethod<jboolean>("java/lang/Boolean", + "parseBoolean", + parameter.object<jstring>()); + QVERIFY(b); } { @@ -398,6 +552,10 @@ void tst_QJniObject::callStaticBooleanMethodClassName() "(Ljava/lang/String;)Z", parameter.object<jstring>()); QVERIFY(!b); + b = QJniObject::callStaticMethod<jboolean>("java/lang/Boolean", + "parseBoolean", + parameter.object<jstring>()); + QVERIFY(!b); } } @@ -408,7 +566,6 @@ void tst_QJniObject::callStaticByteMethodClassName() jbyte returnValue = QJniObject::callStaticMethod<jbyte>("java/lang/Byte", "parseByte", - "(Ljava/lang/String;)B", parameter.object<jstring>()); QCOMPARE(returnValue, jbyte(number.toInt())); } @@ -424,7 +581,6 @@ void tst_QJniObject::callStaticByteMethod() jbyte returnValue = QJniObject::callStaticMethod<jbyte>(cls, "parseByte", - "(Ljava/lang/String;)B", parameter.object<jstring>()); QCOMPARE(returnValue, jbyte(number.toInt())); } @@ -452,7 +608,6 @@ void tst_QJniObject::callStaticIntMethodClassName() jint returnValue = QJniObject::callStaticMethod<jint>("java/lang/Integer", "parseInt", - "(Ljava/lang/String;)I", parameter.object<jstring>()); QCOMPARE(returnValue, number.toInt()); } @@ -469,7 +624,6 @@ void tst_QJniObject::callStaticIntMethod() jint returnValue = QJniObject::callStaticMethod<jint>(cls, "parseInt", - "(Ljava/lang/String;)I", parameter.object<jstring>()); QCOMPARE(returnValue, number.toInt()); } @@ -494,7 +648,6 @@ void tst_QJniObject::callStaticCharMethodClassName() { jchar returnValue = QJniObject::callStaticMethod<jchar>("java/lang/Character", "toUpperCase", - "(C)C", jchar('a')); QCOMPARE(returnValue, jchar('A')); } @@ -508,7 +661,6 @@ void tst_QJniObject::callStaticCharMethod() jchar returnValue = QJniObject::callStaticMethod<jchar>(cls, "toUpperCase", - "(C)C", jchar('a')); QCOMPARE(returnValue, jchar('A')); } @@ -533,7 +685,6 @@ void tst_QJniObject::callStaticDoubleMethodClassName () jdouble returnValue = QJniObject::callStaticMethod<jdouble>("java/lang/Double", "parseDouble", - "(Ljava/lang/String;)D", parameter.object<jstring>()); QCOMPARE(returnValue, number.toDouble()); } @@ -550,7 +701,6 @@ void tst_QJniObject::callStaticDoubleMethod() jdouble returnValue = QJniObject::callStaticMethod<jdouble>(cls, "parseDouble", - "(Ljava/lang/String;)D", parameter.object<jstring>()); QCOMPARE(returnValue, number.toDouble()); } @@ -579,7 +729,6 @@ void tst_QJniObject::callStaticFloatMethodClassName() jfloat returnValue = QJniObject::callStaticMethod<jfloat>("java/lang/Float", "parseFloat", - "(Ljava/lang/String;)F", parameter.object<jstring>()); QCOMPARE(returnValue, number.toFloat()); } @@ -596,7 +745,6 @@ void tst_QJniObject::callStaticFloatMethod() jfloat returnValue = QJniObject::callStaticMethod<jfloat>(cls, "parseFloat", - "(Ljava/lang/String;)F", parameter.object<jstring>()); QCOMPARE(returnValue, number.toFloat()); } @@ -624,7 +772,6 @@ void tst_QJniObject::callStaticShortMethodClassName() jshort returnValue = QJniObject::callStaticMethod<jshort>("java/lang/Short", "parseShort", - "(Ljava/lang/String;)S", parameter.object<jstring>()); QCOMPARE(returnValue, number.toShort()); } @@ -641,7 +788,6 @@ void tst_QJniObject::callStaticShortMethod() jshort returnValue = QJniObject::callStaticMethod<jshort>(cls, "parseShort", - "(Ljava/lang/String;)S", parameter.object<jstring>()); QCOMPARE(returnValue, number.toShort()); } @@ -669,7 +815,6 @@ void tst_QJniObject::callStaticLongMethodClassName() jlong returnValue = QJniObject::callStaticMethod<jlong>("java/lang/Long", "parseLong", - "(Ljava/lang/String;)J", parameter.object<jstring>()); QCOMPARE(returnValue, jlong(number.toLong())); } @@ -685,7 +830,6 @@ void tst_QJniObject::callStaticLongMethod() jlong returnValue = QJniObject::callStaticMethod<jlong>(cls, "parseLong", - "(Ljava/lang/String;)J", parameter.object<jstring>()); QCOMPARE(returnValue, jlong(number.toLong())); } @@ -789,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() @@ -805,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() @@ -821,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() @@ -873,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() @@ -889,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> @@ -924,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() @@ -948,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() @@ -966,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> @@ -980,21 +1166,33 @@ void setStaticField(const char *fieldName, T testValue) T res = QJniObject::getStaticField<T>(testClassName, fieldName); QCOMPARE(res, testValue); + + // use template overload to reset to default + T defaultValue = {}; + 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() @@ -1009,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() @@ -1044,9 +1253,15 @@ void tst_QJniObject::templateApiCheck() 1, true, 'c'); + QJniObject::callStaticMethod<void>(testClassName, + "staticVoidMethodWithArgs", + 1, + true, + 'c'); testClass.callMethod<void>("voidMethod"); testClass.callMethod<void>("voidMethodWithArgs", "(IZC)V", 1, true, 'c'); + testClass.callMethod<void>("voidMethodWithArgs", 1, true, 'c'); // jboolean ----------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod<jboolean>(testClassName, "staticBooleanMethod")); @@ -1056,6 +1271,11 @@ void tst_QJniObject::templateApiCheck() true, true, true)); + QVERIFY(QJniObject::callStaticMethod<jboolean>(testClassName, + "staticBooleanMethodWithArgs", + true, + true, + true)); QVERIFY(testClass.callMethod<jboolean>("booleanMethod")); QVERIFY(testClass.callMethod<jboolean>("booleanMethodWithArgs", @@ -1063,6 +1283,10 @@ void tst_QJniObject::templateApiCheck() true, true, true)); + QVERIFY(testClass.callMethod<jboolean>("booleanMethodWithArgs", + true, + true, + true)); // jbyte -------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod<jbyte>(testClassName, @@ -1073,9 +1297,15 @@ void tst_QJniObject::templateApiCheck() 1, 1, 1) == A_BYTE_VALUE); + QVERIFY(QJniObject::callStaticMethod<jbyte>(testClassName, + "staticByteMethodWithArgs", + jbyte(1), + jbyte(1), + jbyte(1)) == A_BYTE_VALUE); QVERIFY(testClass.callMethod<jbyte>("byteMethod") == A_BYTE_VALUE); QVERIFY(testClass.callMethod<jbyte>("byteMethodWithArgs", "(BBB)B", 1, 1, 1) == A_BYTE_VALUE); + QVERIFY(testClass.callMethod<jbyte>("byteMethodWithArgs", jbyte(1), jbyte(1), jbyte(1)) == A_BYTE_VALUE); // jchar -------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod<jchar>(testClassName, @@ -1086,6 +1316,11 @@ void tst_QJniObject::templateApiCheck() jchar(1), jchar(1), jchar(1)) == A_CHAR_VALUE); + QVERIFY(QJniObject::callStaticMethod<jchar>(testClassName, + "staticCharMethodWithArgs", + jchar(1), + jchar(1), + jchar(1)) == A_CHAR_VALUE); QVERIFY(testClass.callMethod<jchar>("charMethod") == A_CHAR_VALUE); QVERIFY(testClass.callMethod<jchar>("charMethodWithArgs", @@ -1093,6 +1328,10 @@ void tst_QJniObject::templateApiCheck() jchar(1), jchar(1), jchar(1)) == A_CHAR_VALUE); + QVERIFY(testClass.callMethod<jchar>("charMethodWithArgs", + jchar(1), + jchar(1), + jchar(1)) == A_CHAR_VALUE); // jshort ------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod<jshort>(testClassName, @@ -1103,6 +1342,11 @@ void tst_QJniObject::templateApiCheck() jshort(1), jshort(1), jshort(1)) == A_SHORT_VALUE); + QVERIFY(QJniObject::callStaticMethod<jshort>(testClassName, + "staticShortMethodWithArgs", + jshort(1), + jshort(1), + jshort(1)) == A_SHORT_VALUE); QVERIFY(testClass.callMethod<jshort>("shortMethod") == A_SHORT_VALUE); QVERIFY(testClass.callMethod<jshort>("shortMethodWithArgs", @@ -1110,6 +1354,10 @@ void tst_QJniObject::templateApiCheck() jshort(1), jshort(1), jshort(1)) == A_SHORT_VALUE); + QVERIFY(testClass.callMethod<jshort>("shortMethodWithArgs", + jshort(1), + jshort(1), + jshort(1)) == A_SHORT_VALUE); // jint --------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod<jint>(testClassName, @@ -1120,6 +1368,11 @@ void tst_QJniObject::templateApiCheck() jint(1), jint(1), jint(1)) == A_INT_VALUE); + QVERIFY(QJniObject::callStaticMethod<jint>(testClassName, + "staticIntMethodWithArgs", + jint(1), + jint(1), + jint(1)) == A_INT_VALUE); QVERIFY(testClass.callMethod<jint>("intMethod") == A_INT_VALUE); QVERIFY(testClass.callMethod<jint>("intMethodWithArgs", @@ -1127,6 +1380,10 @@ void tst_QJniObject::templateApiCheck() jint(1), jint(1), jint(1)) == A_INT_VALUE); + QVERIFY(testClass.callMethod<jint>("intMethodWithArgs", + jint(1), + jint(1), + jint(1)) == A_INT_VALUE); // jlong -------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod<jlong>(testClassName, @@ -1137,6 +1394,11 @@ void tst_QJniObject::templateApiCheck() jlong(1), jlong(1), jlong(1)) == A_LONG_VALUE); + QVERIFY(QJniObject::callStaticMethod<jlong>(testClassName, + "staticLongMethodWithArgs", + jlong(1), + jlong(1), + jlong(1)) == A_LONG_VALUE); QVERIFY(testClass.callMethod<jlong>("longMethod") == A_LONG_VALUE); QVERIFY(testClass.callMethod<jlong>("longMethodWithArgs", @@ -1144,6 +1406,10 @@ void tst_QJniObject::templateApiCheck() jlong(1), jlong(1), jlong(1)) == A_LONG_VALUE); + QVERIFY(testClass.callMethod<jlong>("longMethodWithArgs", + jlong(1), + jlong(1), + jlong(1)) == A_LONG_VALUE); // jfloat ------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod<jfloat>(testClassName, @@ -1154,6 +1420,11 @@ void tst_QJniObject::templateApiCheck() jfloat(1.1), jfloat(1.1), jfloat(1.1)) == A_FLOAT_VALUE); + QVERIFY(QJniObject::callStaticMethod<jfloat>(testClassName, + "staticFloatMethodWithArgs", + jfloat(1.1), + jfloat(1.1), + jfloat(1.1)) == A_FLOAT_VALUE); QVERIFY(testClass.callMethod<jfloat>("floatMethod") == A_FLOAT_VALUE); QVERIFY(testClass.callMethod<jfloat>("floatMethodWithArgs", @@ -1161,6 +1432,10 @@ void tst_QJniObject::templateApiCheck() jfloat(1.1), jfloat(1.1), jfloat(1.1)) == A_FLOAT_VALUE); + QVERIFY(testClass.callMethod<jfloat>("floatMethodWithArgs", + jfloat(1.1), + jfloat(1.1), + jfloat(1.1)) == A_FLOAT_VALUE); // jdouble ------------------------------------------------------------------------------------ QVERIFY(QJniObject::callStaticMethod<jdouble>(testClassName, @@ -1171,6 +1446,11 @@ void tst_QJniObject::templateApiCheck() jdouble(1.1), jdouble(1.1), jdouble(1.1)) == A_DOUBLE_VALUE); + QVERIFY(QJniObject::callStaticMethod<jdouble>(testClassName, + "staticDoubleMethodWithArgs", + jdouble(1.1), + jdouble(1.1), + jdouble(1.1)) == A_DOUBLE_VALUE); QVERIFY(testClass.callMethod<jdouble>("doubleMethod") == A_DOUBLE_VALUE); QVERIFY(testClass.callMethod<jdouble>("doubleMethodWithArgs", @@ -1178,6 +1458,10 @@ void tst_QJniObject::templateApiCheck() jdouble(1.1), jdouble(1.1), jdouble(1.1)) == A_DOUBLE_VALUE); + QVERIFY(testClass.callMethod<jdouble>("doubleMethodWithArgs", + jdouble(1.1), + jdouble(1.1), + jdouble(1.1)) == A_DOUBLE_VALUE); // jobject ------------------------------------------------------------------------------------ { @@ -1240,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 ------------------------------------------------------------------------------ @@ -1252,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 --------------------------------------------------------------------------------- @@ -1264,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 --------------------------------------------------------------------------------- @@ -1276,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 -------------------------------------------------------------------------------- @@ -1288,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 ---------------------------------------------------------------------------------- @@ -1300,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 --------------------------------------------------------------------------------- @@ -1312,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 -------------------------------------------------------------------------------- @@ -1324,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 ------------------------------------------------------------------------------- @@ -1336,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})); } } @@ -1348,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")); } @@ -1360,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" |