File indexing completed on 2024-05-19 05:14:15

0001 /*
0002     SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
0003     SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005 
0006 #include "kandroidextras/jnimethod.h"
0007 #include "kandroidextras/jnitypes.h"
0008 #include "kandroidextras/intent.h"
0009 #include "kandroidextras/javatypes.h"
0010 
0011 #include <QtTest/qtest.h>
0012 
0013 using namespace KAndroidExtras;
0014 
0015 class TestClass
0016 {
0017     JNI_OBJECT(TestClass, android::content::Intent)
0018 public:
0019     TestClass() = default;
0020     JNI_CONSTRUCTOR(TestClass, android::content::Intent)
0021     JNI_METHOD(java::lang::String, getName)
0022     JNI_METHOD(void, setName, java::lang::String)
0023     JNI_METHOD(jint, getFlags)
0024     JNI_METHOD(void, setFlags, jint)
0025     JNI_METHOD(void, start)
0026     JNI_METHOD(bool, setCoordinates, jfloat, jfloat)
0027     JNI_METHOD(void, startIntent, android::content::Intent)
0028     JNI_METHOD(android::content::Intent, getIntent)
0029     JNI_METHOD(Jni::Array<java::lang::String>, getStringList)
0030     JNI_METHOD(Jni::Array<jshort>, getShortList)
0031 
0032     // overloads
0033     JNI_METHOD(void, overloaded)
0034     JNI_METHOD(void, overloaded, jint)
0035     JNI_METHOD(void, overloaded, java::lang::String, bool)
0036     JNI_METHOD(jint, overloaded, jlong, java::lang::String, Jni::Array<jshort>)
0037     JNI_METHOD(Jni::Array<jshort>, overloaded, Intent)
0038 
0039     JNI_PROPERTY(java::lang::String, name)
0040 
0041     JNI_STATIC_METHOD(void, noRetNoArg)
0042     JNI_STATIC_METHOD(jlong, retNoArg)
0043     JNI_STATIC_METHOD(void, noRetArg, java::lang::String)
0044     JNI_STATIC_METHOD(android::content::Intent, retArg, bool)
0045 
0046     // bool/jboolean arguments
0047     JNI_METHOD(void, setBool, bool)
0048     JNI_CONSTRUCTOR(TestClass, bool)
0049 
0050     // basic C++ types that do not map to JNI (must not compile)
0051 //     JNI_METHOD(void, setUnsigned, uint32_t)
0052 //     JNI_STATIC_METHOD(void, setStaticUnsigned, uint64_t)
0053 //     JNI_METHOD(char, charReturn)
0054 //     JNI_STATIC_METHOD(uint64_t, staticCharReturn);
0055 //     JNI_CONSTRUCTOR(TestClass, char)
0056 
0057     friend class JniMethodTest;
0058 };
0059 
0060 class JniMethodTest : public QObject
0061 {
0062     Q_OBJECT
0063 private Q_SLOTS:
0064     void testMethodCalls()
0065     {
0066 #ifndef Q_OS_ANDROID
0067         TestClass obj;
0068         QString s = obj.getName();
0069         Q_UNUSED(s);
0070         obj.setName(QStringLiteral("bla"));
0071          // explicit cast needed when coming from untyped JNI handler
0072         obj.setName(Jni::Object<java::lang::String>(QJniObject::fromString(QStringLiteral("bla"))));
0073         int i = obj.getFlags();
0074         Q_UNUSED(i);
0075         obj.setFlags(42);
0076         obj.start();
0077         bool b = obj.setCoordinates(0.0f, 0.0f);
0078         Q_UNUSED(b)
0079 
0080         // implicit conversion
0081         b = obj.setCoordinates(0.0, 0.0);
0082         // implicit conversion, and must not copy the property wrapper
0083         obj.setName(obj.name);
0084         // implicit conversion from manual wrappers
0085         Intent intent;
0086         obj.startIntent(intent);
0087         // returning a non-wrapped type
0088         QJniObject j = obj.getIntent();
0089         // lvalue QJniObject argument needs explicit cast
0090         obj.setName(Jni::Object<java::lang::String>(j));
0091         // implicit conversion from a static property wrapper
0092         obj.setFlags(Intent::FLAG_GRANT_READ_URI_PERMISSION);
0093 
0094         // avoiding type conversion on return values
0095         j = obj.getName();
0096         // call chaining with JNI handle pass-through
0097         obj.setName(obj.getName());
0098 
0099         // conversion of array return types
0100         j = obj.getStringList();
0101         QStringList l = obj.getStringList();
0102         std::vector<QString> l2 = obj.getStringList();
0103         std::vector<jshort> l3 = obj.getShortList();
0104 
0105         // nullptr arguments
0106         obj.startIntent(nullptr);
0107 
0108         // overloaded methods
0109         obj.overloaded();
0110         obj.overloaded(42);
0111         obj.overloaded(QStringLiteral("hello"), true);
0112         Jni::Array<jshort> l4 = obj.overloaded(intent);
0113         obj.overloaded(23, QStringLiteral("world"), l4);
0114 
0115         // bool conversion
0116         obj.setBool(true);
0117 
0118         QCOMPARE(obj.jniHandle().protocol().size(), 28);
0119         QCOMPARE(obj.jniHandle().protocol()[0], QLatin1StringView("callObjectMethod: getName ()Ljava/lang/String; ()"));
0120         QCOMPARE(obj.jniHandle().protocol()[1], QLatin1StringView("callMethod: setName (Ljava/lang/String;)V (o)"));
0121         QCOMPARE(obj.jniHandle().protocol()[2], QLatin1StringView("callMethod: setName (Ljava/lang/String;)V (o)"));
0122         QCOMPARE(obj.jniHandle().protocol()[3], QLatin1StringView("callMethod: getFlags ()I ()"));
0123         QCOMPARE(obj.jniHandle().protocol()[4], QLatin1StringView("callMethod: setFlags (I)V (I)"));
0124         QCOMPARE(obj.jniHandle().protocol()[5], QLatin1StringView("callMethod: start ()V ()"));
0125         QCOMPARE(obj.jniHandle().protocol()[6], QLatin1StringView("callMethod: setCoordinates (FF)Z (FF)"));
0126 
0127         QCOMPARE(obj.jniHandle().protocol()[7], QLatin1StringView("callMethod: setCoordinates (FF)Z (FF)"));
0128         QCOMPARE(obj.jniHandle().protocol()[8], QLatin1StringView("getObjectField: name Ljava/lang/String;"));
0129         QCOMPARE(obj.jniHandle().protocol()[9], QLatin1StringView("callMethod: setName (Ljava/lang/String;)V (o)"));
0130         QCOMPARE(obj.jniHandle().protocol()[10], QLatin1StringView("callMethod: startIntent (Landroid/content/Intent;)V (o)"));
0131         QCOMPARE(obj.jniHandle().protocol()[11], QLatin1StringView("callObjectMethod: getIntent ()Landroid/content/Intent; ()"));
0132         QCOMPARE(obj.jniHandle().protocol()[12], QLatin1StringView("callMethod: setName (Ljava/lang/String;)V (o)"));
0133         QCOMPARE(obj.jniHandle().protocol()[13], QLatin1StringView("callMethod: setFlags (I)V (I)"));
0134         QCOMPARE(obj.jniHandle().protocol()[14], QLatin1StringView("callObjectMethod: getName ()Ljava/lang/String; ()"));
0135         QCOMPARE(obj.jniHandle().protocol()[15], QLatin1StringView("callObjectMethod: getName ()Ljava/lang/String; ()"));
0136         QCOMPARE(obj.jniHandle().protocol()[16], QLatin1StringView("callMethod: setName (Ljava/lang/String;)V (o)"));
0137         QCOMPARE(obj.jniHandle().protocol()[17], QLatin1StringView("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
0138         QCOMPARE(obj.jniHandle().protocol()[18], QLatin1StringView("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
0139         QCOMPARE(obj.jniHandle().protocol()[19], QLatin1StringView("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
0140         QCOMPARE(obj.jniHandle().protocol()[20], QLatin1StringView("callObjectMethod: getShortList ()[S ()"));
0141         QCOMPARE(obj.jniHandle().protocol()[21], QLatin1StringView("callMethod: startIntent (Landroid/content/Intent;)V (o)"));
0142 
0143         QCOMPARE(obj.jniHandle().protocol()[22], QLatin1StringView("callMethod: overloaded ()V ()"));
0144         QCOMPARE(obj.jniHandle().protocol()[23], QLatin1StringView("callMethod: overloaded (I)V (I)"));
0145         QCOMPARE(obj.jniHandle().protocol()[24], QLatin1StringView("callMethod: overloaded (Ljava/lang/String;Z)V (oZ)"));
0146         QCOMPARE(obj.jniHandle().protocol()[25], QLatin1StringView("callObjectMethod: overloaded (Landroid/content/Intent;)[S (o)"));
0147         QCOMPARE(obj.jniHandle().protocol()[26], QLatin1StringView("callMethod: overloaded (JLjava/lang/String;[S)I (Joo)"));
0148         QCOMPARE(obj.jniHandle().protocol()[27], QLatin1StringView("callMethod: setBool (Z)V (Z)"));
0149 
0150         // ctor call
0151         obj = TestClass(intent);
0152         QCOMPARE(obj.jniHandle().protocol().size(), 1);
0153         QCOMPARE(obj.jniHandle().protocol()[0], QLatin1StringView("ctor: android/content/Intent (Landroid/content/Intent;)V (o)"));
0154         obj = TestClass(false);
0155         QCOMPARE(obj.jniHandle().protocol().size(), 1);
0156         QCOMPARE(obj.jniHandle().protocol()[0], QLatin1StringView("ctor: android/content/Intent (Z)V (Z)"));
0157 #if 0
0158         // stuff that must not compile
0159         obj.setName(42);
0160         obj.setFlags(QStringLiteral("42"));
0161         s = obj.getStringList();
0162         obj.setFlags(nullptr);
0163 #endif
0164 #endif
0165     }
0166 
0167     void testStaticCalls()
0168     {
0169 #ifndef Q_OS_ANDROID
0170         QJniObject::m_staticProtocol.clear();
0171 
0172         TestClass::noRetNoArg();
0173         TestClass::noRetArg(QStringLiteral("test"));
0174         TestClass::retNoArg();
0175         QJniObject o = TestClass::retArg(true);
0176 
0177         QCOMPARE(QJniObject::m_staticProtocol.size(), 3);
0178         QCOMPARE(QJniObject::m_staticProtocol[0], QLatin1StringView("callStaticMethod: android/content/Intent noRetNoArg ()V ()"));
0179         QCOMPARE(QJniObject::m_staticProtocol[1], QLatin1StringView("callStaticMethod: android/content/Intent noRetArg (Ljava/lang/String;)V (o)"));
0180         QCOMPARE(QJniObject::m_staticProtocol[2], QLatin1StringView("callStaticMethod: android/content/Intent retNoArg ()J ()"));
0181         QCOMPARE(o.protocol().at(0), QLatin1StringView("callStaticObjectMethod: android/content/Intent retArg (Z)Landroid/content/Intent; (Z)"));
0182 #endif
0183     }
0184 };
0185 
0186 QTEST_GUILESS_MAIN(JniMethodTest)
0187 
0188 #include "jnimethodtest.moc"