diff options
Diffstat (limited to 'tests/auto/qml/qjsengine/tst_qjsengine.cpp')
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 287 |
1 files changed, 266 insertions, 21 deletions
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 75fa2216f7..6ca2663f30 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -66,6 +66,10 @@ private slots: void newArray(); void newArray_HooliganTask218092(); void newArray_HooliganTask233836(); + void toScriptValue_data(); + void toScriptValue(); + void toScriptValuenotroundtripped_data(); + void toScriptValuenotroundtripped(); void newVariant(); void newVariant_valueOfToString(); void newVariant_valueOfEnum(); @@ -94,6 +98,7 @@ private slots: void valueConversion_basic2(); void valueConversion_dateTime(); void valueConversion_regExp(); + void valueConversion_RegularExpression(); void castWithMultipleInheritance(); void collectGarbage(); void gcWithNestedDataStructure(); @@ -135,6 +140,8 @@ private slots: void qRegExpInport_data(); void qRegExpInport(); + void qRegularExpressionImport_data(); + void qRegularExpressionImport(); void dateRoundtripJSQtJS(); void dateRoundtripQtJSQt(); void dateConversionJSQt(); @@ -238,6 +245,9 @@ private slots: void equality(); void aggressiveGc(); + void interrupt_data(); + void interrupt(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -481,17 +491,97 @@ void tst_QJSEngine::newArray_HooliganTask233836() } } +void tst_QJSEngine::toScriptValue_data() +{ + QTest::addColumn<QVariant>("input"); + + QTest::newRow("UnknownType") << QVariant(int(QMetaType::UnknownType), nullptr); + QTest::newRow("Nullptr") << QVariant(int(QMetaType::Nullptr), nullptr); + QTest::newRow("true") << QVariant(true); + QTest::newRow("false") << QVariant(false); + QTest::newRow("int") << QVariant(int(42)); + QTest::newRow("uint") << QVariant(uint(42)); + QTest::newRow("longlong") << QVariant(qlonglong(4242)); + QTest::newRow("ulonglong") << QVariant(qulonglong(4242)); + QTest::newRow("double") << QVariant(double(42.42)); + QTest::newRow("float") << QVariant(float(42.42)); + QTest::newRow("qstring") << QVariant(QString::fromLatin1("hello")); + QTest::newRow("qbytearray") << QVariant(QByteArray("hello")); + QTest::newRow("short") << QVariant(short('r')); + QTest::newRow("ushort") << QVariant(short('b')); + QTest::newRow("char") << QVariant(char('r')); + QTest::newRow("uchar") << QVariant(uchar('b')); + QTest::newRow("qchar") << QVariant(QString::fromUtf8("å").at(0)); + QTest::newRow("qdate") << QVariant(QDate(1925, 5, 8)); + QTest::newRow("qtime") << QVariant(QTime(4, 5, 6)); + QTest::newRow("qregularexpression") << QVariant(QRegularExpression(".*")); + QTest::newRow("qpointf") << QVariant(QPointF(42, 24)); + QTest::newRow("qvariantlist") << QVariant(QVariantList() << 42.24 << 5 << "hello"); + QTest::newRow("qvariantlist_point") << QVariant(QVariantList() << 42.24 << QPointF(42.24, 24.42) << QPointF(24.42, 42.24)); + QVariantMap vm; vm.insert("test", 55); vm.insert("abc", 42.42);; + QTest::newRow("qvariantmap") << QVariant(vm); + vm.clear(); vm.insert("point1", QPointF(42.24, 24.42)); vm.insert("point2", QPointF(42.24, 24.42)); + QTest::newRow("qvariantmap_point") << QVariant(vm); + QTest::newRow("qvariant") << QVariant(QVariant(42)); + QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>() << 1 << 2 << 3 << 4); + QTest::newRow("QVector<int>") << QVariant::fromValue(QVector<int>() << 1 << 2 << 3 << 4); + QTest::newRow("QList<QString>") << QVariant::fromValue(QVector<QString>() << "1" << "2" << "3" << "4"); + QTest::newRow("QStringList") << QVariant::fromValue(QStringList() << "1" << "2" << "3" << "4"); + QTest::newRow("QMap<QString, QString>") << QVariant::fromValue(QMap<QString, QString>{{ "1", "2" }, { "3", "4" }}); + QTest::newRow("QHash<QString, QString>") << QVariant::fromValue(QHash<QString, QString>{{ "1", "2" }, { "3", "4" }}); + QTest::newRow("QMap<QString, QPointF>") << QVariant::fromValue(QMap<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }}); + QTest::newRow("QHash<QString, QPointF>") << QVariant::fromValue(QHash<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }}); +} + +void tst_QJSEngine::toScriptValue() +{ + QFETCH(QVariant, input); + + QJSEngine engine; + QJSValue outputJS = engine.toScriptValue(input); + QVariant output = engine.fromScriptValue<QVariant>(outputJS); + + QCOMPARE(input, output); +} + +void tst_QJSEngine::toScriptValuenotroundtripped_data() +{ + QTest::addColumn<QVariant>("input"); + QTest::addColumn<QVariant>("output"); + + QTest::newRow("QList<QObject*>") << QVariant::fromValue(QList<QObject*>() << this) << QVariant(QVariantList() << QVariant::fromValue(this)); + QTest::newRow("QObjectList") << QVariant::fromValue(QObjectList() << this) << QVariant(QVariantList() << QVariant::fromValue(this)); + QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)); + QTest::newRow("QVector<QPoint>") << QVariant::fromValue(QVector<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)); + QTest::newRow("VoidStar") << QVariant(int(QMetaType::VoidStar), nullptr) << QVariant(int(QMetaType::Nullptr), nullptr); + QTest::newRow("qregex") << QVariant(QRegExp(".*", Qt::CaseSensitive, QRegExp::RegExp2)) << QVariant(QRegularExpression(".*")); +} + +// This is almost the same as toScriptValue, but the inputs don't roundtrip to +// exactly the same value. +void tst_QJSEngine::toScriptValuenotroundtripped() +{ + QFETCH(QVariant, input); + QFETCH(QVariant, output); + + QJSEngine engine; + QJSValue outputJS = engine.toScriptValue(input); + QVariant actualOutput = engine.fromScriptValue<QVariant>(outputJS); + + QCOMPARE(actualOutput, output); +} + void tst_QJSEngine::newVariant() { QJSEngine eng; { QJSValue opaque = eng.toScriptValue(QVariant(QPoint(1, 2))); QVERIFY(!opaque.isUndefined()); - QCOMPARE(opaque.isVariant(), true); + QCOMPARE(opaque.isVariant(), false); QVERIFY(!opaque.isCallable()); QCOMPARE(opaque.isObject(), true); QVERIFY(!opaque.prototype().isUndefined()); - QCOMPARE(opaque.prototype().isVariant(), true); + QCOMPARE(opaque.prototype().isVariant(), false); QVERIFY(opaque.property("valueOf").callWithInstance(opaque).equals(opaque)); } } @@ -505,7 +595,7 @@ void tst_QJSEngine::newVariant_valueOfToString() QJSValue value = object.property("valueOf").callWithInstance(object); QVERIFY(value.isObject()); QVERIFY(value.strictlyEquals(object)); - QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint, QPoint(10,20))")); + QCOMPARE(object.toString(), QString::fromLatin1("QPoint(10, 20)")); } } @@ -523,22 +613,27 @@ void tst_QJSEngine::newVariant_valueOfEnum() void tst_QJSEngine::newRegExp() { QJSEngine eng; - QJSValue rexp = eng.toScriptValue(QRegExp("foo")); - QVERIFY(!rexp.isUndefined()); - QCOMPARE(rexp.isRegExp(), true); - QCOMPARE(rexp.isObject(), true); - QCOMPARE(rexp.isCallable(), false); - // prototype should be RegExp.prototype - QVERIFY(!rexp.prototype().isUndefined()); - QCOMPARE(rexp.prototype().isObject(), true); - // Get [[Class]] internal property of RegExp Prototype Object. - // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods". - // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object". - QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)"); - QCOMPARE(r.toString(), QString::fromLatin1("[object Object]")); - QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); + QJSValue rexps[] = { + eng.toScriptValue(QRegularExpression("foo")), + eng.toScriptValue(QRegExp("foo")) + }; + for (const auto &rexp : rexps) { + QVERIFY(!rexp.isUndefined()); + QCOMPARE(rexp.isRegExp(), true); + QCOMPARE(rexp.isObject(), true); + QCOMPARE(rexp.isCallable(), false); + // prototype should be RegExp.prototype + QVERIFY(!rexp.prototype().isUndefined()); + QCOMPARE(rexp.prototype().isObject(), true); + // Get [[Class]] internal property of RegExp Prototype Object. + // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods". + // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object". + QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)"); + QCOMPARE(r.toString(), QString::fromLatin1("[object Object]")); + QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); - QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern()); + QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern()); + } } void tst_QJSEngine::jsRegExp() @@ -1490,15 +1585,15 @@ void tst_QJSEngine::valueConversion_QVariant() QCOMPARE(val.toString(), str); } { - QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this)); + QJSValue val = eng.toScriptValue(QVariant::fromValue((QObject*)this)); QVERIFY(!val.isVariant()); QVERIFY(val.isQObject()); QCOMPARE(val.toQObject(), (QObject*)this); } { - QVariant var = qVariantFromValue(QPoint(123, 456)); + QVariant var = QVariant::fromValue(QPoint(123, 456)); QJSValue val = eng.toScriptValue(var); - QVERIFY(val.isVariant()); + QVERIFY(!val.isVariant()); QCOMPARE(val.toVariant(), var); } @@ -1604,6 +1699,28 @@ void tst_QJSEngine::valueConversion_regExp() } } +void tst_QJSEngine::valueConversion_RegularExpression() +{ + QJSEngine eng; + { + QRegularExpression in = QRegularExpression("foo"); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QRegularExpression out = qjsvalue_cast<QRegularExpression>(val); + QCOMPARE(out.pattern(), in.pattern()); + QCOMPARE(out.patternOptions(), in.patternOptions()); + } + { + QRegularExpression in = QRegularExpression("foo", + QRegularExpression::CaseInsensitiveOption); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QCOMPARE(qjsvalue_cast<QRegularExpression>(val), in); + QRegularExpression out = qjsvalue_cast<QRegularExpression>(val); + QCOMPARE(out.patternOptions(), in.patternOptions()); + } +} + Q_DECLARE_METATYPE(QGradient) Q_DECLARE_METATYPE(QGradient*) Q_DECLARE_METATYPE(QLinearGradient) @@ -2953,6 +3070,8 @@ void tst_QJSEngine::reentrancy_objectCreation() QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')"); QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2)); QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1)); + QCOMPARE(qjsvalue_cast<QRegularExpression>(r1), qjsvalue_cast<QRegularExpression>(r2)); + QCOMPARE(qjsvalue_cast<QRegularExpression>(r2), qjsvalue_cast<QRegularExpression>(r1)); } { QJSValue o1 = eng1.newQObject(temp); @@ -3148,6 +3267,56 @@ void tst_QJSEngine::qRegExpInport() } } +void tst_QJSEngine::qRegularExpressionImport_data() +{ + QTest::addColumn<QRegularExpression>("rx"); + QTest::addColumn<QString>("string"); + QTest::addColumn<QString>("matched"); + + QTest::newRow("normal") << QRegularExpression("(test|foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("normal2") << QRegularExpression("(Test|Foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive") << QRegularExpression("(test|foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive2") << QRegularExpression("(Test|Foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo"; + QTest::newRow("b(a*)(b*)") << QRegularExpression("b(a*)(b*)", QRegularExpression::CaseInsensitiveOption) << "aaabbBbaAabaAaababaaabbaaab"; + QTest::newRow("greedy") << QRegularExpression("a*(a*)", QRegularExpression::CaseInsensitiveOption) << "aaaabaaba"; + QTest::newRow("wildcard") << QRegularExpression(".*\\.txt") << "file.txt"; + QTest::newRow("wildcard 2") << QRegularExpression("a.b\\.txt") << "ab.txt abb.rtc acb.txt"; + QTest::newRow("slash") << QRegularExpression("g/.*/s", QRegularExpression::CaseInsensitiveOption) << "string/string/string"; + QTest::newRow("slash2") << QRegularExpression("g / .* / s", QRegularExpression::CaseInsensitiveOption) << "string / string / string"; + QTest::newRow("fixed") << QRegularExpression("a\\*aa\\.a\\(ba\\)\\*a\\\\ba", QRegularExpression::CaseInsensitiveOption) << "aa*aa.a(ba)*a\\ba"; + QTest::newRow("fixed insensitive") << QRegularExpression("A\\*A", QRegularExpression::CaseInsensitiveOption) << "a*A A*a A*A a*a"; + QTest::newRow("fixed sensitive") << QRegularExpression("A\\*A") << "a*A A*a A*A a*a"; + QTest::newRow("html") << QRegularExpression("<b>(.*)</b>") << "<b>bold</b><i>italic</i><b>bold</b>"; + QTest::newRow("html minimal") << QRegularExpression("^<b>(.*)</b>$") << "<b>bold</b><i>italic</i><b>bold</b>"; + QTest::newRow("aaa") << QRegularExpression("a{2,5}") << "aAaAaaaaaAa"; + QTest::newRow("aaa minimal") << QRegularExpression("^a{2,5}$") << "aAaAaaaaaAa"; + QTest::newRow("minimal") << QRegularExpression("^.*\\} [*8]$") << "}?} ?} *"; + QTest::newRow(".? minimal") << QRegularExpression("^.?$") << ".?"; + QTest::newRow(".+ minimal") << QRegularExpression("^.+$") << ".+"; + QTest::newRow("[.?] minimal") << QRegularExpression("^[.?]$") << ".?"; + QTest::newRow("[.+] minimal") << QRegularExpression("^[.+]$") << ".+"; +} + +void tst_QJSEngine::qRegularExpressionImport() +{ + QFETCH(QRegularExpression, rx); + QFETCH(QString, string); + + QJSEngine eng; + QJSValue rexp; + rexp = eng.toScriptValue(rx); + + QCOMPARE(rexp.isRegExp(), true); + QCOMPARE(rexp.isCallable(), false); + + QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })"); + QJSValue result = func.call(QJSValueList() << string << rexp); + + const QRegularExpressionMatch match = rx.match(string); + for (int i = 0; i <= match.lastCapturedIndex(); i++) + QCOMPARE(result.property(i).toString(), match.captured(i)); +} + // QScriptValue::toDateTime() returns a local time, whereas JS dates // are always stored as UTC. Qt Script must respect the current time // zone, and correctly adjust for daylight saving time that may be in @@ -4673,6 +4842,82 @@ void tst_QJSEngine::aggressiveGc() qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc); } +void tst_QJSEngine::interrupt_data() +{ + QTest::addColumn<int>("jitThreshold"); + QTest::addColumn<QString>("code"); + + const int big = (1 << 24); + for (int i = 0; i <= big; i += big) { + const char *mode = i ? "interpret" : "jit"; + QTest::addRow("for with content / %s", mode) << i << "var a = 0; for (;;) { a += 2; }"; + QTest::addRow("for empty / %s", mode) << i << "for (;;) {}"; + QTest::addRow("for continue / %s", mode) << i << "for (;;) { continue; }"; + QTest::addRow("while with content / %s", mode) << i << "var a = 0; while (true) { a += 2; }"; + QTest::addRow("while empty / %s", mode) << i << "while (true) {}"; + QTest::addRow("while continue / %s", mode) << i << "while (true) { continue; }"; + QTest::addRow("do with content / %s", mode) << i << "var a = 0; do { a += 2; } while (true);"; + QTest::addRow("do empty / %s", mode) << i << "do {} while (true);"; + QTest::addRow("do continue / %s", mode) << i << "do { continue; } while (true);"; + QTest::addRow("nested loops / %s", mode) << i << "while (true) { for (;;) {} }"; + QTest::addRow("labeled continue / %s", mode) << i << "a: while (true) { for (;;) { continue a; } }"; + QTest::addRow("labeled break / %s", mode) << i << "while (true) { a: for (;;) { break a; } }"; + QTest::addRow("tail call / %s", mode) << i << "'use strict';\nfunction x() { return x(); }; x();"; + } +} + +class TemporaryJitThreshold +{ + Q_DISABLE_COPY_MOVE(TemporaryJitThreshold) +public: + TemporaryJitThreshold(int threshold) { + m_wasSet = qEnvironmentVariableIsSet(m_envVar); + m_value = qgetenv(m_envVar); + qputenv(m_envVar, QByteArray::number(threshold)); + } + + ~TemporaryJitThreshold() + { + if (m_wasSet) + qputenv(m_envVar, m_value); + else + qunsetenv(m_envVar); + } + +private: + const char *m_envVar = "QV4_JIT_CALL_THRESHOLD"; + bool m_wasSet = false; + QByteArray m_value; +}; + +void tst_QJSEngine::interrupt() +{ + QFETCH(int, jitThreshold); + QFETCH(QString, code); + + TemporaryJitThreshold threshold(jitThreshold); + Q_UNUSED(threshold); + + QJSEngine *engineInThread = nullptr; + QScopedPointer<QThread> worker(QThread::create([&engineInThread, &code, jitThreshold](){ + QJSEngine jsEngine; + engineInThread = &jsEngine; + QJSValue result = jsEngine.evaluate(code); + QVERIFY(jsEngine.isInterrupted()); + QVERIFY(result.isError()); + QCOMPARE(result.toString(), QString::fromLatin1("Error: Interrupted")); + engineInThread = nullptr; + })); + worker->start(); + + QTRY_VERIFY(engineInThread); + + engineInThread->setInterrupted(true); + + QVERIFY(worker->wait()); + QVERIFY(!engineInThread); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" |