diff options
Diffstat (limited to 'tests')
13 files changed, 1159 insertions, 525 deletions
diff --git a/tests/auto/corelib/global/q23/expected/tst_q23_expected.cpp b/tests/auto/corelib/global/q23/expected/tst_q23_expected.cpp index 6de836da9d0..92df31d1a75 100644 --- a/tests/auto/corelib/global/q23/expected/tst_q23_expected.cpp +++ b/tests/auto/corelib/global/q23/expected/tst_q23_expected.cpp @@ -20,7 +20,7 @@ private Q_SLOTS: void tst_q23_expected::value_throw_exception_unreachable_data() { QTest::addColumn<bool>("unexpected"); - QTest::addRow("") << false; + QTest::addRow("expected") << false; } void tst_q23_expected::value_throw_exception_unreachable() diff --git a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt index 98509dc0e5f..1d320b49c86 100644 --- a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt @@ -14,8 +14,16 @@ endif() qt_internal_add_test(tst_qjniobject SOURCES tst_qjniobject.cpp + LIBRARIES + Qt::CorePrivate ) +# if we can, enable C++23 so that we can use std::expected in the test +# otherwise the test will use our own tl::expected copy from qexpected_p.h +if ("${CMAKE_CXX_COMPILE_FEATURES}" MATCHES "cxx_std_23") + set_property(TARGET tst_qjniobject PROPERTY CXX_STANDARD 23) +endif() + if(ANDROID) set_property(TARGET tst_qjniobject APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/testdata diff --git a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java index e0765fcb7e0..bdec173ca97 100644 --- a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java +++ b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java @@ -137,6 +137,10 @@ public class QtJniObjectTestClass public Throwable throwableMethod() { return staticThrowableMethod(); } // -------------------------------------------------------------------------------------------- + public static void staticThrowingMethod() throws Throwable { throw new Throwable(A_STRING_OBJECT); } + public void throwingMethod() throws Throwable { throw new Throwable(A_STRING_OBJECT); } + + // -------------------------------------------------------------------------------------------- public static Object[] staticObjectArrayMethod() { Object[] array = { new Object(), new Object(), new Object() }; return array; } public Object[] objectArrayMethod() { return staticObjectArrayMethod(); } diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index c06b500778b..215b3bf3b78 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -8,12 +8,20 @@ #include <QtCore/QJniObject> #include <QTest> +#if defined(__cpp_lib_expected) +# include <expected> +#else +# include <QtCore/private/qexpected_p.h> +#endif + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; static constexpr const char testClassName[] = "org/qtproject/qt/android/testdatapackage/QtJniObjectTestClass"; Q_DECLARE_JNI_CLASS(QtJniObjectTestClass, testClassName) +Q_DECLARE_JNI_CLASS(NoSuchClass, "no/such/Class") + using TestClass = QtJniTypes::QtJniObjectTestClass; static const jbyte A_BYTE_VALUE = 127; @@ -125,6 +133,24 @@ private slots: void callback(); void callStaticOverloadResolution(); + void implicitExceptionHandling_construct(); + void implicitExceptionHandling_callMethod(); + void implicitExceptionHandling_callStaticMethod(); + void implicitExceptionHandling_getField(); + void implicitExceptionHandling_setField(); + void implicitExceptionHandling_getStaticField(); + void implicitExceptionHandling_setStaticField(); + + void constructWithException(); + void callMethodWithException(); + void callMethodWithMonadic(); + void callMethodWithTryCatch(); + void callStaticMethodWithException(); + void getFieldWithException(); + void setFieldWithException(); + void getStaticFieldWithException(); + void setStaticFieldWithException(); + void cleanupTestCase(); }; @@ -2303,8 +2329,547 @@ void tst_QJniObject::callStaticOverloadResolution() QCOMPARE(result, value); } -QT_END_NAMESPACE +void tst_QJniObject::implicitExceptionHandling_construct() +{ + QJniEnvironment env; + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); + + // Constructor, including named constructor + { + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject testObject("NoSuchClass"); + QVERIFY(!env.checkAndClearExceptions()); + } + { + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + QJniObject testObject = QJniObject::construct<TestClass>(u"NoSuchConstructor"_s); + QVERIFY(!env.checkAndClearExceptions()); + } +} + +void tst_QJniObject::implicitExceptionHandling_callMethod() +{ + QJniEnvironment env; + const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); + const QRegularExpression throwingMethod("java.lang.Throwable: .*"); + + QJniObject testObject = QJniObject::construct<TestClass>(); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + testObject.callMethod<void>("noSuchMethod1"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + testObject.callMethod<void>("noSuchMethod2", "()V"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, throwingMethod); + testObject.callMethod<void>("throwingMethod"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + (void)testObject.callObjectMethod<jstring>("noSuchMethod"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + testObject.callObjectMethod("noSuchMethod", "()V"); + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniObject::implicitExceptionHandling_callStaticMethod() +{ + QJniEnvironment env; + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); + const QRegularExpression throwingMethod("java.lang.Throwable: .*"); + + const jclass classId = env.findClass<TestClass>(); + QVERIFY(classId != nullptr); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + TestClass::callStaticMethod<void>("noSuchStaticMethod"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + QJniObject::callStaticMethod<void>(QtJniTypes::Traits<TestClass>::className(), + "noSuchStaticMethod2", "()V"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject::callStaticMethod<void>("noSuchClass", "noSuchStaticMethod2", "()V"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, throwingMethod); + jmethodID methodId = env.findStaticMethod<void>(classId, "staticThrowingMethod"); + QVERIFY(methodId != nullptr); + QJniObject::callStaticMethod<void>(classId, methodId); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject::callStaticObjectMethod("noSuchClass", "noSuchStaticMethod", "()V"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + QJniObject::callStaticObjectMethod(classId, "noSuchStaticMethod", "()V"); + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniObject::implicitExceptionHandling_getField() +{ + QJniEnvironment env; + const QRegularExpression invalidField("java.lang.NoSuchFieldError: .*"); + + QJniObject testObject = QJniObject::construct<TestClass>(); + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)testObject.getField<int>("noSuchField"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)testObject.getObjectField<jobject>("noSuchObjectField"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)testObject.getObjectField("noSuchObjectField2", "Ljava/lang/Object;"); + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniObject::implicitExceptionHandling_setField() +{ + QJniEnvironment env; + const QRegularExpression invalidField("java.lang.NoSuchFieldError: .*"); + + QJniObject testObject = QJniObject::construct<TestClass>(); + QTest::ignoreMessage(QtWarningMsg, invalidField); + testObject.setField("noSuchField", 123); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + testObject.setField("BOOLEAN_VAR", "I", 123); + QVERIFY(!env.checkAndClearExceptions()); + + // make sure that code specifying deducible type explicitly still works + static_assert(std::is_same_v<decltype(testObject.setField<jboolean>("BOOLEAN_VAR", true)), + void>); +} + +void tst_QJniObject::implicitExceptionHandling_getStaticField() +{ + QJniEnvironment env; + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + const QRegularExpression invalidField("java.lang.NoSuchFieldError: .*"); + + const jclass classId = env.findClass<TestClass>(); + QVERIFY(classId != nullptr); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)TestClass::getStaticField<int>("noSuchStaticField"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)QJniObject::getStaticField<int>(classId, "noSuchStaticField"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidClass); + (void)QJniObject::getStaticObjectField("noSuchClass", "noSuchStaticField", "I"); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + (void)QJniObject::getStaticObjectField(classId, "S_BOOLEAN_VAR", "I"); + QVERIFY(!env.checkAndClearExceptions()); +} + +void tst_QJniObject::implicitExceptionHandling_setStaticField() +{ + QJniEnvironment env; + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + const QRegularExpression invalidField("java.lang.NoSuchFieldError: .*"); + + const jclass classId = env.findClass<TestClass>(); + QVERIFY(classId != nullptr); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + TestClass::setStaticField("noSuchStaticField", 123); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + QJniObject::setStaticField(classId, "noSuchStaticField", 123); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject::setStaticField("noSuchClass", "noSuchStaticField", 123); + QVERIFY(!env.checkAndClearExceptions()); + + QTest::ignoreMessage(QtWarningMsg, invalidField); + QJniObject::setStaticField(QtJniTypes::Traits<TestClass>::className(), + "S_BOOLEAN_VAR", "I", 123); + QVERIFY(!env.checkAndClearExceptions()); + + // make sure that code specifying deducible type explicitly still works + static_assert(std::is_same_v<decltype(TestClass::setStaticField<jboolean>("S_BOOLEAN_VAR", true)), + void>); +} + +#if __cpp_lib_expected +template <typename T> +using QJniReturnValue = std::expected<T, jthrowable>; +using BadAccessException = std::bad_expected_access<jthrowable>; +// even with __cpp_lib_expected >= 202211L, monadic functions seem to be rather +// broken or not reliably available +#define EXPECTED_HAS_MONADIC false +#elif TL_EXPECTED_VERSION_MAJOR +#define EXPECTED_HAS_MONADIC true +template <typename T> +using QJniReturnValue = tl::expected<T, jthrowable>; +using BadAccessException = tl::bad_expected_access<jthrowable>; +#endif + +static_assert(QtJniTypes::Traits<QJniReturnValue<int>>::signature() == + QtJniTypes::Traits<int>::signature()); +static_assert(QtJniTypes::Traits<QJniReturnValue<QString>>::signature() == + QtJniTypes::Traits<QString>::signature()); + + +void tst_QJniObject::constructWithException() +{ +#if __cpp_lib_expected + qInfo() << "Testing explicit exception handling with std::expected" << __cpp_lib_expected; +#elif defined(TL_EXPECTED_VERSION_MAJOR) + qInfo() << "Testing explicit exception handling with tl::expected"; +#else + qInfo() << "Testing explicit exception handling with QJniReturnValue"; +#endif + + const QRegularExpression invalidClass("java.lang.ClassNotFoundException: .*"); + { + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QJniObject invalid = QJniObject::construct<QtJniTypes::NoSuchClass>(); + } + + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); + + { + // can only handle exceptions when using the named constructor + auto result = QJniObject::construct<QJniReturnValue<TestClass>>(); + QVERIFY(result); + result = QJniObject::construct<QJniReturnValue<TestClass>>(u"123"_s); + QVERIFY(!result); + QVERIFY(result.error()); + } + + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); + + { + const auto result = QJniObject::construct<QJniReturnValue<QtJniTypes::NoSuchClass>>(); + QVERIFY(!result); + QVERIFY(result.error()); + } + + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); + + { + // no way to prevent implicit exception here + QTest::ignoreMessage(QtWarningMsg, invalidClass); + QtJniTypes::NoSuchClass noSuchClass; + QVERIFY(!noSuchClass.isValid()); + } + + QVERIFY(!QJniEnvironment().checkAndClearExceptions()); +} + +void tst_QJniObject::callMethodWithException() +{ + TestClass testObject; + { + auto result = testObject.callMethod<QJniReturnValue<void>>("voidMethod"); + QVERIFY(result); + result = testObject.callMethod<QJniReturnValue<void>>("voidMethod", 123); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = testObject.callMethod<QJniReturnValue<jint>>("intMethod"); + QVERIFY(result); + QCOMPARE(result.value(), A_INT_VALUE); + result = testObject.callMethod<QJniReturnValue<jint>>("intMethod", 123); + QVERIFY(!result); + QVERIFY(result.error()); + QCOMPARE(result.value_or(456), 456); + } + + { + auto result = testObject.callMethod<QJniReturnValue<jstring>>("stringMethod"); + QVERIFY(result); + QVERIFY(result.value()); + result = testObject.callMethod<QJniReturnValue<jstring>>("stringMethod", 123); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = testObject.callMethod<QJniReturnValue<QString>>("stringMethod"); + QVERIFY(result); + QVERIFY(!result.value().isEmpty()); + result = testObject.callMethod<QJniReturnValue<QString>>("stringMethod", 123); + QVERIFY(!result); + QVERIFY(result.error()); + QCOMPARE(result.value_or(u"Default"_s), u"Default"_s); + } + + { + QJniArray<jboolean> newArray(QList<jboolean>{true, false, false}); + auto result = testObject.callMethod<QJniReturnValue<QJniArray<jboolean>>>("reverseBooleanArray", newArray); + // this shorthand cannot work with e.g. std::expected + // result = testObject.callMethod<QJniReturnValue<jboolean[]>>("reverseBooleanArray", newArray); + QVERIFY(result); + QCOMPARE(result.value().toContainer(), (QList<jboolean>{false, false, true})); + result = testObject.callMethod<QJniReturnValue<QJniArray<jboolean>>>("reverseBooleanArray", 123); + QVERIFY(!result); + QVERIFY(result.error()); + } + + // throwing method - QJniObject cleans the exception and prints qWarning + { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"java.lang.Throwable: "_s + A_STRING_OBJECT() + u".*"_s)); + testObject.callMethod<void>("throwingMethod"); + + auto result = testObject.callMethod<QJniReturnValue<void>>("throwingMethod"); + QVERIFY(!result); + const QStringList stackTrace = QJniEnvironment::stackTrace(result.error()); + QCOMPARE_GE(stackTrace.size(), 1); + QCOMPARE(stackTrace.at(0), u"java.lang.Throwable: "_s + A_STRING_OBJECT()); + } +} + +void tst_QJniObject::callMethodWithMonadic() +{ +#if !EXPECTED_HAS_MONADIC + QSKIP("Used version of std::expected does not have monadic functions"); +#else + enum Monadic { + AndThen, + OrElse, + Transform, + }; + + TestClass testObject; + { + QList<Monadic> flow; + const auto result = testObject.callMethod<QJniReturnValue<void>>("voidMethod") + .and_then([&flow]{ + flow << AndThen; + return QJniReturnValue<void>(); + }) + .or_else([&flow](jthrowable error){ + if (error) + flow << OrElse; + else + qWarning("Invalid call to or_else monadic"); + return QJniReturnValue<void>(); + }) + .transform([&flow](){ + flow << Transform; + return 42; + }); + QCOMPARE(flow, (QList<Monadic>{AndThen, Transform})); + QVERIFY(result); + QCOMPARE(*result, 42); + } + { + QList<Monadic> flow; + const auto result = testObject.callMethod<QJniReturnValue<void>>("voidMethod", 123) + .and_then([&flow]{ + flow << AndThen; + return QJniReturnValue<void>(); + }) + .or_else([&flow](jthrowable error){ + if (error) + flow << OrElse; + else + qWarning("Invalid call to or_else monadic"); + return QJniReturnValue<void>(typename QJniReturnValue<void>::unexpected_type(error)); + }) + .transform([&flow](){ + flow << Transform; + return 42; + }); + QCOMPARE(flow, (QList<Monadic>{OrElse})); + QVERIFY(!result); + } + + { + QList<Monadic> flow; + const auto result = testObject.callMethod<QJniReturnValue<jobject>>("objectMethod") + .and_then([&flow](auto &&obj){ + flow << AndThen; + return QJniReturnValue<jobject>(obj); + }) + .or_else([&flow](jthrowable error){ + if (error) + flow << OrElse; + else + qWarning("Invalid call to or_else monadic"); + return QJniReturnValue<jobject>(typename QJniReturnValue<void>::unexpected_type(error)); + }) + .transform([&flow](auto &&obj){ + flow << Transform; + return QJniObject(obj).template getField<int>("INT_FIELD"); + }) + .and_then([&flow](auto value){ + flow << AndThen; + return QJniReturnValue<int>(value * 2); + }); + QCOMPARE(flow, (QList<Monadic>{AndThen, Transform, AndThen})); + QVERIFY(result); + QCOMPARE(*result, 246); + } + { + QList<Monadic> flow; + const auto result = testObject.callMethod<QJniReturnValue<jobject>>("objectMethod", 123) + .and_then([&flow](const QJniObject &obj){ + flow << AndThen; + return QJniReturnValue<jobject>(obj.object()); + }) + .or_else([&flow](jthrowable error){ + if (error) + flow << OrElse; + else + qWarning("Invalid call to or_else monadic"); + return QJniReturnValue<jobject>(typename QJniReturnValue<jobject>::unexpected_type(error)); + }) + .transform([&flow](const QJniObject &obj){ + flow << Transform; + return obj.getField<int>("INT_FIELD"); + }); + QCOMPARE(flow, (QList<Monadic>{OrElse})); + QVERIFY(!result); + } +#endif +} + +void tst_QJniObject::callMethodWithTryCatch() +{ + TestClass testObject; + + const QRegularExpression invalidMethod("java.lang.NoSuchMethodError: .*"); + QTest::ignoreMessage(QtWarningMsg, invalidMethod); + + try { + const auto result = testObject.callMethod<QJniReturnValue<QJniObject>>("objectMethod", 123); + result.value().getField<int>("INT_FIELD"); + } + catch (BadAccessException &e) { + qWarning().noquote() << QJniEnvironment::stackTrace(e.error()).join('\n'); + } +} + +void tst_QJniObject::callStaticMethodWithException() +{ + { + auto result = TestClass::callStaticMethod<QJniReturnValue<int>>("staticIntMethod"); + QVERIFY(result); + QCOMPARE(*result, A_INT_VALUE); + result = TestClass::callStaticMethod<QJniReturnValue<int>>("staticIntMethod", 123); + QVERIFY(!result && result.error()); + } + + { + auto result = TestClass::callStaticMethod<QJniReturnValue<QString>>("staticStringMethod"); + QVERIFY(result); + QCOMPARE(*result, A_STRING_OBJECT()); + result = TestClass::callStaticMethod<QJniReturnValue<QString>>("staticStringMethod", 123); + QVERIFY(!result && result.error()); + } + + // throwing method + { + const auto result = TestClass::callStaticMethod<QJniReturnValue<void>>("staticThrowingMethod"); + QVERIFY(!result); + QStringList stackTrace = QJniEnvironment::stackTrace(result.error()); + QCOMPARE_GE(stackTrace.size(), 1); + QCOMPARE(stackTrace.at(0), u"java.lang.Throwable: "_s + A_STRING_OBJECT()); + } +} + +void tst_QJniObject::getFieldWithException() +{ + TestClass testObject; + { + auto result = testObject.getField<QJniReturnValue<jboolean>>("BOOL_FIELD"); + QVERIFY(result); + result = testObject.getField<QJniReturnValue<jboolean>>("INVALID_BOOL"); + QVERIFY(!result && result.error()); + } + + { + auto result = testObject.getField<QJniReturnValue<QString>>("STRING_OBJECT_VAR"); + QVERIFY(result); + result = testObject.getField<QJniReturnValue<QString>>("INVALID_STRING"); + QVERIFY(!result && result.error()); + } +} + +void tst_QJniObject::setFieldWithException() +{ + TestClass testObject; + { + auto result = testObject.setField<QJniReturnValue<jboolean>>("BOOL_FIELD", true); + QVERIFY(result); + result = testObject.setField<QJniReturnValue<jboolean>>("SET_INVALID_BOOL", true); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = testObject.setField<QJniReturnValue<QString>>("STRING_OBJECT_VAR", u"test"_s); + QVERIFY(result); + result = testObject.setField<QJniReturnValue<QString>>("SET_INVALID_STRING", u"test"_s); + QVERIFY(!result); + QVERIFY(result.error()); + } +} + +void tst_QJniObject::getStaticFieldWithException() +{ + { + auto result = TestClass::getStaticField<QJniReturnValue<jshort>>("S_SHORT_VAR"); + QVERIFY(result); + result = TestClass::getStaticField<QJniReturnValue<jshort>>("S_INVALID_SHORT"); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = TestClass::getStaticField<QJniReturnValue<QString>>("S_STRING_OBJECT_VAR"); + QVERIFY(result); + result = TestClass::getStaticField<QJniReturnValue<QString>>("S_INVALID_STRING"); + QVERIFY(!result); + QVERIFY(result.error()); + } +} + +void tst_QJniObject::setStaticFieldWithException() +{ + { + auto result = TestClass::setStaticField<QJniReturnValue<jboolean>>("S_BOOLEAN_VAR", true); + QVERIFY(result); + result = TestClass::setStaticField<QJniReturnValue<jboolean>>("SET_S_INVALID_BOOL", true); + QVERIFY(!result); + QVERIFY(result.error()); + } + + { + auto result = TestClass::setStaticField<QJniReturnValue<QString>>("S_STRING_OBJECT_VAR", u"test"_s); + QVERIFY(result); + result = TestClass::setStaticField<QJniReturnValue<QString>>("SET_S_INVALID_STRING", u"test"_s); + QVERIFY(!result); + QVERIFY(result.error()); + } +} QTEST_MAIN(tst_QJniObject) +QT_END_NAMESPACE + #include "tst_qjniobject.moc" diff --git a/tests/auto/corelib/text/qstring/tst_qstring.cpp b/tests/auto/corelib/text/qstring/tst_qstring.cpp index db2b18a5c01..8217da843e0 100644 --- a/tests/auto/corelib/text/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/text/qstring/tst_qstring.cpp @@ -9409,7 +9409,7 @@ void tst_QString::rawData() void tst_QString::testUtf16() { { - const char16_t arr[] = {'a', 'b', 'c'}; + constexpr char16_t arr[] = {u'a', u'b', u'c'}; QString s = QString::fromRawData(arr, 3); // doesn't guarantee null-termination QCOMPARE(s.size(), qsizetype(std::size(arr))); // The string points to the raw data diff --git a/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp index b53362b43e9..f4699f92959 100644 --- a/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp +++ b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp @@ -20,6 +20,7 @@ private Q_SLOTS: void exceptions(); void earlyExitScope(); void mixedTypes(); + void sharedPtr(); private: void earlyExitScope_helper(int exitpoint, std::atomic<int> &member); }; @@ -221,6 +222,34 @@ void tst_QAtomicScopedValueRollback::mixedTypes() } } +void tst_QAtomicScopedValueRollback::sharedPtr() +{ +#ifdef __cpp_lib_atomic_shared_ptr + std::atomic<std::shared_ptr<int>> a{std::make_shared<int>(42)}; + QCOMPARE_NE(a.load(), nullptr); + QCOMPARE_EQ(*a.load(), 42); + { + QAtomicScopedValueRollback rb(a, nullptr); + QCOMPARE_EQ(a.load(), nullptr); + } + QCOMPARE_NE(a.load(), nullptr); + QCOMPARE_EQ(*a.load(), 42); + { + QAtomicScopedValueRollback rb{a, std::make_shared<int>(123)}; + QCOMPARE_NE(a.load(), nullptr); + QCOMPARE_EQ(*a.load(), 123); + rb.commit(); + a.store(std::make_shared<int>(256)); + QCOMPARE_NE(a.load(), nullptr); + QCOMPARE_EQ(*a.load(), 256); + } + QCOMPARE_NE(a.load(), nullptr); + QCOMPARE_EQ(*a.load(), 123); +#else + QSKIP("This test requires atomic<shared_ptr> support enabled in the C++ standard library."); +#endif +} + static void operator*=(std::atomic<int> &lhs, int rhs) { int expected = lhs.load(); diff --git a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp index 986cf2407b7..fd960581cf9 100644 --- a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp +++ b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #define QT_USE_QSTRINGBUILDER -#define QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT #include <QTest> diff --git a/tests/auto/wasm/CMakeLists.txt b/tests/auto/wasm/CMakeLists.txt index 35b4d45e533..ffa2b9ca98c 100644 --- a/tests/auto/wasm/CMakeLists.txt +++ b/tests/auto/wasm/CMakeLists.txt @@ -6,4 +6,5 @@ add_subdirectory(localfileapi) add_subdirectory(qwasmkeytranslator) add_subdirectory(qwasmwindowstack) add_subdirectory(qwasmwindowtreenode) +add_subdirectory(qwasmpromise) add_subdirectory(selenium) diff --git a/tests/auto/wasm/qwasmpromise/CMakeLists.txt b/tests/auto/wasm/qwasmpromise/CMakeLists.txt new file mode 100644 index 00000000000..44e6203ec86 --- /dev/null +++ b/tests/auto/wasm/qwasmpromise/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qwasmpromise Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwasmpromise LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qwasmpromise + SOURCES + tst_qwasmpromise.cpp + DEFINES + QT_NO_FOREACH + LIBRARIES + Qt::Core + Qt::Gui + Qt::GuiPrivate +) diff --git a/tests/auto/wasm/qwasmpromise/tst_qwasmpromise.cpp b/tests/auto/wasm/qwasmpromise/tst_qwasmpromise.cpp new file mode 100644 index 00000000000..0c8582d49a0 --- /dev/null +++ b/tests/auto/wasm/qwasmpromise/tst_qwasmpromise.cpp @@ -0,0 +1,526 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> + +#include <emscripten/val.h> +#include <emscripten.h> + +#include <QtCore/private/qstdweb_p.h> + +namespace { + +emscripten::val g_testSupport; + +void init() { + g_testSupport = emscripten::val::object(); + EM_ASM({ + var testSupport = Emval.toValue($0); + testSupport.resolve = {}; + testSupport.reject = {}; + testSupport.promises = {}; + testSupport.waitConditionPromise = new Promise((resolve, reject) => { + testSupport.finishWaiting = resolve; + }); + + testSupport.makeTestPromise = (param) => { + testSupport.promises[param] = new Promise((resolve, reject) => { + testSupport.resolve[param] = resolve; + testSupport.reject[param] = reject; + }); + + return testSupport.promises[param]; + }; + }, g_testSupport.as_handle()); +} +} + +class tst_QWasmPromise : public QObject +{ + Q_OBJECT + +public: + tst_QWasmPromise() = default; + +private slots: + void init(); + void suspendExclusive(); + void simpleResolve(); + void multipleResolve(); + void simpleReject(); + void multipleReject(); + void throwInThen(); + void bareFinally(); + void finallyWithThen(); + void finallyWithThrow(); + void finallyWithThrowInThen(); + void nested(); + void all(); + void allWithThrow(); + void allWithFinally(); + void allWithFinallyAndThrow(); +}; + +static bool g_Done = false; + +#define QWASMDONE() g_Done = true; + +void tst_QWasmPromise::init() { + g_Done = false; + ::init(); +} + +class BarrierCallback { +public: + BarrierCallback(int number, std::function<void()> onDone) + : m_remaining(number), m_onDone(std::move(onDone)) {} + + void operator()() { + if (!--m_remaining) { + m_onDone(); + } + } + +private: + int m_remaining; + std::function<void()> m_onDone; +}; + +void tst_QWasmPromise::suspendExclusive() +{ + init(); + + { + auto promise = qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("simpleResolve")) + .addThenFunction([](emscripten::val result) { + QVERIFY(result.isString()); + QCOMPARE("Some lovely data", result.as<std::string>()); + QWASMDONE(); + }) + .addCatchFunction([](emscripten::val error) { + Q_UNUSED(error); + QFAIL("Unexpected catch"); + QWASMDONE(); + }); + + g_testSupport["resolve"].call<void>("simpleResolve", std::string("Some lovely data")); + promise.suspendExclusive(); + } + QVERIFY(g_Done); + QVERIFY(qstdweb::Promise::State::numInstances() == 0); +} + +void tst_QWasmPromise::simpleResolve() +{ + init(); + + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("simpleResolve")) + .addThenFunction([](emscripten::val result) { + QVERIFY(result.isString()); + QCOMPARE("Some lovely data", result.as<std::string>()); + QWASMDONE(); + }) + .addCatchFunction([](emscripten::val error) { + Q_UNUSED(error); + QFAIL("Unexpected catch"); + QWASMDONE(); + }) + .addFinallyFunction([](){}); + + g_testSupport["resolve"].call<void>("simpleResolve", std::string("Some lovely data")); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::multipleResolve() +{ + init(); + static constexpr int promiseCount = 1000; + + auto onThen = std::make_shared<BarrierCallback>(promiseCount, []() { + QWASMDONE(); + }); + + for (int i = 0; i < promiseCount; ++i) { + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + (QStringLiteral("test") + QString::number(i)).toStdString()) + .addThenFunction([=](emscripten::val result) { + QVERIFY(result.isString()); + QCOMPARE(QString::number(i).toStdString(), result.as<std::string>()); + (*onThen)(); + }) + .addCatchFunction([](emscripten::val error) { + Q_UNUSED(error); + QFAIL("Unexpected catch"); + QWASMDONE(); + }); + } + + for (int i = 0; i < promiseCount; ++i) + g_testSupport["resolve"].call<void>(("test" + std::to_string(i)).c_str(), std::to_string(i)); + + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::simpleReject() +{ + init(); + + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("simpleReject")) + .addThenFunction([](emscripten::val result) { + Q_UNUSED(result); + QFAIL("Unexpected then"); + QWASMDONE(); + }) + .addCatchFunction([](emscripten::val result) { + QVERIFY(result.isString()); + QCOMPARE("Evil error", result.as<std::string>()); + QWASMDONE(); + }); + + g_testSupport["reject"].call<void>("simpleReject", std::string("Evil error")); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::multipleReject() +{ + static constexpr int promiseCount = 1000; + + auto onCatch = std::make_shared<BarrierCallback>(promiseCount, []() { + QWASMDONE(); + }); + + for (int i = 0; i < promiseCount; ++i) { + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + (QStringLiteral("test") + QString::number(i)).toStdString()) + .addThenFunction([](emscripten::val result) { + Q_UNUSED(result); + QFAIL("Unexpected then"); + }) + .addCatchFunction([=](emscripten::val error) { + Q_UNUSED(error); + (*onCatch)(); + }); + } + + for (int i = 0; i < promiseCount; ++i) + g_testSupport["reject"].call<void>(("test" + std::to_string(i)).c_str(), std::to_string(i)); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::throwInThen() +{ + init(); + QSKIP("Throw not supported"); + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("throwInThen")) + .addThenFunction([](emscripten::val result) { + Q_UNUSED(result); + EM_ASM({ + throw "Expected error"; + }); + }) + .addCatchFunction([](emscripten::val error) { + QCOMPARE("Expected error", error.as<std::string>()); + QWASMDONE(); + }); + + g_testSupport["resolve"].call<void>("throwInThen", std::string("Evil error")); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::bareFinally() +{ + init(); + + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("bareFinally")) + .addFinallyFunction([]() { + QWASMDONE(); + }); + + g_testSupport["resolve"].call<void>("bareFinally", std::string("Evil error")); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::finallyWithThen() +{ + init(); + + bool *thenCalled = new bool(false); + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("finallyWithThen")) + .addThenFunction([thenCalled] (emscripten::val result) { + Q_UNUSED(result); + *thenCalled = true; + }) + .addFinallyFunction([thenCalled]() { + QVERIFY(*thenCalled); + delete thenCalled; + QWASMDONE(); + }); + + g_testSupport["resolve"].call<void>("finallyWithThen", std::string("Evil error")); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::finallyWithThrow() +{ + init(); + + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("finallyWithThrow")) + .addCatchFunction([](emscripten::val error) { + Q_UNUSED(error); + }) + .addFinallyFunction([]() { + QWASMDONE(); + }); + + g_testSupport["reject"].call<void>("finallyWithThrow", std::string("Evil error")); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::finallyWithThrowInThen() +{ + init(); + QSKIP("Throw not supported"); + + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("finallyWithThrowInThen")) + .addThenFunction([](emscripten::val result) { + Q_UNUSED(result); + EM_ASM({ + throw "Expected error"; + }); + }) + .addCatchFunction([](emscripten::val result) { + QVERIFY(result.isString()); + QCOMPARE("Expected error", result.as<std::string>()); + }) + .addFinallyFunction([]() { + QWASMDONE(); + }); + + g_testSupport["resolve"].call<void>("finallyWithThrowInThen", std::string("Evil error")); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::nested() +{ + init(); + + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("outer")) + .addThenFunction([](emscripten::val result) { + QVERIFY(result.isString()); + QCOMPARE("Outer data", result.as<std::string>()); + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("inner")) + .addThenFunction([](emscripten::val innerResult) { + QVERIFY(innerResult.isString()); + QCOMPARE("Inner data", innerResult.as<std::string>()); + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + std::string("innermost")) + .addThenFunction([](emscripten::val innerResult) { + QVERIFY(innerResult.isString()); + QCOMPARE("Innermost data", innerResult.as<std::string>()); + QWASMDONE(); + }) + .addCatchFunction([](emscripten::val error) { + Q_UNUSED(error); + QFAIL("Unexpected catch"); + }); + g_testSupport["resolve"].call<void>("innermost", std::string("Innermost data")); + }); + g_testSupport["resolve"].call<void>("inner", std::string("Inner data")); + }) + .addCatchFunction([](emscripten::val error) { + Q_UNUSED(error); + QFAIL("Unexpected catch"); + }); + + g_testSupport["resolve"].call<void>("outer", std::string("Outer data")); + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::all() +{ + init(); + + { + static constexpr int promiseCount = 1000; + auto thenCalledOnce = std::make_shared<bool>(true); + + std::vector<qstdweb::Promise> promises; + promises.reserve(promiseCount); + + for (int i = 0; i < promiseCount; ++i) { + promises.push_back( + qstdweb::Promise( + g_testSupport, + "makeTestPromise", + emscripten::val(("all" + QString::number(i)).toStdString()))); + } + + qstdweb::Promise( + promises) + .addThenFunction([thenCalledOnce](emscripten::val result) { + QVERIFY(*thenCalledOnce); + *thenCalledOnce = false; + + QVERIFY(result.isArray()); + QCOMPARE(promiseCount, result["length"].as<int>()); + for (int i = 0; i < promiseCount; ++i) + QCOMPARE(QStringLiteral("Data %1").arg(i).toStdString(), result[i].as<std::string>()); + + QWASMDONE(); + }) + .addCatchFunction([](emscripten::val error) { + Q_UNUSED(error); + QFAIL("Unexpected catch"); + }); + + for (int i = promiseCount - 1; i >= 0; --i) + g_testSupport["resolve"].call<void>(("all" + std::to_string(i)).c_str(), "Data " + std::to_string(i)); + } + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::allWithThrow() +{ + init(); + + { + auto promise1 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise1")); + auto promise2 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise2")); + auto promise3 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise3")); + auto catchCalledOnce = std::make_shared<bool>(true); + + qstdweb::Promise( + {promise1, promise2, promise3}) + .addThenFunction([](emscripten::val result) { + Q_UNUSED(result); + QFAIL("Unexpected then"); + }) + .addCatchFunction([catchCalledOnce](emscripten::val result) { + QVERIFY(*catchCalledOnce); + *catchCalledOnce = false; + QVERIFY(result.isString()); + QCOMPARE("Error 2", result.as<std::string>()); + QWASMDONE(); + }); + + g_testSupport["resolve"].call<void>("promise3", std::string("Data 3")); + g_testSupport["resolve"].call<void>("promise1", std::string("Data 1")); + g_testSupport["reject"].call<void>("promise2", std::string("Error 2")); + } + + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::allWithFinally() +{ + init(); + { + auto promise1 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise1")); + auto promise2 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise2")); + auto promise3 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise3")); + + auto finallyCalledOnce = std::make_shared<bool>(true); + + qstdweb::Promise( + {promise1, promise2, promise3}) + .addThenFunction([](emscripten::val result) { + Q_UNUSED(result); + }) + .addFinallyFunction([finallyCalledOnce]() { + QVERIFY(*finallyCalledOnce); + *finallyCalledOnce = false; + QWASMDONE(); + }); + + g_testSupport["resolve"].call<void>("promise3", std::string("Data 3")); + g_testSupport["resolve"].call<void>("promise1", std::string("Data 1")); + g_testSupport["resolve"].call<void>("promise2", std::string("Data 2")); + } + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +void tst_QWasmPromise::allWithFinallyAndThrow() +{ + init(); + QSKIP("Throw not supported"); + { + auto promise1 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise1")); + auto promise2 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise2")); + auto promise3 = qstdweb::Promise(g_testSupport, "makeTestPromise", std::string("promise3")); + auto finallyCalledOnce = std::make_shared<bool>(true); + + qstdweb::Promise( + {promise1, promise2, promise3}) + .addThenFunction([](emscripten::val result) { + Q_UNUSED(result); + EM_ASM({ + throw "This breaks it all!!!"; + }); + }) + .addCatchFunction([](emscripten::val) { ; }) + .addFinallyFunction([finallyCalledOnce]() { + QVERIFY(*finallyCalledOnce); + *finallyCalledOnce = false; + QWASMDONE(); + }); + + g_testSupport["resolve"].call<void>("promise3", std::string("Data 3")); + g_testSupport["resolve"].call<void>("promise1", std::string("Data 1")); + g_testSupport["resolve"].call<void>("promise2", std::string("Data 2")); + } + + QVERIFY(QTest::qWaitFor([]() { return g_Done; })); + QVERIFY(QTest::qWaitFor([]() { return qstdweb::Promise::State::numInstances() == 0; })); +} + +QTEST_MAIN(tst_QWasmPromise) +#include "tst_qwasmpromise.moc" diff --git a/tests/manual/wasm/qstdweb/CMakeLists.txt b/tests/manual/wasm/qstdweb/CMakeLists.txt index 8904abc94b2..46a04a6cdb3 100644 --- a/tests/manual/wasm/qstdweb/CMakeLists.txt +++ b/tests/manual/wasm/qstdweb/CMakeLists.txt @@ -1,28 +1,5 @@ # Copyright (C) 2024 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -qt_internal_add_manual_test(promise_auto - SOURCES - promise_main.cpp - ../qtwasmtestlib/qtwasmtestlib.cpp - LIBRARIES - Qt::Core - Qt::CorePrivate -) - -include_directories(../qtwasmtestlib/) - -add_custom_command( - TARGET promise_auto POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/promise_auto.html - ${CMAKE_CURRENT_BINARY_DIR}/promise_auto.html) - -add_custom_command( - TARGET promise_auto POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/../qtwasmtestlib/qtwasmtestlib.js - ${CMAKE_CURRENT_BINARY_DIR}/qtwasmtestlib.js) - qt_internal_add_manual_test(files_auto SOURCES files_main.cpp diff --git a/tests/manual/wasm/qstdweb/promise_auto.html b/tests/manual/wasm/qstdweb/promise_auto.html deleted file mode 100644 index 94a8dbb88a2..00000000000 --- a/tests/manual/wasm/qstdweb/promise_auto.html +++ /dev/null @@ -1,10 +0,0 @@ -<!doctype html> -<script type="text/javascript" src="qtwasmtestlib.js"></script> -<script type="text/javascript" src="promise_auto.js"></script> -<script> - window.onload = () => { - runTestCase(promise_auto_entry, document.getElementById("log")); - }; -</script> -<p>Running promise auto test.</p> -<div id="log"></div> diff --git a/tests/manual/wasm/qstdweb/promise_main.cpp b/tests/manual/wasm/qstdweb/promise_main.cpp deleted file mode 100644 index 395e815cf95..00000000000 --- a/tests/manual/wasm/qstdweb/promise_main.cpp +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include <QtCore/QCoreApplication> -#include <QtCore/QEvent> -#include <QtCore/QMutex> -#include <QtCore/QObject> -#include <QtCore/QDebug> -#include <QtCore/private/qstdweb_p.h> - -#include <qtwasmtestlib.h> -#include <emscripten.h> - -using namespace emscripten; - -class WasmPromiseTest : public QObject -{ - Q_OBJECT - -public: - WasmPromiseTest() : m_window(val::global("window")), m_testSupport(val::object()) {} - - ~WasmPromiseTest() noexcept = default; - -private: - void init() { - m_testSupport = val::object(); - m_window.set("testSupport", m_testSupport); - - EM_ASM({ - testSupport.resolve = {}; - testSupport.reject = {}; - testSupport.promises = {}; - testSupport.waitConditionPromise = new Promise((resolve, reject) => { - testSupport.finishWaiting = resolve; - }); - - testSupport.makeTestPromise = (param) => { - testSupport.promises[param] = new Promise((resolve, reject) => { - testSupport.resolve[param] = resolve; - testSupport.reject[param] = reject; - }); - - return testSupport.promises[param]; - }; - }); - } - - val m_window; - val m_testSupport; - -private slots: - void simpleResolve(); - void multipleResolve(); - void simpleReject(); - void multipleReject(); - void throwInThen(); - void bareFinally(); - void finallyWithThen(); - void finallyWithThrow(); - void finallyWithThrowInThen(); - void nested(); - void all(); - void allWithThrow(); - void allWithFinally(); - void allWithFinallyAndThrow(); -}; - -class BarrierCallback { -public: - BarrierCallback(int number, std::function<void()> onDone) - : m_remaining(number), m_onDone(std::move(onDone)) {} - - void operator()() { - if (!--m_remaining) { - m_onDone(); - } - } - -private: - int m_remaining; - std::function<void()> m_onDone; -}; - -// Post event to the main thread and verify that it is processed. -void WasmPromiseTest::simpleResolve() -{ - init(); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [](val result) { - QWASMVERIFY(result.isString()); - QWASMCOMPARE("Some lovely data", result.as<std::string>()); - - QWASMSUCCESS(); - }, - .catchFunc = [](val error) { - Q_UNUSED(error); - - QWASMFAIL("Unexpected catch"); - } - }, std::string("simpleResolve")); - - EM_ASM({ - testSupport.resolve["simpleResolve"]("Some lovely data"); - }); -} - -void WasmPromiseTest::multipleResolve() -{ - init(); - - static constexpr int promiseCount = 1000; - - auto onThen = std::make_shared<BarrierCallback>(promiseCount, []() { - QWASMSUCCESS(); - }); - - for (int i = 0; i < promiseCount; ++i) { - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [=](val result) { - QWASMVERIFY(result.isString()); - QWASMCOMPARE(QString::number(i).toStdString(), result.as<std::string>()); - - (*onThen)(); - }, - .catchFunc = [](val error) { - Q_UNUSED(error); - QWASMFAIL("Unexpected catch"); - } - }, (QStringLiteral("test") + QString::number(i)).toStdString()); - } - - EM_ASM({ - for (let i = $0 - 1; i >= 0; --i) { - testSupport.resolve['test' + i](`${i}`); - } - }, promiseCount); -} - -void WasmPromiseTest::simpleReject() -{ - init(); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [](val result) { - Q_UNUSED(result); - QWASMFAIL("Unexpected then"); - }, - .catchFunc = [](val result) { - QWASMVERIFY(result.isString()); - QWASMCOMPARE("Evil error", result.as<std::string>()); - QWASMSUCCESS(); - } - }, std::string("simpleReject")); - - EM_ASM({ - testSupport.reject["simpleReject"]("Evil error"); - }); -} - -void WasmPromiseTest::multipleReject() -{ - static constexpr int promiseCount = 1000; - - auto onCatch = std::make_shared<BarrierCallback>(promiseCount, []() { - QWASMSUCCESS(); - }); - - for (int i = 0; i < promiseCount; ++i) { - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [=](val result) { - QWASMVERIFY(result.isString()); - QWASMCOMPARE(QString::number(i).toStdString(), result.as<std::string>()); - - (*onCatch)(); - }, - .catchFunc = [](val error) { - Q_UNUSED(error); - QWASMFAIL("Unexpected catch"); - } - }, (QStringLiteral("test") + QString::number(i)).toStdString()); - } - - EM_ASM({ - for (let i = $0 - 1; i >= 0; --i) { - testSupport.resolve['test' + i](`${i}`); - } - }, promiseCount); -} - -void WasmPromiseTest::throwInThen() -{ - init(); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [](val result) { - Q_UNUSED(result); - EM_ASM({ - throw "Expected error"; - }); - }, - .catchFunc = [](val error) { - QWASMCOMPARE("Expected error", error.as<std::string>()); - QWASMSUCCESS(); - } - }, std::string("throwInThen")); - - EM_ASM({ - testSupport.resolve["throwInThen"](); - }); -} - -void WasmPromiseTest::bareFinally() -{ - init(); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .finallyFunc = []() { - QWASMSUCCESS(); - } - }, std::string("bareFinally")); - - EM_ASM({ - testSupport.resolve["bareFinally"](); - }); -} - -void WasmPromiseTest::finallyWithThen() -{ - init(); - - bool *thenCalled = new bool(false); - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [thenCalled] (val result) { - Q_UNUSED(result); - *thenCalled = true; - }, - .finallyFunc = [thenCalled]() { - QWASMVERIFY(*thenCalled); - delete thenCalled; - QWASMSUCCESS(); - } - }, std::string("finallyWithThen")); - - EM_ASM({ - testSupport.resolve["finallyWithThen"](); - }); -} - -void WasmPromiseTest::finallyWithThrow() -{ - init(); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .catchFunc = [](val error) { - Q_UNUSED(error); - }, - .finallyFunc = []() { - QWASMSUCCESS(); - } - }, std::string("finallyWithThrow")); - - EM_ASM({ - testSupport.reject["finallyWithThrow"](); - }); -} - -void WasmPromiseTest::finallyWithThrowInThen() -{ - init(); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [](val result) { - Q_UNUSED(result); - EM_ASM({ - throw "Expected error"; - }); - }, - .catchFunc = [](val result) { - QWASMVERIFY(result.isString()); - QWASMCOMPARE("Expected error", result.as<std::string>()); - }, - .finallyFunc = []() { - QWASMSUCCESS(); - } - }, std::string("bareFinallyWithThen")); - - EM_ASM({ - testSupport.resolve["bareFinallyWithThen"](); - }); -} - -void WasmPromiseTest::nested() -{ - init(); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [this](val result) { - QWASMVERIFY(result.isString()); - QWASMCOMPARE("Outer data", result.as<std::string>()); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [this](val innerResult) { - QWASMVERIFY(innerResult.isString()); - QWASMCOMPARE("Inner data", innerResult.as<std::string>()); - - qstdweb::Promise::make(m_testSupport, "makeTestPromise", { - .thenFunc = [](val innerResult) { - QWASMVERIFY(innerResult.isString()); - QWASMCOMPARE("Innermost data", innerResult.as<std::string>()); - - QWASMSUCCESS(); - }, - .catchFunc = [](val error) { - Q_UNUSED(error); - QWASMFAIL("Unexpected catch"); - } - }, std::string("innermost")); - - EM_ASM({ - testSupport.resolve["innermost"]("Innermost data"); - }); - }, - .catchFunc = [](val error) { - Q_UNUSED(error); - QWASMFAIL("Unexpected catch"); - } - }, std::string("inner")); - - EM_ASM({ - testSupport.resolve["inner"]("Inner data"); - }); - }, - .catchFunc = [](val error) { - Q_UNUSED(error); - QWASMFAIL("Unexpected catch"); - } - }, std::string("outer")); - - EM_ASM({ - testSupport.resolve["outer"]("Outer data"); - }); -} - -void WasmPromiseTest::all() -{ - init(); - - static constexpr int promiseCount = 1000; - auto thenCalledOnce = std::shared_ptr<bool>(); - *thenCalledOnce = true; - - std::vector<val> promises; - promises.reserve(promiseCount); - - for (int i = 0; i < promiseCount; ++i) { - promises.push_back(m_testSupport.call<val>("makeTestPromise", val(("all" + QString::number(i)).toStdString()))); - } - - qstdweb::Promise::all(std::move(promises), { - .thenFunc = [=](val result) { - QWASMVERIFY(*thenCalledOnce); - *thenCalledOnce = false; - - QWASMVERIFY(result.isArray()); - QWASMCOMPARE(promiseCount, result["length"].as<int>()); - for (int i = 0; i < promiseCount; ++i) { - QWASMCOMPARE(QStringLiteral("Data %1").arg(i).toStdString(), result[i].as<std::string>()); - } - - QWASMSUCCESS(); - }, - .catchFunc = [](val error) { - Q_UNUSED(error); - QWASMFAIL("Unexpected catch"); - } - }); - - EM_ASM({ - console.log('Resolving'); - for (let i = $0 - 1; i >= 0; --i) { - testSupport.resolve['all' + i](`Data ${i}`); - } - }, promiseCount); -} - -void WasmPromiseTest::allWithThrow() -{ - init(); - - val promise1 = m_testSupport.call<val>("makeTestPromise", val("promise1")); - val promise2 = m_testSupport.call<val>("makeTestPromise", val("promise2")); - val promise3 = m_testSupport.call<val>("makeTestPromise", val("promise3")); - - auto catchCalledOnce = std::shared_ptr<bool>(); - *catchCalledOnce = true; - - qstdweb::Promise::all({promise1, promise2, promise3}, { - .thenFunc = [](val result) { - Q_UNUSED(result); - QWASMFAIL("Unexpected then"); - }, - .catchFunc = [catchCalledOnce](val result) { - QWASMVERIFY(*catchCalledOnce); - *catchCalledOnce = false; - QWASMVERIFY(result.isString()); - QWASMCOMPARE("Error 2", result.as<std::string>()); - QWASMSUCCESS(); - } - }); - - EM_ASM({ - testSupport.resolve["promise3"]("Data 3"); - testSupport.resolve["promise1"]("Data 1"); - testSupport.reject["promise2"]("Error 2"); - }); -} - -void WasmPromiseTest::allWithFinally() -{ - init(); - - val promise1 = m_testSupport.call<val>("makeTestPromise", val("promise1")); - val promise2 = m_testSupport.call<val>("makeTestPromise", val("promise2")); - val promise3 = m_testSupport.call<val>("makeTestPromise", val("promise3")); - - auto finallyCalledOnce = std::shared_ptr<bool>(); - *finallyCalledOnce = true; - - qstdweb::Promise::all({promise1, promise2, promise3}, { - .thenFunc = [](val result) { - Q_UNUSED(result); - }, - .finallyFunc = [finallyCalledOnce]() { - QWASMVERIFY(*finallyCalledOnce); - *finallyCalledOnce = false; - QWASMSUCCESS(); - } - }); - - EM_ASM({ - testSupport.resolve["promise3"]("Data 3"); - testSupport.resolve["promise1"]("Data 1"); - testSupport.resolve["promise2"]("Data 2"); - }); -} - -void WasmPromiseTest::allWithFinallyAndThrow() -{ - init(); - - val promise1 = m_testSupport.call<val>("makeTestPromise", val("promise1")); - val promise2 = m_testSupport.call<val>("makeTestPromise", val("promise2")); - val promise3 = m_testSupport.call<val>("makeTestPromise", val("promise3")); - - auto finallyCalledOnce = std::shared_ptr<bool>(); - *finallyCalledOnce = true; - - qstdweb::Promise::all({promise1, promise2, promise3}, { - .thenFunc = [](val result) { - Q_UNUSED(result); - EM_ASM({ - throw "This breaks it all!!!"; - }); - }, - .finallyFunc = [finallyCalledOnce]() { - QWASMVERIFY(*finallyCalledOnce); - *finallyCalledOnce = false; - QWASMSUCCESS(); - } - }); - - EM_ASM({ - testSupport.resolve["promise3"]("Data 3"); - testSupport.resolve["promise1"]("Data 1"); - testSupport.resolve["promise2"]("Data 2"); - }); -} - -int main(int argc, char **argv) -{ - auto testObject = std::make_shared<WasmPromiseTest>(); - QtWasmTest::initTestCase<QCoreApplication>(argc, argv, testObject); - return 0; -} - -#include "promise_main.moc" |
