diff -r 000000000000 -r 1918ee327afb tests/auto/qscriptclass/tst_qscriptclass.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/auto/qscriptclass/tst_qscriptclass.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,906 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QScriptContext*) +Q_DECLARE_METATYPE(QScriptValueList) +Q_DECLARE_METATYPE(QScriptValue) + +//TESTED_CLASS= +//TESTED_FILES=script/qscriptclass.h script/qscriptclass.cpp script/qscriptclasspropertyiterator.h script/qscriptclasspropertyiterator.cpp + +class tst_QScriptClass : public QObject +{ + Q_OBJECT + +public: + tst_QScriptClass(); + virtual ~tst_QScriptClass(); + +private slots: + void newInstance(); + void getAndSetProperty(); + void enumerate(); + void extension(); +}; + +tst_QScriptClass::tst_QScriptClass() +{ +} + +tst_QScriptClass::~tst_QScriptClass() +{ +} + + + +class TestClass : public QScriptClass +{ +public: + struct CustomProperty { + QueryFlags qflags; + uint id; + QScriptValue::PropertyFlags pflags; + QScriptValue value; + + CustomProperty(QueryFlags qf, uint i, QScriptValue::PropertyFlags pf, + const QScriptValue &val) + : qflags(qf), id(i), pflags(pf), value(val) { } + }; + + enum CallableMode { + NotCallable, + CallableReturnsSum, + CallableReturnsArgument, + CallableReturnsInvalidVariant + }; + + TestClass(QScriptEngine *engine); + ~TestClass(); + + void addCustomProperty(const QScriptString &name, QueryFlags qflags, + uint id, QScriptValue::PropertyFlags pflags, + const QScriptValue &value); + void removeCustomProperty(const QScriptString &name); + + QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); + + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + + void setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value); + + QScriptValue::PropertyFlags propertyFlags( + const QScriptValue &object, const QScriptString &name, uint id); + + QScriptClassPropertyIterator *newIterator(const QScriptValue &object); + + QScriptValue prototype() const; + + QString name() const; + + bool supportsExtension(Extension extension) const; + QVariant extension(Extension extension, + const QVariant &argument = QVariant()); + + QScriptValue lastQueryPropertyObject() const; + QScriptString lastQueryPropertyName() const; + QueryFlags lastQueryPropertyFlags() const; + + QScriptValue lastPropertyObject() const; + QScriptString lastPropertyName() const; + uint lastPropertyId() const; + + QScriptValue lastSetPropertyObject() const; + QScriptString lastSetPropertyName() const; + uint lastSetPropertyId() const; + QScriptValue lastSetPropertyValue() const; + + QScriptValue lastPropertyFlagsObject() const; + QScriptString lastPropertyFlagsName() const; + uint lastPropertyFlagsId() const; + + QScriptClass::Extension lastExtensionType() const; + QVariant lastExtensionArgument() const; + + void clearReceivedArgs(); + + void setIterationEnabled(bool enable); + bool isIterationEnabled() const; + + void setCallableMode(CallableMode mode); + CallableMode callableMode() const; + + void setHasInstance(bool hasInstance); + bool hasInstance() const; + +private: + CustomProperty *findCustomProperty(const QScriptString &name); + + QHash customProperties; + + QScriptValue m_lastQueryPropertyObject; + QScriptString m_lastQueryPropertyName; + QScriptClass::QueryFlags m_lastQueryPropertyFlags; + + QScriptValue m_lastPropertyObject; + QScriptString m_lastPropertyName; + uint m_lastPropertyId; + + QScriptValue m_lastSetPropertyObject; + QScriptString m_lastSetPropertyName; + uint m_lastSetPropertyId; + QScriptValue m_lastSetPropertyValue; + + QScriptValue m_lastPropertyFlagsObject; + QScriptString m_lastPropertyFlagsName; + uint m_lastPropertyFlagsId; + + QScriptClass::Extension m_lastExtensionType; + QVariant m_lastExtensionArgument; + + QScriptValue m_prototype; + bool m_iterationEnabled; + CallableMode m_callableMode; + bool m_hasInstance; +}; + +class TestClassPropertyIterator : public QScriptClassPropertyIterator +{ +public: + TestClassPropertyIterator(const QHash &props, + const QScriptValue &object); + ~TestClassPropertyIterator(); + + bool hasNext() const; + void next(); + + bool hasPrevious() const; + void previous(); + + void toFront(); + void toBack(); + + QScriptString name() const; + uint id() const; + QScriptValue::PropertyFlags flags() const; + +private: + int m_index; + int m_last; + QHash m_props; +}; + + + +TestClass::TestClass(QScriptEngine *engine) + : QScriptClass(engine), m_iterationEnabled(true), + m_callableMode(NotCallable), m_hasInstance(false) +{ + m_prototype = engine->newObject(); + clearReceivedArgs(); +} + +TestClass::~TestClass() +{ + qDeleteAll(customProperties); +} + +TestClass::CustomProperty* TestClass::findCustomProperty(const QScriptString &name) +{ + QHash::const_iterator it; + it = customProperties.constFind(name); + if (it == customProperties.constEnd()) + return 0; + return it.value(); + +} + +void TestClass::addCustomProperty(const QScriptString &name, QueryFlags qflags, + uint id, QScriptValue::PropertyFlags pflags, + const QScriptValue &value) +{ + customProperties.insert(name, new CustomProperty(qflags, id, pflags, value)); +} + +void TestClass::removeCustomProperty(const QScriptString &name) +{ + CustomProperty *prop = customProperties.take(name); + if (prop) + delete prop; +} + +QScriptClass::QueryFlags TestClass::queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id) +{ + m_lastQueryPropertyObject = object; + m_lastQueryPropertyName = name; + m_lastQueryPropertyFlags = flags; + CustomProperty *prop = findCustomProperty(name); + if (!prop) + return 0; + *id = prop->id; + return prop->qflags & flags; +} + +QScriptValue TestClass::property(const QScriptValue &object, + const QScriptString &name, uint id) +{ + m_lastPropertyObject = object; + m_lastPropertyName = name; + m_lastPropertyId = id; + CustomProperty *prop = findCustomProperty(name); + if (!prop) + return QScriptValue(); + return prop->value; +} + +void TestClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) +{ + m_lastSetPropertyObject = object; + m_lastSetPropertyName = name; + m_lastSetPropertyId = id; + m_lastSetPropertyValue = value; + CustomProperty *prop = findCustomProperty(name); + if (!prop) + return; + prop->value = value; +} + +QScriptValue::PropertyFlags TestClass::propertyFlags( + const QScriptValue &object, const QScriptString &name, uint id) +{ + m_lastPropertyFlagsObject = object; + m_lastPropertyFlagsName = name; + m_lastPropertyFlagsId = id; + CustomProperty *prop = findCustomProperty(name); + if (!prop) + return 0; + return prop->pflags; +} + +QScriptClassPropertyIterator *TestClass::newIterator(const QScriptValue &object) +{ + if (!m_iterationEnabled) + return 0; + return new TestClassPropertyIterator(customProperties, object); +} + +QScriptValue TestClass::prototype() const +{ + return m_prototype; +} + +QString TestClass::name() const +{ + return QLatin1String("TestClass"); +} + +bool TestClass::supportsExtension(Extension extension) const +{ + if (extension == Callable) + return (m_callableMode != NotCallable); + if (extension == HasInstance) + return m_hasInstance; + return false; +} + +QVariant TestClass::extension(Extension extension, + const QVariant &argument) +{ + m_lastExtensionType = extension; + m_lastExtensionArgument = argument; + if (extension == Callable) { + Q_ASSERT(m_callableMode != NotCallable); + QScriptContext *ctx = qvariant_cast(argument); + if (m_callableMode == CallableReturnsSum) { + qsreal sum = 0; + for (int i = 0; i < ctx->argumentCount(); ++i) + sum += ctx->argument(i).toNumber(); + QScriptValueIterator it(ctx->thisObject()); + while (it.hasNext()) { + it.next(); + sum += it.value().toNumber(); + } + return sum; + } else if (m_callableMode == CallableReturnsArgument) { + return qVariantFromValue(ctx->argument(0)); + } else if (m_callableMode == CallableReturnsInvalidVariant) { + return QVariant(); + } + } else if (extension == HasInstance) { + Q_ASSERT(m_hasInstance); + QScriptValueList args = qvariant_cast(argument); + QScriptValue obj = args.at(0); + QScriptValue value = args.at(1); + return value.property("foo").equals(obj.property("foo")); + } + return QVariant(); +} + +QScriptValue TestClass::lastQueryPropertyObject() const +{ + return m_lastQueryPropertyObject; +} + +QScriptString TestClass::lastQueryPropertyName() const +{ + return m_lastQueryPropertyName; +} + +QScriptClass::QueryFlags TestClass::lastQueryPropertyFlags() const +{ + return m_lastQueryPropertyFlags; +} + +QScriptValue TestClass::lastPropertyObject() const +{ + return m_lastPropertyObject; +} + +QScriptString TestClass::lastPropertyName() const +{ + return m_lastPropertyName; +} + +uint TestClass::lastPropertyId() const +{ + return m_lastPropertyId; +} + +QScriptValue TestClass::lastSetPropertyObject() const +{ + return m_lastSetPropertyObject; +} + +QScriptString TestClass::lastSetPropertyName() const +{ + return m_lastSetPropertyName; +} + +uint TestClass::lastSetPropertyId() const +{ + return m_lastSetPropertyId; +} + +QScriptValue TestClass::lastSetPropertyValue() const +{ + return m_lastSetPropertyValue; +} + +QScriptValue TestClass::lastPropertyFlagsObject() const +{ + return m_lastPropertyFlagsObject; +} + +QScriptString TestClass::lastPropertyFlagsName() const +{ + return m_lastPropertyFlagsName; +} + +uint TestClass::lastPropertyFlagsId() const +{ + return m_lastPropertyFlagsId; +} + +QScriptClass::Extension TestClass::lastExtensionType() const +{ + return m_lastExtensionType; +} + +QVariant TestClass::lastExtensionArgument() const +{ + return m_lastExtensionArgument; +} + +void TestClass::clearReceivedArgs() +{ + m_lastQueryPropertyObject = QScriptValue(); + m_lastQueryPropertyName = QScriptString(); + m_lastQueryPropertyFlags = 0; + + m_lastPropertyObject = QScriptValue(); + m_lastPropertyName = QScriptString(); + m_lastPropertyId = uint(-1); + + m_lastSetPropertyObject = QScriptValue(); + m_lastSetPropertyName = QScriptString(); + m_lastSetPropertyId = uint(-1); + m_lastSetPropertyValue = QScriptValue(); + + m_lastPropertyFlagsObject = QScriptValue(); + m_lastPropertyFlagsName = QScriptString(); + m_lastPropertyFlagsId = uint(-1); + + m_lastExtensionType = static_cast(-1); + m_lastExtensionArgument = QVariant(); +} + +void TestClass::setIterationEnabled(bool enable) +{ + m_iterationEnabled = enable; +} + +bool TestClass::isIterationEnabled() const +{ + return m_iterationEnabled; +} + +void TestClass::setCallableMode(CallableMode mode) +{ + m_callableMode = mode; +} + +TestClass::CallableMode TestClass::callableMode() const +{ + return m_callableMode; +} + +void TestClass::setHasInstance(bool hasInstance) +{ + m_hasInstance = hasInstance; +} + +bool TestClass::hasInstance() const +{ + return m_hasInstance; +} + + +TestClassPropertyIterator::TestClassPropertyIterator(const QHash &props, + const QScriptValue &object) + : QScriptClassPropertyIterator(object) +{ + m_props = props; + toFront(); +} + +TestClassPropertyIterator::~TestClassPropertyIterator() +{ +} + +bool TestClassPropertyIterator::hasNext() const +{ + return m_index < m_props.size(); +} + +void TestClassPropertyIterator::next() +{ + m_last = m_index; + ++m_index; +} + +bool TestClassPropertyIterator::hasPrevious() const +{ + return m_index > 0; +} + +void TestClassPropertyIterator::previous() +{ + --m_index; + m_last = m_index; +} + +void TestClassPropertyIterator::toFront() +{ + m_index = 0; + m_last = -1; +} + +void TestClassPropertyIterator::toBack() +{ + m_index = m_props.size(); + m_last = -1; +} + +QScriptString TestClassPropertyIterator::name() const +{ + return m_props.keys().value(m_last); +} + +uint TestClassPropertyIterator::id() const +{ + QScriptString key = m_props.keys().value(m_last); + if (!key.isValid()) + return 0; + TestClass::CustomProperty *prop = m_props.value(key); + return prop->id; +} + +QScriptValue::PropertyFlags TestClassPropertyIterator::flags() const +{ + QScriptString key = m_props.keys().value(m_last); + if (!key.isValid()) + return 0; + TestClass::CustomProperty *prop = m_props.value(key); + return prop->pflags; +} + + + +void tst_QScriptClass::newInstance() +{ + QScriptEngine eng; + + TestClass cls(&eng); + + QScriptValue obj1 = eng.newObject(&cls); + QVERIFY(!obj1.data().isValid()); + QVERIFY(obj1.prototype().strictlyEquals(cls.prototype())); + QEXPECT_FAIL("", "classname is not implemented", Continue); + QCOMPARE(obj1.toString(), QString::fromLatin1("[object TestClass]")); + QCOMPARE(obj1.scriptClass(), (QScriptClass*)&cls); + + QScriptValue num(&eng, 456); + QScriptValue obj2 = eng.newObject(&cls, num); + QVERIFY(obj2.data().strictlyEquals(num)); + QVERIFY(obj2.prototype().strictlyEquals(cls.prototype())); + QCOMPARE(obj2.scriptClass(), (QScriptClass*)&cls); + + QScriptValue obj3 = eng.newObject(); + QCOMPARE(obj3.scriptClass(), (QScriptClass*)0); + obj3.setScriptClass(&cls); + QCOMPARE(obj3.scriptClass(), (QScriptClass*)&cls); + + obj3.setScriptClass(0); + QCOMPARE(obj3.scriptClass(), (QScriptClass*)0); + obj3.setScriptClass(&cls); + QCOMPARE(obj3.scriptClass(), (QScriptClass*)&cls); + + TestClass cls2(&eng); + obj3.setScriptClass(&cls2); + QCOMPARE(obj3.scriptClass(), (QScriptClass*)&cls2); + + // undefined behavior really, but shouldn't crash + QScriptValue arr = eng.newArray(); + QVERIFY(arr.isArray()); + QCOMPARE(arr.scriptClass(), (QScriptClass*)0); + QTest::ignoreMessage(QtWarningMsg, "QScriptValue::setScriptClass() failed: cannot change class of non-QScriptObject"); + arr.setScriptClass(&cls); + QEXPECT_FAIL("", "Changing class of arbitrary script object is not allowed (it's OK)", Continue); + QCOMPARE(arr.scriptClass(), (QScriptClass*)&cls); + QEXPECT_FAIL("", "Changing class of arbitrary script object is not allowed (it's OK)", Continue); + QVERIFY(!arr.isArray()); + QVERIFY(arr.isObject()); +} + +void tst_QScriptClass::getAndSetProperty() +{ + QScriptEngine eng; + + TestClass cls(&eng); + + QScriptValue obj1 = eng.newObject(&cls); + QScriptValue obj2 = eng.newObject(&cls); + QScriptString foo = eng.toStringHandle("foo"); + QScriptString bar = eng.toStringHandle("bar"); + QScriptValue num(&eng, 123); + + // should behave just like normal + for (int x = 0; x < 2; ++x) { + QScriptValue &o = (x == 0) ? obj1 : obj2; + for (int y = 0; y < 2; ++y) { + QScriptString &s = (y == 0) ? foo : bar; + + // read property + cls.clearReceivedArgs(); + QScriptValue ret = o.property(s); + QVERIFY(!ret.isValid()); + QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(o)); + QVERIFY(cls.lastQueryPropertyName() == s); + QVERIFY(!cls.lastPropertyObject().isValid()); + QVERIFY(!cls.lastSetPropertyObject().isValid()); + QVERIFY(cls.lastQueryPropertyFlags() == QScriptClass::HandlesReadAccess); + + // write property + cls.clearReceivedArgs(); + o.setProperty(s, num); + QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(o)); + QVERIFY(cls.lastQueryPropertyName() == s); + QVERIFY(!cls.lastPropertyObject().isValid()); + QVERIFY(!cls.lastSetPropertyObject().isValid()); + QVERIFY(cls.lastQueryPropertyFlags() == QScriptClass::HandlesWriteAccess); + + // re-read property + // When a QScriptClass doesn't want to handle a property write, + // that property becomes a normal property and the QScriptClass + // shall not be queried about it again. + cls.clearReceivedArgs(); + QVERIFY(o.property(s).strictlyEquals(num)); + QVERIFY(!cls.lastQueryPropertyObject().isValid()); + } + } + + // add a custom property + QScriptString foo2 = eng.toStringHandle("foo2"); + const uint foo2Id = 123; + const QScriptValue::PropertyFlags foo2Pflags = QScriptValue::Undeletable; + QScriptValue foo2Value(&eng, 456); + cls.addCustomProperty(foo2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, + foo2Id, foo2Pflags, foo2Value); + + { + // read property + cls.clearReceivedArgs(); + { + QScriptValue ret = obj1.property(foo2); + QVERIFY(ret.strictlyEquals(foo2Value)); + } + QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1)); + QVERIFY(cls.lastQueryPropertyName() == foo2); + QVERIFY(cls.lastPropertyObject().strictlyEquals(obj1)); + QVERIFY(cls.lastPropertyName() == foo2); + QCOMPARE(cls.lastPropertyId(), foo2Id); + + // read flags + cls.clearReceivedArgs(); + QCOMPARE(obj1.propertyFlags(foo2), foo2Pflags); + QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1)); + QVERIFY(cls.lastQueryPropertyName() == foo2); + QVERIFY(!cls.lastPropertyObject().isValid()); + QVERIFY(cls.lastPropertyFlagsObject().strictlyEquals(obj1)); + QVERIFY(cls.lastPropertyFlagsName() == foo2); + QCOMPARE(cls.lastPropertyFlagsId(), foo2Id); + + // write property + cls.clearReceivedArgs(); + QScriptValue newFoo2Value(&eng, 789); + obj1.setProperty(foo2, newFoo2Value); + QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1)); + QVERIFY(cls.lastQueryPropertyName() == foo2); + + // read property again + cls.clearReceivedArgs(); + { + QScriptValue ret = obj1.property(foo2); + QVERIFY(ret.strictlyEquals(newFoo2Value)); + } + QVERIFY(cls.lastQueryPropertyObject().strictlyEquals(obj1)); + QVERIFY(cls.lastQueryPropertyName() == foo2); + QVERIFY(cls.lastPropertyObject().strictlyEquals(obj1)); + QVERIFY(cls.lastPropertyName() == foo2); + QCOMPARE(cls.lastPropertyId(), foo2Id); + } + + // remove script class; normal properties should remain + obj1.setScriptClass(0); + QCOMPARE(obj1.scriptClass(), (QScriptClass*)0); + QVERIFY(obj1.property(foo).equals(num)); + QVERIFY(obj1.property(bar).equals(num)); + obj1.setProperty(foo, QScriptValue()); + QVERIFY(!obj1.property(foo).isValid()); + obj1.setProperty(bar, QScriptValue()); + QVERIFY(!obj1.property(bar).isValid()); +} + +void tst_QScriptClass::enumerate() +{ + QScriptEngine eng; + + TestClass cls(&eng); + + QScriptValue obj = eng.newObject(&cls); + QScriptString foo = eng.toStringHandle("foo"); + obj.setProperty(foo, QScriptValue(&eng, 123)); + + cls.setIterationEnabled(false); + { + QScriptValueIterator it(obj); + QVERIFY(it.hasNext()); + it.next(); + QVERIFY(it.scriptName() == foo); + QVERIFY(!it.hasNext()); + } + + // add a custom property + QScriptString foo2 = eng.toStringHandle("foo2"); + const uint foo2Id = 123; + const QScriptValue::PropertyFlags foo2Pflags = QScriptValue::Undeletable; + QScriptValue foo2Value(&eng, 456); + cls.addCustomProperty(foo2, QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess, + foo2Id, foo2Pflags, QScriptValue()); + + cls.setIterationEnabled(true); + QScriptValueIterator it(obj); + for (int x = 0; x < 2; ++x) { + QVERIFY(it.hasNext()); + it.next(); + QEXPECT_FAIL("", "", Abort); + QVERIFY(it.scriptName() == foo); + QVERIFY(it.hasNext()); + it.next(); + QVERIFY(it.scriptName() == foo2); + QCOMPARE(it.flags(), foo2Pflags); + QVERIFY(!it.hasNext()); + + QVERIFY(it.hasPrevious()); + it.previous(); + QVERIFY(it.scriptName() == foo2); + QCOMPARE(it.flags(), foo2Pflags); + QVERIFY(it.hasPrevious()); + it.previous(); + QVERIFY(it.scriptName() == foo); + QVERIFY(!it.hasPrevious()); + } +} + +void tst_QScriptClass::extension() +{ + 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); + } + // Callable + { + TestClass cls(&eng); + cls.setCallableMode(TestClass::CallableReturnsSum); + QVERIFY(cls.supportsExtension(QScriptClass::Callable)); + + QScriptValue obj = eng.newObject(&cls); + obj.setProperty("one", QScriptValue(&eng, 1)); + obj.setProperty("two", QScriptValue(&eng, 2)); + obj.setProperty("three", QScriptValue(&eng, 3)); + 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()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qsreal(15)); + } + + cls.setCallableMode(TestClass::CallableReturnsArgument); + cls.clearReceivedArgs(); + { + QScriptValue ret = obj.call(obj, QScriptValueList() << 123); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + 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()); + 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()); + } + + cls.setCallableMode(TestClass::CallableReturnsInvalidVariant); + { + QScriptValue ret = obj.call(obj); + QVERIFY(ret.isUndefined()); + } + + // construct() + cls.clearReceivedArgs(); + { + QScriptValue ret = obj.construct(); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isObject()); + } + } + // HasInstance + { + TestClass cls(&eng); + cls.setHasInstance(true); + QVERIFY(cls.supportsExtension(QScriptClass::HasInstance)); + + QScriptValue obj = eng.newObject(&cls); + obj.setProperty("foo", QScriptValue(&eng, 123)); + QScriptValue plain = eng.newObject(); + QVERIFY(!plain.instanceOf(obj)); + + 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 lst = qvariant_cast(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", 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()); + } + } +} + +QTEST_MAIN(tst_QScriptClass) +#include "tst_qscriptclass.moc"