diff options
Diffstat (limited to 'tests/auto/qscriptclass/tst_qscriptclass.cpp')
-rw-r--r-- | tests/auto/qscriptclass/tst_qscriptclass.cpp | 887 |
1 files changed, 674 insertions, 213 deletions
diff --git a/tests/auto/qscriptclass/tst_qscriptclass.cpp b/tests/auto/qscriptclass/tst_qscriptclass.cpp index 0645319..9ab8318 100644 --- a/tests/auto/qscriptclass/tst_qscriptclass.cpp +++ b/tests/auto/qscriptclass/tst_qscriptclass.cpp @@ -65,10 +65,27 @@ public: private slots: void newInstance(); - void getAndSetProperty(); + void setScriptClassOfExistingObject(); + void setScriptClassOfNonQtScriptObject(); + void getAndSetPropertyFromCpp(); + void getAndSetPropertyFromJS(); + void deleteUndeletableProperty(); + void writeReadOnlyProperty(); + void writePropertyWithoutWriteAccess(); void getProperty_invalidValue(); void enumerate(); - void extension(); + void extension_None(); + void extension_Callable(); + void extension_Callable_construct(); + void extension_HasInstance(); + void originalProperties1(); + void originalProperties2(); + void originalProperties3(); + void originalProperties4(); + void defaultImplementations(); + void scriptClassObjectInPrototype(); + void scriptClassWithNullEngine(); + void scriptClassInOtherEngine(); }; tst_QScriptClass::tst_QScriptClass() @@ -301,7 +318,12 @@ void TestClass::setProperty(QScriptValue &object, const QScriptString &name, CustomProperty *prop = findCustomProperty(name); if (!prop) return; - prop->value = value; + if (prop->pflags & QScriptValue::ReadOnly) + return; + if (!value.isValid()) // deleteProperty() requested + removeCustomProperty(name); + else + prop->value = value; } QScriptValue::PropertyFlags TestClass::propertyFlags( @@ -595,7 +617,7 @@ void tst_QScriptClass::newInstance() QScriptValue obj1 = eng.newObject(&cls); QVERIFY(!obj1.data().isValid()); QVERIFY(obj1.prototype().strictlyEquals(cls.prototype())); - QEXPECT_FAIL("", "classname is not implemented", Continue); + QEXPECT_FAIL("", "QTBUG-17599: classname is not implemented", Continue); QCOMPARE(obj1.toString(), QString::fromLatin1("[object TestClass]")); QCOMPARE(obj1.scriptClass(), (QScriptClass*)&cls); @@ -604,7 +626,14 @@ void tst_QScriptClass::newInstance() QVERIFY(obj2.data().strictlyEquals(num)); QVERIFY(obj2.prototype().strictlyEquals(cls.prototype())); QCOMPARE(obj2.scriptClass(), (QScriptClass*)&cls); + QVERIFY(!obj2.equals(obj1)); + QVERIFY(!obj2.strictlyEquals(obj1)); +} +void tst_QScriptClass::setScriptClassOfExistingObject() +{ + QScriptEngine eng; + TestClass cls(&eng); QScriptValue obj3 = eng.newObject(); QCOMPARE(obj3.scriptClass(), (QScriptClass*)0); obj3.setScriptClass(&cls); @@ -618,7 +647,12 @@ void tst_QScriptClass::newInstance() TestClass cls2(&eng); obj3.setScriptClass(&cls2); QCOMPARE(obj3.scriptClass(), (QScriptClass*)&cls2); +} +void tst_QScriptClass::setScriptClassOfNonQtScriptObject() +{ + QScriptEngine eng; + TestClass cls(&eng); // undefined behavior really, but shouldn't crash QScriptValue arr = eng.newArray(); QVERIFY(arr.isArray()); @@ -632,7 +666,7 @@ void tst_QScriptClass::newInstance() QVERIFY(arr.isObject()); } -void tst_QScriptClass::getAndSetProperty() +void tst_QScriptClass::getAndSetPropertyFromCpp() { QScriptEngine eng; @@ -644,7 +678,9 @@ void tst_QScriptClass::getAndSetProperty() QScriptString bar = eng.toStringHandle("bar"); QScriptValue num(&eng, 123); - // should behave just like normal + // Initially our TestClass instances have no custom properties, + // and queryProperty() will always return false. + // Hence, the properties will be created as normal JS properties. for (int x = 0; x < 2; ++x) { QScriptValue &o = (x == 0) ? obj1 : obj2; for (int y = 0; y < 2; ++y) { @@ -705,7 +741,7 @@ void tst_QScriptClass::getAndSetProperty() QCOMPARE(obj1.propertyFlags(foo2), foo2Pflags); QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1)); QVERIFY(cls.lastQueryPropertyName() == foo2); - QEXPECT_FAIL("", "classObject.getOwnPropertyDescriptor() reads the property value", Continue); + QEXPECT_FAIL("", "QTBUG-17601: classObject.getOwnPropertyDescriptor() reads the property value", Continue); QVERIFY(!cls.lastPropertyObject().isValid()); QVERIFY(cls.lastPropertyFlagsObject().strictlyEquals(obj1)); QVERIFY(cls.lastPropertyFlagsName() == foo2); @@ -731,6 +767,14 @@ void tst_QScriptClass::getAndSetProperty() QCOMPARE(cls.lastPropertyId(), foo2Id); } + // attempt to delete custom property + obj1.setProperty(foo2, QScriptValue()); + // delete real property + obj1.setProperty(foo, QScriptValue()); + QVERIFY(!obj1.property(foo).isValid()); + obj1.setProperty(foo, num); + QVERIFY(obj1.property(foo).equals(num)); + // remove script class; normal properties should remain obj1.setScriptClass(0); QCOMPARE(obj1.scriptClass(), (QScriptClass*)0); @@ -742,6 +786,80 @@ void tst_QScriptClass::getAndSetProperty() QVERIFY(!obj1.property(bar).isValid()); } +void tst_QScriptClass::getAndSetPropertyFromJS() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.addCustomProperty(eng.toStringHandle("x"), + QScriptClass::HandlesReadAccess + | QScriptClass::HandlesWriteAccess, + /*id=*/1, /*flags=*/0, /*value=*/123); + eng.globalObject().setProperty("o", eng.newObject(&cls)); + + // Accessing a custom property + QCOMPARE(eng.evaluate("o.x").toInt32(), 123); + QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 456); + + // Accessing a new JS property + QVERIFY(eng.evaluate("o.y").isUndefined()); + QCOMPARE(eng.evaluate("o.y = 789; o.y").toInt32(), 789); + + // Deleting custom property + QVERIFY(eng.evaluate("delete o.x").toBool()); + QVERIFY(eng.evaluate("o.x").isUndefined()); + + // Deleting JS property + QVERIFY(eng.evaluate("delete o.y").toBool()); + QVERIFY(eng.evaluate("o.y").isUndefined()); +} + +void tst_QScriptClass::deleteUndeletableProperty() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.addCustomProperty(eng.toStringHandle("x"), QScriptClass::HandlesWriteAccess, + /*id=*/0, QScriptValue::Undeletable, QScriptValue()); + eng.globalObject().setProperty("o", eng.newObject(&cls)); + QVERIFY(!eng.evaluate("delete o.x").toBool()); +} + +void tst_QScriptClass::writeReadOnlyProperty() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.addCustomProperty(eng.toStringHandle("x"), + QScriptClass::HandlesReadAccess + | QScriptClass::HandlesWriteAccess, + /*id=*/0, QScriptValue::ReadOnly, 123); + eng.globalObject().setProperty("o", eng.newObject(&cls)); + // Note that if a property is read-only, the setProperty() + // reimplementation will still get called; it's up to that + // function to respect the ReadOnly flag. + QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 123); +} + +void tst_QScriptClass::writePropertyWithoutWriteAccess() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.addCustomProperty(eng.toStringHandle("x"), + QScriptClass::HandlesReadAccess, + /*id=*/0, /*flags=*/0, 123); + eng.globalObject().setProperty("o", eng.newObject(&cls)); + QCOMPARE(eng.evaluate("o.x").toInt32(), 123); + + // This will create a JS property on the instance that + // shadows the custom property. + // This behavior is not documented. It might be more + // intuitive to treat a property that only handles read + // access as a read-only, non-shadowable property. + QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 456); + + QVERIFY(eng.evaluate("delete o.x").toBool()); + // Now the custom property is seen again. + QCOMPARE(eng.evaluate("o.x").toInt32(), 123); +} + void tst_QScriptClass::getProperty_invalidValue() { QScriptEngine eng; @@ -791,10 +909,12 @@ void tst_QScriptClass::enumerate() cls.setIterationEnabled(true); QScriptValueIterator it(obj); + // This test relies on the order in which properties are enumerated, + // which we don't guarantee. However, for compatibility's sake we prefer + // that normal JS properties come before QScriptClass properties. for (int x = 0; x < 2; ++x) { QVERIFY(it.hasNext()); it.next(); - QEXPECT_FAIL("", "", Abort); QVERIFY(it.scriptName() == foo); QVERIFY(it.hasNext()); it.next(); @@ -813,230 +933,571 @@ void tst_QScriptClass::enumerate() } } -void tst_QScriptClass::extension() +void tst_QScriptClass::extension_None() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.setCallableMode(TestClass::NotCallable); + QVERIFY(!cls.supportsExtension(QScriptClass::Callable)); + QVERIFY(!cls.supportsExtension(QScriptClass::HasInstance)); + QScriptValue obj = eng.newObject(&cls); + QVERIFY(!obj.call().isValid()); + QCOMPARE((int)cls.lastExtensionType(), -1); + QVERIFY(!obj.instanceOf(obj)); + QCOMPARE((int)cls.lastExtensionType(), -1); + QVERIFY(!obj.construct().isValid()); +} + +void tst_QScriptClass::extension_Callable() { QScriptEngine eng; + TestClass cls(&eng); + cls.setCallableMode(TestClass::CallableReturnsSum); + QVERIFY(cls.supportsExtension(QScriptClass::Callable)); + + QScriptValue obj = eng.newObject(&cls); + eng.globalObject().setProperty("obj", obj); + obj.setProperty("one", QScriptValue(&eng, 1)); + obj.setProperty("two", QScriptValue(&eng, 2)); + obj.setProperty("three", QScriptValue(&eng, 3)); + // From C++ + cls.clearReceivedArgs(); { - TestClass cls(&eng); - cls.setCallableMode(TestClass::NotCallable); - QVERIFY(!cls.supportsExtension(QScriptClass::Callable)); - QVERIFY(!cls.supportsExtension(QScriptClass::HasInstance)); - QScriptValue obj = eng.newObject(&cls); - QVERIFY(!obj.call().isValid()); - QCOMPARE((int)cls.lastExtensionType(), -1); - QVERIFY(!obj.instanceOf(obj)); - QCOMPARE((int)cls.lastExtensionType(), -1); + QScriptValueList args; + args << QScriptValue(&eng, 4) << QScriptValue(&eng, 5); + QScriptValue ret = obj.call(obj, args); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qsreal(1+2+3+4+5)); } - // Callable + // From JS + cls.clearReceivedArgs(); { - TestClass cls(&eng); - cls.setCallableMode(TestClass::CallableReturnsSum); - QVERIFY(cls.supportsExtension(QScriptClass::Callable)); - - QScriptValue obj = eng.newObject(&cls); - eng.globalObject().setProperty("obj", obj); - obj.setProperty("one", QScriptValue(&eng, 1)); - obj.setProperty("two", QScriptValue(&eng, 2)); - obj.setProperty("three", QScriptValue(&eng, 3)); - // From C++ - cls.clearReceivedArgs(); - { - QScriptValueList args; - args << QScriptValue(&eng, 4) << QScriptValue(&eng, 5); - QScriptValue ret = obj.call(obj, args); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toNumber(), qsreal(15)); - } - // From JS - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("obj(4, 5)"); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toNumber(), qsreal(15)); - } + QScriptValue ret = eng.evaluate("obj(4, 5)"); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qsreal(1+2+3+4+5)); + } - cls.setCallableMode(TestClass::CallableReturnsArgument); - // From C++ - cls.clearReceivedArgs(); - { - QScriptValue ret = obj.call(obj, QScriptValueList() << 123); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toInt32(), 123); - } - cls.clearReceivedArgs(); - { - QScriptValue ret = obj.call(obj, QScriptValueList() << true); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); - QVERIFY(ret.isBoolean()); - QCOMPARE(ret.toBoolean(), true); - } - { - QScriptValue ret = obj.call(obj, QScriptValueList() << QString::fromLatin1("ciao")); - QVERIFY(ret.isString()); - QCOMPARE(ret.toString(), QString::fromLatin1("ciao")); - } - { - QScriptValue objobj = eng.newObject(); - QScriptValue ret = obj.call(obj, QScriptValueList() << objobj); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(objobj)); - } - { - QScriptValue ret = obj.call(obj, QScriptValueList() << QScriptValue()); - QVERIFY(ret.isUndefined()); - } - // From JS - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("obj(123)"); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toInt32(), 123); - } + cls.setCallableMode(TestClass::CallableReturnsArgument); + // From C++ + cls.clearReceivedArgs(); + { + QScriptValue ret = obj.call(obj, QScriptValueList() << 123); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + cls.clearReceivedArgs(); + { + QScriptValue ret = obj.call(obj, QScriptValueList() << true); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); + QVERIFY(ret.isBoolean()); + QCOMPARE(ret.toBoolean(), true); + } + { + QScriptValue ret = obj.call(obj, QScriptValueList() << QString::fromLatin1("ciao")); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("ciao")); + } + { + QScriptValue objobj = eng.newObject(); + QScriptValue ret = obj.call(obj, QScriptValueList() << objobj); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(objobj)); + } + { + QScriptValue ret = obj.call(obj, QScriptValueList() << QScriptValue()); + QVERIFY(ret.isUndefined()); + } + // From JS + cls.clearReceivedArgs(); + { + QScriptValue ret = eng.evaluate("obj(123)"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } - cls.setCallableMode(TestClass::CallableReturnsInvalidVariant); - { - QScriptValue ret = obj.call(obj); - QVERIFY(ret.isUndefined()); - } + cls.setCallableMode(TestClass::CallableReturnsInvalidVariant); + { + QScriptValue ret = obj.call(obj); + QVERIFY(ret.isUndefined()); + } - cls.setCallableMode(TestClass::CallableReturnsThisObject); - // From C++ - { - QScriptValue ret = obj.call(obj); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(obj)); - } - // From JS - { - QScriptValue ret = eng.evaluate("obj()"); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(eng.globalObject())); - } + cls.setCallableMode(TestClass::CallableReturnsThisObject); + // From C++ + { + QScriptValue ret = obj.call(obj); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(obj)); + } + // From JS + { + QScriptValue ret = eng.evaluate("obj()"); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(eng.globalObject())); + } - cls.setCallableMode(TestClass::CallableReturnsCallee); - // From C++ - { - QScriptValue ret = obj.call(); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(obj)); - } - // From JS - { - QScriptValue ret = eng.evaluate("obj()"); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(obj)); - } + cls.setCallableMode(TestClass::CallableReturnsCallee); + // From C++ + { + QScriptValue ret = obj.call(); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(obj)); + } + // From JS + { + QScriptValue ret = eng.evaluate("obj()"); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(obj)); + } - cls.setCallableMode(TestClass::CallableReturnsArgumentsObject); - // From C++ - { - QScriptValue ret = obj.call(obj, QScriptValueList() << 123); - QVERIFY(ret.isObject()); - QVERIFY(ret.property("length").isNumber()); - QCOMPARE(ret.property("length").toInt32(), 1); - QVERIFY(ret.property(0).isNumber()); - QCOMPARE(ret.property(0).toInt32(), 123); - } - // From JS - { - QScriptValue ret = eng.evaluate("obj(123)"); - QVERIFY(ret.isObject()); - QVERIFY(ret.property("length").isNumber()); - QCOMPARE(ret.property("length").toInt32(), 1); - QVERIFY(ret.property(0).isNumber()); - QCOMPARE(ret.property(0).toInt32(), 123); - } + cls.setCallableMode(TestClass::CallableReturnsArgumentsObject); + // From C++ + { + QScriptValue ret = obj.call(obj, QScriptValueList() << 123); + QVERIFY(ret.isObject()); + QVERIFY(ret.property("length").isNumber()); + QCOMPARE(ret.property("length").toInt32(), 1); + QVERIFY(ret.property(0).isNumber()); + QCOMPARE(ret.property(0).toInt32(), 123); + } + // From JS + { + QScriptValue ret = eng.evaluate("obj(123)"); + QVERIFY(ret.isObject()); + QVERIFY(ret.property("length").isNumber()); + QCOMPARE(ret.property("length").toInt32(), 1); + QVERIFY(ret.property(0).isNumber()); + QCOMPARE(ret.property(0).toInt32(), 123); + } +} - // construct() - // From C++ - cls.clearReceivedArgs(); - cls.setCallableMode(TestClass::CallableReturnsGlobalObject); - { - QScriptValue ret = obj.construct(); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(eng.globalObject())); - } - // From JS - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("new obj()"); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(eng.globalObject())); - } - // From C++ - cls.clearReceivedArgs(); - cls.setCallableMode(TestClass::CallableInitializesThisObject); - { - QScriptValue ret = obj.construct(); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); - QVERIFY(ret.isQObject()); - QCOMPARE(ret.toQObject(), (QObject*)&eng); - } - // From JS - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("new obj()"); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); - QVERIFY(ret.isQObject()); - QCOMPARE(ret.toQObject(), (QObject*)&eng); - } +void tst_QScriptClass::extension_Callable_construct() +{ + QScriptEngine eng; + TestClass cls(&eng); + QScriptValue obj = eng.newObject(&cls); + eng.globalObject().setProperty("obj", obj); + + // From C++ + cls.clearReceivedArgs(); + cls.setCallableMode(TestClass::CallableReturnsGlobalObject); + { + QScriptValue ret = obj.construct(); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(eng.globalObject())); } - // HasInstance + // From JS + cls.clearReceivedArgs(); { - TestClass cls(&eng); - cls.setHasInstance(true); - QVERIFY(cls.supportsExtension(QScriptClass::HasInstance)); + QScriptValue ret = eng.evaluate("new obj()"); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(eng.globalObject())); + } + // From C++ + cls.clearReceivedArgs(); + cls.setCallableMode(TestClass::CallableInitializesThisObject); + { + QScriptValue ret = obj.construct(); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); + QVERIFY(ret.isQObject()); + QCOMPARE(ret.toQObject(), (QObject*)&eng); + } + // From JS + cls.clearReceivedArgs(); + { + QScriptValue ret = eng.evaluate("new obj()"); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptContext*>()); + QVERIFY(ret.isQObject()); + QCOMPARE(ret.toQObject(), (QObject*)&eng); + } +} - QScriptValue obj = eng.newObject(&cls); - obj.setProperty("foo", QScriptValue(&eng, 123)); - QScriptValue plain = eng.newObject(); - QVERIFY(!plain.instanceOf(obj)); +void tst_QScriptClass::extension_HasInstance() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.setHasInstance(true); + QVERIFY(cls.supportsExtension(QScriptClass::HasInstance)); - eng.globalObject().setProperty("HasInstanceTester", obj); - eng.globalObject().setProperty("hasInstanceValue", plain); - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); - QCOMPARE(cls.lastExtensionType(), QScriptClass::HasInstance); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptValueList>()); - QScriptValueList lst = qvariant_cast<QScriptValueList>(cls.lastExtensionArgument()); - QCOMPARE(lst.size(), 2); - QVERIFY(lst.at(0).strictlyEquals(obj)); - QVERIFY(lst.at(1).strictlyEquals(plain)); - QVERIFY(ret.isBoolean()); - QVERIFY(!ret.toBoolean()); - } + QScriptValue obj = eng.newObject(&cls); + obj.setProperty("foo", QScriptValue(&eng, 123)); + QScriptValue plain = eng.newObject(); + QVERIFY(!plain.instanceOf(obj)); - plain.setProperty("foo", QScriptValue(&eng, 456)); - QVERIFY(!plain.instanceOf(obj)); - { - QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); - QVERIFY(ret.isBoolean()); - QVERIFY(!ret.toBoolean()); - } + eng.globalObject().setProperty("HasInstanceTester", obj); + eng.globalObject().setProperty("hasInstanceValue", plain); + cls.clearReceivedArgs(); + { + QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); + QCOMPARE(cls.lastExtensionType(), QScriptClass::HasInstance); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId<QScriptValueList>()); + QScriptValueList lst = qvariant_cast<QScriptValueList>(cls.lastExtensionArgument()); + QCOMPARE(lst.size(), 2); + QVERIFY(lst.at(0).strictlyEquals(obj)); + QVERIFY(lst.at(1).strictlyEquals(plain)); + QVERIFY(ret.isBoolean()); + QVERIFY(!ret.toBoolean()); + } - plain.setProperty("foo", obj.property("foo")); - QVERIFY(plain.instanceOf(obj)); - { - QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); - QVERIFY(ret.isBoolean()); - QVERIFY(ret.toBoolean()); - } + plain.setProperty("foo", QScriptValue(&eng, 456)); + QVERIFY(!plain.instanceOf(obj)); + { + QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); + QVERIFY(ret.isBoolean()); + QVERIFY(!ret.toBoolean()); + } + + plain.setProperty("foo", obj.property("foo")); + QVERIFY(plain.instanceOf(obj)); + { + QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); + QVERIFY(ret.isBoolean()); + QVERIFY(ret.toBoolean()); + } +} + +// tests made to match Qt 4.7 (JSC) behaviour +void tst_QScriptClass::originalProperties1() +{ + QScriptEngine eng; + + QScriptString orig1 = eng.toStringHandle("orig1"); + QScriptString orig2 = eng.toStringHandle("orig2"); + QScriptString orig3 = eng.toStringHandle("orig3"); + QScriptString new1 = eng.toStringHandle("new1"); + QScriptString new2 = eng.toStringHandle("new2"); + + { + TestClass cls1(&eng); + cls1.addCustomProperty(orig2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, 89); + cls1.addCustomProperty(new1, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, "hello"); + + TestClass cls2(&eng); + cls2.addCustomProperty(orig2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, 59); + cls2.addCustomProperty(new2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, "world"); + + QScriptValue obj1 = eng.newObject(); + obj1.setProperty(orig1 , 42); + obj1.setProperty(orig2 , "foo"); + obj1.prototype().setProperty(orig3, "bar"); + + QCOMPARE(obj1.property(orig1).toInt32(), 42); + QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar")); + QVERIFY(!obj1.property(new1).isValid()); + QVERIFY(!obj1.property(new2).isValid()); + + eng.globalObject().setProperty("obj" , obj1); + + obj1.setScriptClass(&cls1); + QCOMPARE(obj1.property(orig1).toInt32(), 42); + QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar")); + QCOMPARE(obj1.property(new1).toString(), QString::fromLatin1("hello")); + QVERIFY(!obj1.property(new2).isValid()); + + QScriptValue obj2 = eng.evaluate("obj"); + QCOMPARE(obj2.scriptClass(), &cls1); + QCOMPARE(obj2.property(orig1).toInt32(), 42); + QCOMPARE(obj2.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj2.property(orig3).toString(), QString::fromLatin1("bar")); + QCOMPARE(obj2.property(new1).toString(), QString::fromLatin1("hello")); + QVERIFY(!obj2.property(new2).isValid()); + + obj1.setScriptClass(&cls2); + QCOMPARE(obj1.property(orig1).toInt32(), 42); + QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar")); + QVERIFY(!obj1.property(new1).isValid()); + QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("world")); + + QCOMPARE(obj2.scriptClass(), &cls2); + QCOMPARE(obj2.property(orig1).toInt32(), 42); + QCOMPARE(obj2.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj2.property(orig3).toString(), QString::fromLatin1("bar")); + QVERIFY(!obj2.property(new1).isValid()); + QCOMPARE(obj2.property(new2).toString(), QString::fromLatin1("world")); + + obj1.setScriptClass(0); + QCOMPARE(obj1.property(orig1).toInt32(), 42); + QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar")); + QVERIFY(!obj1.property(new1).isValid()); + QVERIFY(!obj1.property(new2).isValid()); + } +} + +void tst_QScriptClass::originalProperties2() +{ + QScriptEngine eng; + + QScriptString orig1 = eng.toStringHandle("orig1"); + QScriptString orig2 = eng.toStringHandle("orig2"); + QScriptString orig3 = eng.toStringHandle("orig3"); + QScriptString new1 = eng.toStringHandle("new1"); + QScriptString new2 = eng.toStringHandle("new2"); + + { + TestClass cls1(&eng); + cls1.addCustomProperty(orig2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, 89); + cls1.addCustomProperty(new1, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, "hello"); + + TestClass cls2(&eng); + cls2.addCustomProperty(orig2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, 59); + cls2.addCustomProperty(new2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, "world"); + + QScriptValue obj1 = eng.newObject(); + obj1.setProperty(orig1 , 42); + obj1.setProperty(orig2 , "foo"); + obj1.prototype().setProperty(orig3, "bar"); + + QCOMPARE(obj1.property(orig1).toInt32(), 42); + QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("bar")); + QVERIFY(!obj1.property(new1).isValid()); + QVERIFY(!obj1.property(new2).isValid()); + + obj1.setScriptClass(&cls1); + obj1.setProperty(orig1 , QScriptValue(&eng, 852)); + obj1.setProperty(orig2 , "oli"); + obj1.setProperty(orig3 , "fu*c"); + obj1.setProperty(new1 , "moo"); + obj1.setProperty(new2 , "allo?"); + QCOMPARE(obj1.property(orig1).toInt32(), 852); + QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("fu*c")); + QCOMPARE(obj1.property(new1).toString(), QString::fromLatin1("moo")); + QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("allo?")); + + obj1.setScriptClass(&cls2); + QCOMPARE(obj1.property(orig1).toInt32(), 852); + QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("fu*c")); + QVERIFY(!obj1.property(new1).isValid()); + QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("allo?")); + + obj1.setScriptClass(0); + QCOMPARE(obj1.property(orig1).toInt32(), 852); + QCOMPARE(obj1.property(orig2).toString(), QString::fromLatin1("foo")); + QCOMPARE(obj1.property(orig3).toString(), QString::fromLatin1("fu*c")); + QVERIFY(!obj1.property(new1).isValid()); + QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("allo?")); + } +} + +void tst_QScriptClass::originalProperties3() +{ + QScriptEngine eng; + + QScriptString orig1 = eng.toStringHandle("orig1"); + QScriptString orig2 = eng.toStringHandle("orig2"); + QScriptString orig3 = eng.toStringHandle("orig3"); + QScriptString new1 = eng.toStringHandle("new1"); + QScriptString new2 = eng.toStringHandle("new2"); + + { + TestClass cls1(&eng); + cls1.addCustomProperty(orig2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, 89); + cls1.addCustomProperty(new1, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, "hello"); + + TestClass cls2(&eng); + cls2.addCustomProperty(orig2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, 59); + cls2.addCustomProperty(new2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, "world"); + + QScriptValue obj1 = eng.newObject(&cls1); + QVERIFY(!obj1.property(orig1).isValid()); + QCOMPARE(obj1.property(orig2).toInt32(), 89); + QCOMPARE(obj1.property(new1).toString(), QString::fromLatin1("hello")); + QVERIFY(!obj1.property(new2).isValid()); + obj1.setProperty(orig1, 42); + QCOMPARE(obj1.property(orig1).toInt32(), 42); + + eng.globalObject().setProperty("obj" , obj1); + obj1.setScriptClass(&cls2); + QCOMPARE(obj1.property(orig1).toInt32(), 42); + QCOMPARE(obj1.property(orig2).toInt32(), 59); + QVERIFY(!obj1.property(new1).isValid()); + QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("world")); + + QScriptValue obj2 = eng.evaluate("obj"); + QCOMPARE(obj2.scriptClass(), &cls2); + QCOMPARE(obj2.property(orig1).toInt32(), 42); + QCOMPARE(obj2.property(orig2).toInt32(), 59); + QVERIFY(!obj2.property(new1).isValid()); + QCOMPARE(obj2.property(new2).toString(), QString::fromLatin1("world")); + + obj1.setScriptClass(0); + QCOMPARE(obj1.property(orig1).toInt32(), 42); + QVERIFY(!obj1.property(orig2).isValid()); + QVERIFY(!obj1.property(new1).isValid()); + QVERIFY(!obj1.property(new2).isValid()); + + QCOMPARE(obj2.scriptClass(), (QScriptClass *)0); + QCOMPARE(obj2.property(orig1).toInt32(), 42); + QVERIFY(!obj2.property(orig2).isValid()); + QVERIFY(!obj2.property(new1).isValid()); + QVERIFY(!obj2.property(new2).isValid()); } } +void tst_QScriptClass::originalProperties4() +{ + QScriptEngine eng; + + QScriptString orig1 = eng.toStringHandle("orig1"); + QScriptString orig2 = eng.toStringHandle("orig2"); + QScriptString orig3 = eng.toStringHandle("orig3"); + QScriptString new1 = eng.toStringHandle("new1"); + QScriptString new2 = eng.toStringHandle("new2"); + + { + TestClass cls1(&eng); + cls1.addCustomProperty(orig2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, 89); + cls1.addCustomProperty(new1, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, "hello"); + + TestClass cls2(&eng); + cls2.addCustomProperty(orig2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, 59); + cls2.addCustomProperty(new2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, 1, 0, "world"); + + QScriptValue obj1 = eng.newObject(&cls1); + QVERIFY(!obj1.property(orig1).isValid()); + QCOMPARE(obj1.property(orig2).toInt32(), 89); + QCOMPARE(obj1.property(new1).toString(), QString::fromLatin1("hello")); + QVERIFY(!obj1.property(new2).isValid()); + + eng.globalObject().setProperty("obj" , obj1); + + obj1.setScriptClass(0); + QVERIFY(obj1.isObject()); + QVERIFY(!obj1.property(orig1).isValid()); + QVERIFY(!obj1.property(orig2).isValid()); + QVERIFY(!obj1.property(new1).isValid()); + QVERIFY(!obj1.property(new2).isValid()); + obj1.setProperty(orig1, 42); + QCOMPARE(obj1.property(orig1).toInt32(), 42); + + QScriptValue obj2 = eng.evaluate("obj"); + QCOMPARE(obj2.scriptClass(), (QScriptClass *)0); + QVERIFY(obj2.isObject()); + QCOMPARE(obj2.property(orig1).toInt32(), 42); + QVERIFY(!obj2.property(orig2).isValid()); + QVERIFY(!obj2.property(new1).isValid()); + QVERIFY(!obj2.property(new2).isValid()); + + obj1.setScriptClass(&cls2); + QCOMPARE(obj1.property(orig1).toInt32(), 42); + QCOMPARE(obj1.property(orig2).toInt32(), 59); + QVERIFY(!obj1.property(new1).isValid()); + QCOMPARE(obj1.property(new2).toString(), QString::fromLatin1("world")); + + QCOMPARE(obj2.scriptClass(), (QScriptClass *)(&cls2)); + QCOMPARE(obj2.property(orig1).toInt32(), 42); + QCOMPARE(obj2.property(orig2).toInt32(), 59); + QVERIFY(!obj2.property(new1).isValid()); + QCOMPARE(obj2.property(new2).toString(), QString::fromLatin1("world")); + } +} + +void tst_QScriptClass::defaultImplementations() +{ + QScriptEngine eng; + + QScriptClass defaultClass(&eng); + QCOMPARE(defaultClass.engine(), &eng); + QVERIFY(!defaultClass.prototype().isValid()); + QCOMPARE(defaultClass.name(), QString()); + + QScriptValue obj = eng.newObject(&defaultClass); + QCOMPARE(obj.scriptClass(), &defaultClass); + + QScriptString name = eng.toStringHandle("foo"); + uint id = -1; + QCOMPARE(defaultClass.queryProperty(obj, name, QScriptClass::HandlesReadAccess, &id), QScriptClass::QueryFlags(0)); + QVERIFY(!defaultClass.property(obj, name, id).isValid()); + QCOMPARE(defaultClass.propertyFlags(obj, name, id), QScriptValue::PropertyFlags(0)); + defaultClass.setProperty(obj, name, id, 123); + QVERIFY(!obj.property(name).isValid()); + + QCOMPARE(defaultClass.newIterator(obj), (QScriptClassPropertyIterator*)0); + + QVERIFY(!defaultClass.supportsExtension(QScriptClass::Callable)); + QVERIFY(!defaultClass.supportsExtension(QScriptClass::HasInstance)); + QVERIFY(!defaultClass.extension(QScriptClass::Callable).isValid()); + QVERIFY(!defaultClass.extension(QScriptClass::HasInstance).isValid()); +} + +void tst_QScriptClass::scriptClassObjectInPrototype() +{ + QScriptEngine eng; + TestClass cls(&eng); + QScriptValue plainObject = eng.newObject(); + QScriptValue classObject = eng.newObject(&cls); + plainObject.setPrototype(classObject); + QVERIFY(plainObject.prototype().equals(classObject)); + eng.globalObject().setProperty("plainObject", plainObject); + eng.globalObject().setProperty("classObject", classObject); + + QScriptString name = eng.toStringHandle("x"); + cls.addCustomProperty(name, QScriptClass::HandlesReadAccess, /*id=*/1, /*flags=*/0, /*value=*/123); + QVERIFY(plainObject.property(name).equals(classObject.property(name))); + QVERIFY(eng.evaluate("plainObject.x == classObject.x").toBool()); + + // Add a property that shadows the one in the script class. + plainObject.setProperty(name, 456); + QVERIFY(!plainObject.property(name).equals(classObject.property(name))); + QVERIFY(eng.evaluate("plainObject.x != classObject.x").toBool()); + + QVERIFY(eng.evaluate("delete plainObject.x").toBool()); + QVERIFY(eng.evaluate("plainObject.x == classObject.x").toBool()); +} + +void tst_QScriptClass::scriptClassWithNullEngine() +{ + QScriptClass cls(0); + QCOMPARE(cls.engine(), (QScriptEngine*)0); + QScriptEngine eng; + QScriptValue obj = eng.newObject(&cls); + QVERIFY(obj.isObject()); + QCOMPARE(obj.scriptClass(), &cls); + // The class could have been "bound" to the engine at this point, + // but it's currently not. + // This behavior is not documented and is subject to change. + QCOMPARE(cls.engine(), (QScriptEngine*)0); + // The engine pointer stored in the QScriptClass is not actually used + // during property access, so this still works. + obj.setProperty("x", 123); + QVERIFY(obj.property("x").isNumber()); +} + +void tst_QScriptClass::scriptClassInOtherEngine() +{ + QScriptEngine eng; + TestClass cls(&eng); + QScriptEngine eng2; + // We don't check that the class is associated with another engine, so + // we only get a warning when trying to set the prototype of the new + // instance. + // This behavior is not documented and is subject to change. + QTest::ignoreMessage(QtWarningMsg, "QScriptValue::setPrototype() failed: cannot set a prototype created in a different engine"); + QScriptValue obj = eng2.newObject(&cls); + QVERIFY(obj.isObject()); + QCOMPARE(obj.scriptClass(), &cls); + + obj.setProperty("x", 123); + QVERIFY(obj.property("x").isNumber()); +} + QTEST_MAIN(tst_QScriptClass) #include "tst_qscriptclass.moc" |