diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp')
-rw-r--r-- | tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp | 1186 |
1 files changed, 1120 insertions, 66 deletions
diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index 96637a72a6..64b464e002 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -1,37 +1,18 @@ -/**************************************************************************** -** -** 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> #include <QString> #include <QtCore/QJniEnvironment> #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; @@ -55,36 +36,50 @@ 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(); void callStaticBooleanMethodClassName(); void callStaticBooleanMethod(); + void callStaticBooleanMethodById(); void callStaticCharMethodClassName(); void callStaticCharMethod(); + void callStaticCharMethodById(); void callStaticIntMethodClassName(); void callStaticIntMethod(); + void callStaticIntMethodById(); void callStaticByteMethodClassName(); void callStaticByteMethod(); + void callStaticByteMethodById(); void callStaticDoubleMethodClassName(); void callStaticDoubleMethod(); + void callStaticDoubleMethodById(); void callStaticFloatMethodClassName(); void callStaticFloatMethod(); + void callStaticFloatMethodById(); void callStaticLongMethodClassName(); void callStaticLongMethod(); + void callStaticLongMethodById(); void callStaticShortMethodClassName(); void callStaticShortMethod(); + void callStaticShortMethodById(); void getStaticObjectFieldClassName(); void getStaticObjectField(); void getStaticIntFieldClassName(); void getStaticIntField(); void getStaticByteFieldClassName(); void getStaticByteField(); + void getStaticBooleanField(); void getStaticLongFieldClassName(); void getStaticLongField(); void getStaticDoubleFieldClassName(); @@ -97,9 +92,34 @@ private slots: void getStaticCharField(); void getBooleanField(); void getIntField(); + + void setIntField(); + void setByteField(); + void setLongField(); + void setDoubleField(); + void setFloatField(); + void setShortField(); + void setCharField(); + void setBooleanField(); + void setObjectField(); + void setStaticIntField(); + void setStaticByteField(); + void setStaticLongField(); + void setStaticDoubleField(); + void setStaticFloatField(); + void setStaticShortField(); + void setStaticCharField(); + void setStaticBooleanField(); + void setStaticObjectField(); + void templateApiCheck(); void isClassAvailable(); void fromLocalRef(); + void largeObjectArray(); + + void callback_data(); + void callback(); + void callStaticOverloadResolution(); void cleanupTestCase(); }; @@ -112,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() { } @@ -129,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()); @@ -136,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); @@ -151,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); } @@ -175,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() @@ -188,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() @@ -220,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); @@ -229,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")); @@ -245,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() @@ -253,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, @@ -262,10 +403,66 @@ void tst_QJniObject::callStaticObjectMethod() formatString.object<jstring>(), jobjectArray(0)); QVERIFY(returnValue.isValid()); + QCOMPARE(returnValue.toString(), string); + + returnValue = QJniObject::callStaticObjectMethod<jstring>(cls, + "format", + formatString.object<jstring>(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + QCOMPARE(returnValue.toString(), string); + + // 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() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/String"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod( + cls, "format", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;"); + QVERIFY(id != 0); + + QJniObject formatString = QJniObject::fromString(QLatin1String("test format")); + QVERIFY(formatString.isValid()); + + QJniObject returnValue = QJniObject::callStaticObjectMethod( + cls, id, formatString.object<jstring>(), jobjectArray(0)); + QVERIFY(returnValue.isValid()); 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() @@ -283,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); } { @@ -294,6 +494,35 @@ void tst_QJniObject::callStaticBooleanMethod() "(Ljava/lang/String;)Z", parameter.object<jstring>()); QVERIFY(!b); + + b = QJniObject::callStaticMethod<jboolean>(cls, "parseBoolean", parameter.object<jstring>()); + QVERIFY(!b); + } +} + +void tst_QJniObject::callStaticBooleanMethodById() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/Boolean"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod(cls, "parseBoolean", "(Ljava/lang/String;)Z"); + QVERIFY(id != 0); + + { + QJniObject parameter = QJniObject::fromString("true"); + QVERIFY(parameter.isValid()); + + jboolean b = QJniObject::callStaticMethod<jboolean>(cls, id, parameter.object<jstring>()); + QVERIFY(b); + } + + { + QJniObject parameter = QJniObject::fromString("false"); + QVERIFY(parameter.isValid()); + + jboolean b = QJniObject::callStaticMethod<jboolean>(cls, id, parameter.object<jstring>()); + QVERIFY(!b); } } @@ -308,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); } { @@ -319,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); } } @@ -329,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())); } @@ -345,11 +581,26 @@ void tst_QJniObject::callStaticByteMethod() jbyte returnValue = QJniObject::callStaticMethod<jbyte>(cls, "parseByte", - "(Ljava/lang/String;)B", parameter.object<jstring>()); QCOMPARE(returnValue, jbyte(number.toInt())); } +void tst_QJniObject::callStaticByteMethodById() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/Byte"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod(cls, "parseByte", "(Ljava/lang/String;)B"); + QVERIFY(id != 0); + + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jbyte returnValue = QJniObject::callStaticMethod<jbyte>(cls, id, parameter.object<jstring>()); + QCOMPARE(returnValue, jbyte(number.toInt())); +} + void tst_QJniObject::callStaticIntMethodClassName() { QString number = QString::number(123); @@ -357,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()); } @@ -374,16 +624,30 @@ void tst_QJniObject::callStaticIntMethod() jint returnValue = QJniObject::callStaticMethod<jint>(cls, "parseInt", - "(Ljava/lang/String;)I", parameter.object<jstring>()); QCOMPARE(returnValue, number.toInt()); } +void tst_QJniObject::callStaticIntMethodById() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/Integer"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod(cls, "parseInt", "(Ljava/lang/String;)I"); + QVERIFY(id != 0); + + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jint returnValue = QJniObject::callStaticMethod<jint>(cls, id, parameter.object<jstring>()); + QCOMPARE(returnValue, number.toInt()); +} + void tst_QJniObject::callStaticCharMethodClassName() { jchar returnValue = QJniObject::callStaticMethod<jchar>("java/lang/Character", "toUpperCase", - "(C)C", jchar('a')); QCOMPARE(returnValue, jchar('A')); } @@ -397,11 +661,23 @@ void tst_QJniObject::callStaticCharMethod() jchar returnValue = QJniObject::callStaticMethod<jchar>(cls, "toUpperCase", - "(C)C", jchar('a')); QCOMPARE(returnValue, jchar('A')); } +void tst_QJniObject::callStaticCharMethodById() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/Character"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod(cls, "toUpperCase", "(C)C"); + QVERIFY(id != 0); + + jchar returnValue = QJniObject::callStaticMethod<jchar>(cls, id, jchar('a')); + QCOMPARE(returnValue, jchar('A')); +} + void tst_QJniObject::callStaticDoubleMethodClassName () { QString number = QString::number(123.45); @@ -409,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()); } @@ -426,11 +701,27 @@ void tst_QJniObject::callStaticDoubleMethod() jdouble returnValue = QJniObject::callStaticMethod<jdouble>(cls, "parseDouble", - "(Ljava/lang/String;)D", parameter.object<jstring>()); QCOMPARE(returnValue, number.toDouble()); } +void tst_QJniObject::callStaticDoubleMethodById() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/Double"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod(cls, "parseDouble", "(Ljava/lang/String;)D"); + QVERIFY(id != 0); + + QString number = QString::number(123.45); + QJniObject parameter = QJniObject::fromString(number); + + jdouble returnValue = + QJniObject::callStaticMethod<jdouble>(cls, id, parameter.object<jstring>()); + QCOMPARE(returnValue, number.toDouble()); +} + void tst_QJniObject::callStaticFloatMethodClassName() { QString number = QString::number(123.45); @@ -438,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()); } @@ -455,11 +745,26 @@ void tst_QJniObject::callStaticFloatMethod() jfloat returnValue = QJniObject::callStaticMethod<jfloat>(cls, "parseFloat", - "(Ljava/lang/String;)F", parameter.object<jstring>()); QCOMPARE(returnValue, number.toFloat()); } +void tst_QJniObject::callStaticFloatMethodById() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/Float"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod(cls, "parseFloat", "(Ljava/lang/String;)F"); + QVERIFY(id != 0); + + QString number = QString::number(123.45); + QJniObject parameter = QJniObject::fromString(number); + + jfloat returnValue = QJniObject::callStaticMethod<jfloat>(cls, id, parameter.object<jstring>()); + QCOMPARE(returnValue, number.toFloat()); +} + void tst_QJniObject::callStaticShortMethodClassName() { QString number = QString::number(123); @@ -467,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()); } @@ -484,11 +788,26 @@ void tst_QJniObject::callStaticShortMethod() jshort returnValue = QJniObject::callStaticMethod<jshort>(cls, "parseShort", - "(Ljava/lang/String;)S", parameter.object<jstring>()); QCOMPARE(returnValue, number.toShort()); } +void tst_QJniObject::callStaticShortMethodById() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/Short"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod(cls, "parseShort", "(Ljava/lang/String;)S"); + QVERIFY(id != 0); + + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jshort returnValue = QJniObject::callStaticMethod<jshort>(cls, id, parameter.object<jstring>()); + QCOMPARE(returnValue, number.toShort()); +} + void tst_QJniObject::callStaticLongMethodClassName() { QString number = QString::number(123); @@ -496,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())); } @@ -512,17 +830,32 @@ void tst_QJniObject::callStaticLongMethod() jlong returnValue = QJniObject::callStaticMethod<jlong>(cls, "parseLong", - "(Ljava/lang/String;)J", parameter.object<jstring>()); QCOMPARE(returnValue, jlong(number.toLong())); } +void tst_QJniObject::callStaticLongMethodById() +{ + QJniEnvironment env; + jclass cls = env.findClass("java/lang/Long"); + QVERIFY(cls != 0); + + jmethodID id = env.findStaticMethod(cls, "parseLong", "(Ljava/lang/String;)J"); + QVERIFY(id != 0); + + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jlong returnValue = QJniObject::callStaticMethod<jlong>(cls, id, parameter.object<jstring>()); + QCOMPARE(returnValue, jlong(number.toLong())); +} + void tst_QJniObject::getStaticObjectFieldClassName() { { - QJniObject boolObject = QJniObject::getStaticObjectField<jobject>("java/lang/Boolean", - "FALSE", - "Ljava/lang/Boolean;"); + QJniObject boolObject = QJniObject::getStaticObjectField("java/lang/Boolean", + "FALSE", + "Ljava/lang/Boolean;"); QVERIFY(boolObject.isValid()); jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); @@ -530,9 +863,9 @@ void tst_QJniObject::getStaticObjectFieldClassName() } { - QJniObject boolObject = QJniObject::getStaticObjectField<jobject>("java/lang/Boolean", - "TRUE", - "Ljava/lang/Boolean;"); + QJniObject boolObject = QJniObject::getStaticObjectField("java/lang/Boolean", + "TRUE", + "Ljava/lang/Boolean;"); QVERIFY(boolObject.isValid()); jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); @@ -556,9 +889,9 @@ void tst_QJniObject::getStaticObjectField() QVERIFY(cls != 0); { - QJniObject boolObject = QJniObject::getStaticObjectField<jobject>(cls, - "FALSE", - "Ljava/lang/Boolean;"); + QJniObject boolObject = QJniObject::getStaticObjectField(cls, + "FALSE", + "Ljava/lang/Boolean;"); QVERIFY(boolObject.isValid()); jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); @@ -566,9 +899,9 @@ void tst_QJniObject::getStaticObjectField() } { - QJniObject boolObject = QJniObject::getStaticObjectField<jobject>(cls, - "TRUE", - "Ljava/lang/Boolean;"); + QJniObject boolObject = QJniObject::getStaticObjectField(cls, + "TRUE", + "Ljava/lang/Boolean;"); QVERIFY(boolObject.isValid()); jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); @@ -600,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() @@ -616,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() @@ -632,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() @@ -684,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() @@ -700,24 +1054,190 @@ 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> +void setField(const char *fieldName, T testValue) +{ + QJniObject obj(testClassName); + QVERIFY(obj.isValid()); + + obj.setField(fieldName, testValue); + + T res = obj.getField<T>(fieldName); + QCOMPARE(res, 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(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() +{ + setField("DOUBLE_VAR", jdouble(1.2)); +} + +void tst_QJniObject::setFloatField() +{ + setField("FLOAT_VAR", jfloat(1.2)); +} + +void tst_QJniObject::setShortField() +{ + 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() +{ + QJniObject obj(testClassName); + QVERIFY(obj.isValid()); + + 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> +void setStaticField(const char *fieldName, T testValue) +{ + QJniObject::setStaticField(testClassName, fieldName, 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(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() +{ + setStaticField("S_DOUBLE_VAR", jdouble(1.2)); +} + +void tst_QJniObject::setStaticFloatField() +{ + setStaticField("S_FLOAT_VAR", jfloat(1.2)); +} + +void tst_QJniObject::setStaticShortField() +{ + 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() +{ + 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() @@ -733,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")); @@ -745,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", @@ -752,6 +1283,10 @@ void tst_QJniObject::templateApiCheck() true, true, true)); + QVERIFY(testClass.callMethod<jboolean>("booleanMethodWithArgs", + true, + true, + true)); // jbyte -------------------------------------------------------------------------------------- QVERIFY(QJniObject::callStaticMethod<jbyte>(testClassName, @@ -762,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, @@ -775,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", @@ -782,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, @@ -792,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", @@ -799,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, @@ -809,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", @@ -816,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, @@ -826,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", @@ -833,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, @@ -843,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", @@ -850,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, @@ -860,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", @@ -867,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 ------------------------------------------------------------------------------------ { @@ -929,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 ------------------------------------------------------------------------------ @@ -941,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 --------------------------------------------------------------------------------- @@ -953,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 --------------------------------------------------------------------------------- @@ -965,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 -------------------------------------------------------------------------------- @@ -977,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 ---------------------------------------------------------------------------------- @@ -989,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 --------------------------------------------------------------------------------- @@ -1001,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 -------------------------------------------------------------------------------- @@ -1013,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 ------------------------------------------------------------------------------- @@ -1025,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})); } } @@ -1037,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")); } @@ -1049,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" |