/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include "../shared/util.h" struct CustomType { QString string; }; Q_DECLARE_METATYPE(CustomType) Q_DECLARE_METATYPE(QBrush*) Q_DECLARE_METATYPE(QObjectList) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(Qt::BrushStyle) Q_DECLARE_METATYPE(QDir) static void dirFromScript(const QScriptValue &in, QDir &out) { QScriptValue path = in.property("path"); if (!path.isValid()) in.engine()->currentContext()->throwError("No path"); else out.setPath(path.toString()); } namespace MyNS { class A : public QObject { Q_OBJECT public: enum Type { Foo, Bar }; Q_ENUMS(Type) public Q_SLOTS: int slotTakingScopedEnumArg(MyNS::A::Type t) { return t; } }; } class MyQObject : public QObject { Q_OBJECT Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty) Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty) Q_PROPERTY(QVariantList variantListProperty READ variantListProperty WRITE setVariantListProperty) Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty) Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty) Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty) Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty) Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false) Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty) Q_PROPERTY(int readOnlyProperty READ readOnlyProperty) Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType) Q_PROPERTY(Policy enumProperty READ enumProperty WRITE setEnumProperty) Q_PROPERTY(Ability flagsProperty READ flagsProperty WRITE setFlagsProperty) Q_ENUMS(Policy Strategy) Q_FLAGS(Ability) public: enum Policy { FooPolicy = 0, BarPolicy, BazPolicy }; enum Strategy { FooStrategy = 10, BarStrategy, BazStrategy }; enum AbilityFlag { NoAbility = 0x000, FooAbility = 0x001, BarAbility = 0x080, BazAbility = 0x200, AllAbility = FooAbility | BarAbility | BazAbility }; Q_DECLARE_FLAGS(Ability, AbilityFlag) MyQObject(QObject *parent = 0) : QObject(parent), m_intValue(123), m_variantValue(QLatin1String("foo")), m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))), m_stringValue(QLatin1String("bar")), m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")), m_brushValue(QColor(10, 20, 30, 40)), m_hiddenValue(456.0), m_writeOnlyValue(789), m_readOnlyValue(987), m_enumValue(BarPolicy), m_flagsValue(FooAbility), m_qtFunctionInvoked(-1) { } int intProperty() const { return m_intValue; } void setIntProperty(int value) { m_intValue = value; } QVariant variantProperty() const { return m_variantValue; } void setVariantProperty(const QVariant &value) { m_variantValue = value; } QVariantList variantListProperty() const { return m_variantListValue; } void setVariantListProperty(const QVariantList &value) { m_variantListValue = value; } QString stringProperty() const { return m_stringValue; } void setStringProperty(const QString &value) { m_stringValue = value; } QStringList stringListProperty() const { return m_stringListValue; } void setStringListProperty(const QStringList &value) { m_stringListValue = value; } QByteArray byteArrayProperty() const { return m_byteArrayValue; } void setByteArrayProperty(const QByteArray &value) { m_byteArrayValue = value; } QBrush brushProperty() const { return m_brushValue; } Q_INVOKABLE void setBrushProperty(const QBrush &value) { m_brushValue = value; } double hiddenProperty() const { return m_hiddenValue; } void setHiddenProperty(double value) { m_hiddenValue = value; } int writeOnlyProperty() const { return m_writeOnlyValue; } void setWriteOnlyProperty(int value) { m_writeOnlyValue = value; } int readOnlyProperty() const { return m_readOnlyValue; } QKeySequence shortcut() const { return m_shortcut; } void setShortcut(const QKeySequence &seq) { m_shortcut = seq; } CustomType propWithCustomType() const { return m_customType; } void setPropWithCustomType(const CustomType &c) { m_customType = c; } Policy enumProperty() const { return m_enumValue; } void setEnumProperty(Policy policy) { m_enumValue = policy; } Ability flagsProperty() const { return m_flagsValue; } void setFlagsProperty(Ability ability) { m_flagsValue = ability; } int qtFunctionInvoked() const { return m_qtFunctionInvoked; } QVariantList qtFunctionActuals() const { return m_actuals; } void resetQtFunctionInvoked() { m_qtFunctionInvoked = -1; m_actuals.clear(); } void clearConnectNotifySignals() { m_connectNotifySignals.clear(); } void clearDisconnectNotifySignals() { m_disconnectNotifySignals.clear(); } QList connectNotifySignals() const { return m_connectNotifySignals; } QList disconnectNotifySignals() const { return m_disconnectNotifySignals; } bool hasSingleConnectNotifySignal(const QMetaMethod &signal) const { return (m_connectNotifySignals.size() == 1) && (m_connectNotifySignals.first() == signal); } bool hasConnectNotifySignal(const QMetaMethod &signal) const { return m_connectNotifySignals.contains(signal); } bool hasSingleDisconnectNotifySignal(const QMetaMethod &signal) const { return (m_disconnectNotifySignals.size() == 1) && (m_disconnectNotifySignals.first() == signal); } Q_INVOKABLE void myInvokable() { m_qtFunctionInvoked = 0; } Q_INVOKABLE void myInvokableWithIntArg(int arg) { m_qtFunctionInvoked = 1; m_actuals << arg; } Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg) { m_qtFunctionInvoked = 2; m_actuals << arg; } Q_INVOKABLE void myInvokableWithFloatArg(float arg) { m_qtFunctionInvoked = 3; m_actuals << arg; } Q_INVOKABLE void myInvokableWithDoubleArg(double arg) { m_qtFunctionInvoked = 4; m_actuals << arg; } Q_INVOKABLE void myInvokableWithStringArg(const QString &arg) { m_qtFunctionInvoked = 5; m_actuals << arg; } Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2) { m_qtFunctionInvoked = 6; m_actuals << arg1 << arg2; } Q_INVOKABLE int myInvokableReturningInt() { m_qtFunctionInvoked = 7; return 123; } Q_INVOKABLE qlonglong myInvokableReturningLongLong() { m_qtFunctionInvoked = 39; return 456; } Q_INVOKABLE QString myInvokableReturningString() { m_qtFunctionInvoked = 8; return QLatin1String("ciao"); } Q_INVOKABLE QVariant myInvokableReturningVariant() { m_qtFunctionInvoked = 60; return 123; } Q_INVOKABLE QScriptValue myInvokableReturningScriptValue() { m_qtFunctionInvoked = 61; return 456; } Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) // overload { m_qtFunctionInvoked = 9; m_actuals << arg1 << arg2; } Q_INVOKABLE void myInvokableWithEnumArg(Policy policy) { m_qtFunctionInvoked = 10; m_actuals << policy; } Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy) { m_qtFunctionInvoked = 36; m_actuals << policy; } Q_INVOKABLE Policy myInvokableReturningEnum() { m_qtFunctionInvoked = 37; return BazPolicy; } Q_INVOKABLE MyQObject::Strategy myInvokableReturningQualifiedEnum() { m_qtFunctionInvoked = 38; return BazStrategy; } Q_INVOKABLE QVector myInvokableReturningVectorOfInt() { m_qtFunctionInvoked = 11; return QVector(); } Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector &) { m_qtFunctionInvoked = 12; } Q_INVOKABLE QObject *myInvokableReturningQObjectStar() { m_qtFunctionInvoked = 13; return this; } Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst) { m_qtFunctionInvoked = 14; m_actuals << qVariantFromValue(lst); return lst; } Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v) { m_qtFunctionInvoked = 15; m_actuals << v; return v; } Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm) { m_qtFunctionInvoked = 16; m_actuals << vm; return vm; } Q_INVOKABLE QVariantList myInvokableWithVariantListArg(const QVariantList &lst) { m_qtFunctionInvoked = 62; m_actuals.append(QVariant(lst)); return lst; } Q_INVOKABLE QList myInvokableWithListOfIntArg(const QList &lst) { m_qtFunctionInvoked = 17; m_actuals << qVariantFromValue(lst); return lst; } Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject *obj) { m_qtFunctionInvoked = 18; m_actuals << qVariantFromValue(obj); return obj; } Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush) { m_qtFunctionInvoked = 19; m_actuals << qVariantFromValue(brush); return brush; } Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style) { m_qtFunctionInvoked = 43; m_actuals << qVariantFromValue(style); } Q_INVOKABLE void myInvokableWithVoidStarArg(void *arg) { m_qtFunctionInvoked = 44; m_actuals << qVariantFromValue(arg); } Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg) { m_qtFunctionInvoked = 45; m_actuals << qVariantFromValue(arg); } Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg) { m_qtFunctionInvoked = 46; m_actuals << qVariantFromValue(arg); } Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "") { m_qtFunctionInvoked = 47; m_actuals << qVariantFromValue(arg1) << qVariantFromValue(arg2); } Q_INVOKABLE QObject& myInvokableReturningRef() { m_qtFunctionInvoked = 48; return *this; } Q_INVOKABLE const QObject& myInvokableReturningConstRef() const { const_cast(this)->m_qtFunctionInvoked = 49; return *this; } Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg) { const_cast(this)->m_qtFunctionInvoked = 50; m_actuals << qVariantFromValue(arg); } Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg) { const_cast(this)->m_qtFunctionInvoked = 51; m_actuals << qVariantFromValue(arg); } Q_INVOKABLE void myInvokableWithMyQObjectArg(MyQObject *arg) { m_qtFunctionInvoked = 52; m_actuals << qVariantFromValue((QObject*)arg); } Q_INVOKABLE MyQObject* myInvokableReturningMyQObject() { m_qtFunctionInvoked = 53; return this; } Q_INVOKABLE void myInvokableWithConstMyQObjectArg(const MyQObject *arg) { m_qtFunctionInvoked = 54; m_actuals << qVariantFromValue((QObject*)arg); } Q_INVOKABLE void myInvokableWithQDirArg(const QDir &arg) { m_qtFunctionInvoked = 55; m_actuals << qVariantFromValue(arg); } Q_INVOKABLE QScriptValue myInvokableWithScriptValueArg(const QScriptValue &arg) { m_qtFunctionInvoked = 56; return arg; } Q_INVOKABLE QObject* myInvokableReturningMyQObjectAsQObject() { m_qtFunctionInvoked = 57; return this; } Q_INVOKABLE Ability myInvokableWithFlagsArg(Ability arg) { m_qtFunctionInvoked = 58; m_actuals << int(arg); return arg; } Q_INVOKABLE MyQObject::Ability myInvokableWithQualifiedFlagsArg(MyQObject::Ability arg) { m_qtFunctionInvoked = 59; m_actuals << int(arg); return arg; } Q_INVOKABLE QWidget *myInvokableWithQWidgetStarArg(QWidget *arg) { m_qtFunctionInvoked = 63; m_actuals << qVariantFromValue((QWidget*)arg); return arg; } Q_INVOKABLE short myInvokableWithShortArg(short arg) { m_qtFunctionInvoked = 64; m_actuals << qVariantFromValue(arg); return arg; } Q_INVOKABLE unsigned short myInvokableWithUShortArg(unsigned short arg) { m_qtFunctionInvoked = 65; m_actuals << qVariantFromValue(arg); return arg; } Q_INVOKABLE char myInvokableWithCharArg(char arg) { m_qtFunctionInvoked = 66; m_actuals << qVariantFromValue(arg); return arg; } Q_INVOKABLE unsigned char myInvokableWithUCharArg(unsigned char arg) { m_qtFunctionInvoked = 67; m_actuals << qVariantFromValue(arg); return arg; } Q_INVOKABLE qulonglong myInvokableWithULonglongArg(qulonglong arg) { m_qtFunctionInvoked = 68; m_actuals << qVariantFromValue(arg); return arg; } Q_INVOKABLE long myInvokableWithLongArg(long arg) { m_qtFunctionInvoked = 69; m_actuals << qVariantFromValue(arg); return arg; } Q_INVOKABLE unsigned long myInvokableWithULongArg(unsigned long arg) { m_qtFunctionInvoked = 70; m_actuals << qVariantFromValue(arg); return arg; } Q_INVOKABLE QObjectList findObjects() const { return findChildren(); } Q_INVOKABLE QList myInvokableNumbers() const { return QList() << 1 << 2 << 3; } void emitMySignal() { emit mySignal(); } void emitMySignalWithIntArg(int arg) { emit mySignalWithIntArg(arg); } void emitMySignal2(bool arg) { emit mySignal2(arg); } void emitMySignal2() { emit mySignal2(); } void emitMyOverloadedSignal(int arg) { emit myOverloadedSignal(arg); } void emitMyOverloadedSignal(const QString &arg) { emit myOverloadedSignal(arg); } void emitMyOtherOverloadedSignal(const QString &arg) { emit myOtherOverloadedSignal(arg); } void emitMyOtherOverloadedSignal(int arg) { emit myOtherOverloadedSignal(arg); } void emitMySignalWithDefaultArgWithArg(int arg) { emit mySignalWithDefaultArg(arg); } void emitMySignalWithDefaultArg() { emit mySignalWithDefaultArg(); } void emitMySignalWithVariantArg(const QVariant &arg) { emit mySignalWithVariantArg(arg); } void emitMySignalWithScriptEngineArg(QScriptEngine *arg) { emit mySignalWithScriptEngineArg(arg); } public Q_SLOTS: void mySlot() { m_qtFunctionInvoked = 20; } void mySlotWithIntArg(int arg) { m_qtFunctionInvoked = 21; m_actuals << arg; } void mySlotWithDoubleArg(double arg) { m_qtFunctionInvoked = 22; m_actuals << arg; } void mySlotWithStringArg(const QString &arg) { m_qtFunctionInvoked = 23; m_actuals << arg; } void myOverloadedSlot() { m_qtFunctionInvoked = 24; } void myOverloadedSlot(QObject *arg) { m_qtFunctionInvoked = 41; m_actuals << qVariantFromValue(arg); } void myOverloadedSlot(bool arg) { m_qtFunctionInvoked = 25; m_actuals << arg; } void myOverloadedSlot(const QStringList &arg) { m_qtFunctionInvoked = 42; m_actuals << arg; } void myOverloadedSlot(double arg) { m_qtFunctionInvoked = 26; m_actuals << arg; } void myOverloadedSlot(float arg) { m_qtFunctionInvoked = 27; m_actuals << arg; } void myOverloadedSlot(int arg) { m_qtFunctionInvoked = 28; m_actuals << arg; } void myOverloadedSlot(const QString &arg) { m_qtFunctionInvoked = 29; m_actuals << arg; } void myOverloadedSlot(const QColor &arg) { m_qtFunctionInvoked = 30; m_actuals << arg; } void myOverloadedSlot(const QBrush &arg) { m_qtFunctionInvoked = 31; m_actuals << arg; } void myOverloadedSlot(const QDateTime &arg) { m_qtFunctionInvoked = 32; m_actuals << arg; } void myOverloadedSlot(const QDate &arg) { m_qtFunctionInvoked = 33; m_actuals << arg; } void myOverloadedSlot(const QTime &arg) { m_qtFunctionInvoked = 69; m_actuals << arg; } void myOverloadedSlot(const QRegExp &arg) { m_qtFunctionInvoked = 34; m_actuals << arg; } void myOverloadedSlot(const QVariant &arg) { m_qtFunctionInvoked = 35; m_actuals << arg; } virtual int myVirtualSlot(int arg) { m_qtFunctionInvoked = 58; return arg; } void qscript_call(int arg) { m_qtFunctionInvoked = 40; m_actuals << arg; } protected Q_SLOTS: void myProtectedSlot() { m_qtFunctionInvoked = 36; } private Q_SLOTS: void myPrivateSlot() { } Q_SIGNALS: void mySignal(); void mySignalWithIntArg(int arg); void mySignalWithDoubleArg(double arg); void mySignal2(bool arg = false); void myOverloadedSignal(int arg); void myOverloadedSignal(const QString &arg); void myOtherOverloadedSignal(const QString &arg); void myOtherOverloadedSignal(int arg); void mySignalWithDefaultArg(int arg = 123); void mySignalWithVariantArg(const QVariant &arg); void mySignalWithScriptEngineArg(QScriptEngine *arg); protected: void connectNotify(const QMetaMethod &signal) { m_connectNotifySignals.append(signal); } void disconnectNotify(const QMetaMethod &signal) { m_disconnectNotifySignals.append(signal); } protected: int m_intValue; QVariant m_variantValue; QVariantList m_variantListValue; QString m_stringValue; QStringList m_stringListValue; QByteArray m_byteArrayValue; QBrush m_brushValue; double m_hiddenValue; int m_writeOnlyValue; int m_readOnlyValue; QKeySequence m_shortcut; CustomType m_customType; Policy m_enumValue; Ability m_flagsValue; int m_qtFunctionInvoked; QVariantList m_actuals; QList m_connectNotifySignals; QList m_disconnectNotifySignals; }; Q_DECLARE_METATYPE(MyQObject*) Q_DECLARE_METATYPE(MyQObject::Policy) class MyOtherQObject : public MyQObject { Q_OBJECT public: MyOtherQObject(QObject *parent = 0) : MyQObject(parent) { } public Q_SLOTS: virtual int myVirtualSlot(int arg) { m_qtFunctionInvoked = 59; return arg; } }; class MyEnumTestQObject : public QObject { Q_OBJECT Q_PROPERTY(QString p1 READ p1) Q_PROPERTY(QString p2 READ p2) Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false) Q_PROPERTY(QString p4 READ p4) Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false) Q_PROPERTY(QString p6 READ p6) public: MyEnumTestQObject(QObject *parent = 0) : QObject(parent) { } QString p1() const { return QLatin1String("p1"); } QString p2() const { return QLatin1String("p2"); } QString p3() const { return QLatin1String("p3"); } QString p4() const { return QLatin1String("p4"); } QString p5() const { return QLatin1String("p5"); } QString p6() const { return QLatin1String("p5"); } public Q_SLOTS: void mySlot() { } void myOtherSlot() { } Q_SIGNALS: void mySignal(); }; class tst_QScriptExtQObject : public QObject { Q_OBJECT public: tst_QScriptExtQObject(); virtual ~tst_QScriptExtQObject(); public slots: void init(); void cleanup(); protected slots: void onSignalHandlerException(const QScriptValue &exception) { m_signalHandlerException = exception; } private slots: void registeredTypes(); void getSetStaticProperty(); void getSetStaticProperty_propertyFlags(); void getSetStaticProperty_changeInCpp(); void getSetStaticProperty_changeInJS(); void getSetStaticProperty_compatibleVariantTypes(); void getSetStaticProperty_conversion(); void getSetStaticProperty_delete(); void getSetStaticProperty_nonScriptable(); void getSetStaticProperty_writeOnly(); void getSetStaticProperty_readOnly(); void getSetStaticProperty_enum(); void getSetStaticProperty_qflags(); void getSetStaticProperty_pointerDeref(); void getSetStaticProperty_customGetterSetter(); void getSetStaticProperty_methodPersistence(); void getSetDynamicProperty(); void getSetDynamicProperty_deleteFromCpp(); void getSetDynamicProperty_hideChildObject(); void getSetDynamicProperty_setBeforeGet(); void getSetDynamicProperty_doNotHideJSProperty(); void getSetChildren(); void callQtInvokable(); void callQtInvokable2(); void callQtInvokable3(); void callQtInvokable4(); void callQtInvokable5(); void callQtInvokable6(); void callQtInvokable7(); void connectAndDisconnect(); void connectAndDisconnect_emitFromJS(); void connectAndDisconnect_senderWrapperCollected(); void connectAndDisconnectWithBadArgs(); void connectAndDisconnect_senderDeleted(); void cppConnectAndDisconnect(); void cppConnectAndDisconnect2(); void classEnums(); void classConstructor(); void overrideInvokable(); void transferInvokable(); void findChild(); void findChildren(); void childObjects(); void overloadedSlots(); void enumerate_data(); void enumerate(); void enumerateSpecial(); void wrapOptions(); void prototypes(); void objectDeleted(); void connectToDestroyedSignal(); void emitAfterReceiverDeleted(); void inheritedSlots(); void enumerateMetaObject(); void nestedArrayAsSlotArgument_data(); void nestedArrayAsSlotArgument(); void nestedObjectAsSlotArgument_data(); void nestedObjectAsSlotArgument(); void propertyAccessThroughActivationObject(); void connectionRemovedAfterQueuedCall(); void collectQObjectWithClosureSlot(); void collectQObjectWithClosureSlot2(); private: QScriptEngine *m_engine; MyQObject *m_myObject; QScriptValue m_signalHandlerException; }; tst_QScriptExtQObject::tst_QScriptExtQObject() { } tst_QScriptExtQObject::~tst_QScriptExtQObject() { } void tst_QScriptExtQObject::init() { m_engine = new QScriptEngine(); m_myObject = new MyQObject(); m_engine->globalObject().setProperty("myObject", m_engine->newQObject(m_myObject)); m_engine->globalObject().setProperty("global", m_engine->globalObject()); } void tst_QScriptExtQObject::cleanup() { delete m_engine; delete m_myObject; } // this test has to be first and test that some types are automatically registered void tst_QScriptExtQObject::registeredTypes() { QScriptEngine e; QObject *t = new MyQObject; QObject *c = new QObject(t); c->setObjectName ("child1"); e.globalObject().setProperty("MyTest", e.newQObject(t)); QScriptValue v1 = e.evaluate("MyTest.findObjects()[0].objectName;"); QCOMPARE(v1.toString(), c->objectName()); QScriptValue v2 = e.evaluate("MyTest.myInvokableNumbers()"); QCOMPARE(qscriptvalue_cast >(v2), (QList() << 1 << 2 << 3)); } static QScriptValue getSetProperty(QScriptContext *ctx, QScriptEngine *) { if (ctx->argumentCount() != 0) ctx->callee().setProperty("value", ctx->argument(0)); return ctx->callee().property("value"); } static QScriptValue policyToScriptValue(QScriptEngine *engine, const MyQObject::Policy &policy) { return qScriptValueFromValue(engine, policy); } static void policyFromScriptValue(const QScriptValue &value, MyQObject::Policy &policy) { QString str = value.toString(); if (str == QLatin1String("red")) policy = MyQObject::FooPolicy; else if (str == QLatin1String("green")) policy = MyQObject::BarPolicy; else if (str == QLatin1String("blue")) policy = MyQObject::BazPolicy; else policy = (MyQObject::Policy)-1; } void tst_QScriptExtQObject::getSetStaticProperty() { QCOMPARE(m_engine->evaluate("myObject.noSuchProperty").isUndefined(), true); // initial value (set in MyQObject constructor) QCOMPARE(m_engine->evaluate("myObject.intProperty") .strictlyEquals(QScriptValue(m_engine, 123.0)), true); QCOMPARE(m_engine->evaluate("myObject.variantProperty") .toVariant(), QVariant(QLatin1String("foo"))); QCOMPARE(m_engine->evaluate("myObject.stringProperty") .strictlyEquals(QScriptValue(m_engine, QLatin1String("bar"))), true); QCOMPARE(m_engine->evaluate("myObject.variantListProperty").isArray(), true); QCOMPARE(m_engine->evaluate("myObject.variantListProperty.length") .strictlyEquals(QScriptValue(m_engine, 2)), true); QCOMPARE(m_engine->evaluate("myObject.variantListProperty[0]") .strictlyEquals(QScriptValue(m_engine, 123)), true); QCOMPARE(m_engine->evaluate("myObject.variantListProperty[1]") .strictlyEquals(QScriptValue(m_engine, QLatin1String("foo"))), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty").isArray(), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty.length") .strictlyEquals(QScriptValue(m_engine, 2)), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[0]").isString(), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[0]").toString(), QLatin1String("zig")); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[1]").isString(), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[1]").toString(), QLatin1String("zag")); } void tst_QScriptExtQObject::getSetStaticProperty_propertyFlags() { // default flags for "normal" properties { QScriptValue mobj = m_engine->globalObject().property("myObject"); QVERIFY(!(mobj.propertyFlags("intProperty") & QScriptValue::ReadOnly)); QVERIFY(mobj.propertyFlags("intProperty") & QScriptValue::Undeletable); QVERIFY(mobj.propertyFlags("intProperty") & QScriptValue::PropertyGetter); QVERIFY(mobj.propertyFlags("intProperty") & QScriptValue::PropertySetter); QVERIFY(!(mobj.propertyFlags("intProperty") & QScriptValue::SkipInEnumeration)); QVERIFY(mobj.propertyFlags("intProperty") & QScriptValue::QObjectMember); QVERIFY(!(mobj.propertyFlags("mySlot") & QScriptValue::ReadOnly)); QVERIFY(!(mobj.propertyFlags("mySlot") & QScriptValue::Undeletable)); QVERIFY(!(mobj.propertyFlags("mySlot") & QScriptValue::SkipInEnumeration)); QVERIFY(mobj.propertyFlags("mySlot") & QScriptValue::QObjectMember); // signature-based property QVERIFY(!(mobj.propertyFlags("mySlot()") & QScriptValue::ReadOnly)); QVERIFY(!(mobj.propertyFlags("mySlot()") & QScriptValue::Undeletable)); QVERIFY(!(mobj.propertyFlags("mySlot()") & QScriptValue::SkipInEnumeration)); QVERIFY(mobj.propertyFlags("mySlot()") & QScriptValue::QObjectMember); } } void tst_QScriptExtQObject::getSetStaticProperty_changeInCpp() { // property change in C++ should be reflected in script m_myObject->setIntProperty(456); QCOMPARE(m_engine->evaluate("myObject.intProperty") .strictlyEquals(QScriptValue(m_engine, 456)), true); m_myObject->setIntProperty(789); QCOMPARE(m_engine->evaluate("myObject.intProperty") .strictlyEquals(QScriptValue(m_engine, 789)), true); m_myObject->setVariantProperty(QLatin1String("bar")); QVERIFY(m_engine->evaluate("myObject.variantProperty") .strictlyEquals(QScriptValue(m_engine, QLatin1String("bar")))); m_myObject->setVariantProperty(42); QCOMPARE(m_engine->evaluate("myObject.variantProperty") .toVariant(), QVariant(42)); m_myObject->setVariantProperty(qVariantFromValue(QBrush())); QVERIFY(m_engine->evaluate("myObject.variantProperty").isVariant()); m_myObject->setStringProperty(QLatin1String("baz")); QCOMPARE(m_engine->evaluate("myObject.stringProperty") .equals(QScriptValue(m_engine, QLatin1String("baz"))), true); m_myObject->setStringProperty(QLatin1String("zab")); QCOMPARE(m_engine->evaluate("myObject.stringProperty") .equals(QScriptValue(m_engine, QLatin1String("zab"))), true); } void tst_QScriptExtQObject::getSetStaticProperty_changeInJS() { // property change in script should be reflected in C++ QCOMPARE(m_engine->evaluate("myObject.intProperty = 123") .strictlyEquals(QScriptValue(m_engine, 123)), true); QCOMPARE(m_engine->evaluate("myObject.intProperty") .strictlyEquals(QScriptValue(m_engine, 123)), true); QCOMPARE(m_myObject->intProperty(), 123); QCOMPARE(m_engine->evaluate("myObject.intProperty = 'ciao!';" "myObject.intProperty") .strictlyEquals(QScriptValue(m_engine, 0)), true); QCOMPARE(m_myObject->intProperty(), 0); QCOMPARE(m_engine->evaluate("myObject.intProperty = '123';" "myObject.intProperty") .strictlyEquals(QScriptValue(m_engine, 123)), true); QCOMPARE(m_myObject->intProperty(), 123); QCOMPARE(m_engine->evaluate("myObject.stringProperty = 'ciao'") .strictlyEquals(QScriptValue(m_engine, QLatin1String("ciao"))), true); QCOMPARE(m_engine->evaluate("myObject.stringProperty") .strictlyEquals(QScriptValue(m_engine, QLatin1String("ciao"))), true); QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao")); QCOMPARE(m_engine->evaluate("myObject.stringProperty = 123;" "myObject.stringProperty") .strictlyEquals(QScriptValue(m_engine, QLatin1String("123"))), true); QCOMPARE(m_myObject->stringProperty(), QLatin1String("123")); QVERIFY(m_engine->evaluate("myObject.stringProperty = null;" "myObject.stringProperty") .strictlyEquals(QScriptValue(m_engine, QString()))); QCOMPARE(m_myObject->stringProperty(), QString()); QVERIFY(m_engine->evaluate("myObject.stringProperty = undefined;" "myObject.stringProperty") .strictlyEquals(QScriptValue(m_engine, QString()))); QCOMPARE(m_myObject->stringProperty(), QString()); QCOMPARE(m_engine->evaluate("myObject.variantProperty = 'foo';" "myObject.variantProperty.valueOf()").toString(), QLatin1String("foo")); QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo"))); QVERIFY(m_engine->evaluate("myObject.variantProperty = undefined;" "myObject.variantProperty").isUndefined()); QVERIFY(!m_myObject->variantProperty().isValid()); QVERIFY(m_engine->evaluate("myObject.variantProperty = null;" "myObject.variantProperty").isUndefined()); QVERIFY(!m_myObject->variantProperty().isValid()); QCOMPARE(m_engine->evaluate("myObject.variantProperty = 42;" "myObject.variantProperty").toNumber(), 42.0); QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0); QCOMPARE(m_engine->evaluate("myObject.variantListProperty = [1, 'two', true];" "myObject.variantListProperty.length") .strictlyEquals(QScriptValue(m_engine, 3)), true); QCOMPARE(m_engine->evaluate("myObject.variantListProperty[0]") .strictlyEquals(QScriptValue(m_engine, 1)), true); QCOMPARE(m_engine->evaluate("myObject.variantListProperty[1]") .strictlyEquals(QScriptValue(m_engine, QLatin1String("two"))), true); QCOMPARE(m_engine->evaluate("myObject.variantListProperty[2]") .strictlyEquals(QScriptValue(m_engine, true)), true); { QVariantList vl = qscriptvalue_cast(m_engine->evaluate("myObject.variantListProperty")); QCOMPARE(vl, QVariantList() << QVariant(1) << QVariant(QLatin1String("two")) << QVariant(true)); } QCOMPARE(m_engine->evaluate("myObject.stringListProperty = [1, 'two', true];" "myObject.stringListProperty.length") .strictlyEquals(QScriptValue(m_engine, 3)), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[0]").isString(), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[0]").toString(), QLatin1String("1")); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[1]").isString(), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[1]").toString(), QLatin1String("two")); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[2]").isString(), true); QCOMPARE(m_engine->evaluate("myObject.stringListProperty[2]").toString(), QLatin1String("true")); { QStringList sl = qscriptvalue_cast(m_engine->evaluate("myObject.stringListProperty")); QCOMPARE(sl, QStringList() << QLatin1String("1") << QLatin1String("two") << QLatin1String("true")); } } void tst_QScriptExtQObject::getSetStaticProperty_compatibleVariantTypes() { // test setting properties where we can't convert the type natively but where the // types happen to be compatible variant types already { QKeySequence sequence(Qt::ControlModifier + Qt::AltModifier + Qt::Key_Delete); QScriptValue mobj = m_engine->globalObject().property("myObject"); QVERIFY(m_myObject->shortcut().isEmpty()); mobj.setProperty("shortcut", m_engine->newVariant(sequence)); QVERIFY(m_myObject->shortcut() == sequence); } { CustomType t; t.string = "hello"; QScriptValue mobj = m_engine->globalObject().property("myObject"); QVERIFY(m_myObject->propWithCustomType().string.isEmpty()); mobj.setProperty("propWithCustomType", m_engine->newVariant(qVariantFromValue(t))); QVERIFY(m_myObject->propWithCustomType().string == t.string); } } void tst_QScriptExtQObject::getSetStaticProperty_conversion() { // test that we do value conversion if necessary when setting properties { QScriptValue br = m_engine->evaluate("myObject.brushProperty"); QVERIFY(br.isVariant()); QVERIFY(!br.strictlyEquals(m_engine->evaluate("myObject.brushProperty"))); QCOMPARE(qscriptvalue_cast(br), m_myObject->brushProperty()); QCOMPARE(qscriptvalue_cast(br), m_myObject->brushProperty().color()); QColor newColor(40, 30, 20, 10); QScriptValue val = qScriptValueFromValue(m_engine, newColor); m_engine->globalObject().setProperty("myColor", val); QScriptValue ret = m_engine->evaluate("myObject.brushProperty = myColor"); QCOMPARE(ret.strictlyEquals(val), true); br = m_engine->evaluate("myObject.brushProperty"); QCOMPARE(qscriptvalue_cast(br), QBrush(newColor)); QCOMPARE(qscriptvalue_cast(br), newColor); m_engine->globalObject().setProperty("myColor", QScriptValue()); } } void tst_QScriptExtQObject::getSetStaticProperty_delete() { // try to delete QCOMPARE(m_engine->evaluate("delete myObject.intProperty").toBoolean(), false); QCOMPARE(m_engine->evaluate("myObject.intProperty").toNumber(), 123.0); m_myObject->setVariantProperty(42); QCOMPARE(m_engine->evaluate("delete myObject.variantProperty").toBoolean(), false); QCOMPARE(m_engine->evaluate("myObject.variantProperty").toNumber(), 42.0); } void tst_QScriptExtQObject::getSetStaticProperty_nonScriptable() { // non-scriptable property QCOMPARE(m_myObject->hiddenProperty(), 456.0); QCOMPARE(m_engine->evaluate("myObject.hiddenProperty").isUndefined(), true); QCOMPARE(m_engine->evaluate("myObject.hiddenProperty = 123;" "myObject.hiddenProperty").toInt32(), 123); QCOMPARE(m_myObject->hiddenProperty(), 456.0); } void tst_QScriptExtQObject::getSetStaticProperty_writeOnly() { // write-only property QCOMPARE(m_myObject->writeOnlyProperty(), 789); QCOMPARE(m_engine->evaluate("myObject.writeOnlyProperty").isUndefined(), true); QCOMPARE(m_engine->evaluate("myObject.writeOnlyProperty = 123;" "myObject.writeOnlyProperty").isUndefined(), true); QCOMPARE(m_myObject->writeOnlyProperty(), 123); } void tst_QScriptExtQObject::getSetStaticProperty_readOnly() { // read-only property QCOMPARE(m_myObject->readOnlyProperty(), 987); QCOMPARE(m_engine->evaluate("myObject.readOnlyProperty").toInt32(), 987); QCOMPARE(m_engine->evaluate("myObject.readOnlyProperty = 654;" "myObject.readOnlyProperty").toInt32(), 987); QCOMPARE(m_myObject->readOnlyProperty(), 987); { QScriptValue mobj = m_engine->globalObject().property("myObject"); QCOMPARE(mobj.propertyFlags("readOnlyProperty") & QScriptValue::ReadOnly, QScriptValue::ReadOnly); } } void tst_QScriptExtQObject::getSetStaticProperty_enum() { // enum property QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy); { QScriptValue val = m_engine->evaluate("myObject.enumProperty"); QVERIFY(val.isNumber()); QCOMPARE(val.toInt32(), int(MyQObject::BarPolicy)); } m_engine->evaluate("myObject.enumProperty = 2"); QCOMPARE(m_myObject->enumProperty(), MyQObject::BazPolicy); m_engine->evaluate("myObject.enumProperty = 'BarPolicy'"); QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy); m_engine->evaluate("myObject.enumProperty = 'ScoobyDoo'"); QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy); // enum property with custom conversion qScriptRegisterMetaType(m_engine, policyToScriptValue, policyFromScriptValue); m_engine->evaluate("myObject.enumProperty = 'red'"); QCOMPARE(m_myObject->enumProperty(), MyQObject::FooPolicy); m_engine->evaluate("myObject.enumProperty = 'green'"); QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy); m_engine->evaluate("myObject.enumProperty = 'blue'"); QCOMPARE(m_myObject->enumProperty(), MyQObject::BazPolicy); m_engine->evaluate("myObject.enumProperty = 'nada'"); QCOMPARE(m_myObject->enumProperty(), (MyQObject::Policy)-1); } void tst_QScriptExtQObject::getSetStaticProperty_qflags() { // flags property QCOMPARE(m_myObject->flagsProperty(), MyQObject::FooAbility); { QScriptValue val = m_engine->evaluate("myObject.flagsProperty"); QVERIFY(val.isNumber()); QCOMPARE(val.toInt32(), int(MyQObject::FooAbility)); } m_engine->evaluate("myObject.flagsProperty = 0x80"); QCOMPARE(m_myObject->flagsProperty(), MyQObject::BarAbility); m_engine->evaluate("myObject.flagsProperty = 0x81"); QCOMPARE(m_myObject->flagsProperty(), MyQObject::Ability(MyQObject::FooAbility | MyQObject::BarAbility)); m_engine->evaluate("myObject.flagsProperty = 123"); // bogus values are accepted QCOMPARE(int(m_myObject->flagsProperty()), 123); m_engine->evaluate("myObject.flagsProperty = 'BazAbility'"); QCOMPARE(m_myObject->flagsProperty(), MyQObject::BazAbility); m_engine->evaluate("myObject.flagsProperty = 'ScoobyDoo'"); QCOMPARE(m_myObject->flagsProperty(), MyQObject::BazAbility); } void tst_QScriptExtQObject::getSetStaticProperty_pointerDeref() { // auto-dereferencing of pointers { QBrush b = QColor(0xCA, 0xFE, 0xBA, 0xBE); QBrush *bp = &b; QScriptValue bpValue = m_engine->newVariant(qVariantFromValue(bp)); m_engine->globalObject().setProperty("brushPointer", bpValue); { QScriptValue ret = m_engine->evaluate("myObject.setBrushProperty(brushPointer)"); QCOMPARE(ret.isUndefined(), true); QCOMPARE(qscriptvalue_cast(m_engine->evaluate("myObject.brushProperty")), b); } { b = QColor(0xDE, 0xAD, 0xBE, 0xEF); QScriptValue ret = m_engine->evaluate("myObject.brushProperty = brushPointer"); QCOMPARE(ret.strictlyEquals(bpValue), true); QCOMPARE(qscriptvalue_cast(m_engine->evaluate("myObject.brushProperty")), b); } m_engine->globalObject().setProperty("brushPointer", QScriptValue()); } } void tst_QScriptExtQObject::getSetStaticProperty_customGetterSetter() { // install custom property getter+setter { QScriptValue mobj = m_engine->globalObject().property("myObject"); mobj.setProperty("intProperty", m_engine->newFunction(getSetProperty), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); QVERIFY(mobj.property("intProperty").toInt32() != 321); mobj.setProperty("intProperty", 321); QCOMPARE(mobj.property("intProperty").toInt32(), 321); } } void tst_QScriptExtQObject::getSetStaticProperty_methodPersistence() { // method properties are persistent { QScriptValue slot = m_engine->evaluate("myObject.mySlot"); QVERIFY(slot.isFunction()); QScriptValue sameSlot = m_engine->evaluate("myObject.mySlot"); QVERIFY(sameSlot.strictlyEquals(slot)); sameSlot = m_engine->evaluate("myObject['mySlot()']"); QVERIFY(sameSlot.isFunction()); QEXPECT_FAIL("", "QTBUG-17611: Signature-based method lookup creates new function wrapper object", Continue); QVERIFY(sameSlot.strictlyEquals(slot)); } } void tst_QScriptExtQObject::getSetDynamicProperty() { // initially the object does not have the property QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('dynamicProperty')") .strictlyEquals(QScriptValue(m_engine, false)), true); // add a dynamic property in C++ QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false); QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('dynamicProperty')") .strictlyEquals(QScriptValue(m_engine, true)), true); QCOMPARE(m_engine->evaluate("myObject.dynamicProperty") .strictlyEquals(QScriptValue(m_engine, 123)), true); // check the flags { QScriptValue mobj = m_engine->globalObject().property("myObject"); QVERIFY(!(mobj.propertyFlags("dynamicProperty") & QScriptValue::ReadOnly)); QVERIFY(!(mobj.propertyFlags("dynamicProperty") & QScriptValue::Undeletable)); QVERIFY(!(mobj.propertyFlags("dynamicProperty") & QScriptValue::SkipInEnumeration)); QVERIFY(mobj.propertyFlags("dynamicProperty") & QScriptValue::QObjectMember); } // property change in script should be reflected in C++ QCOMPARE(m_engine->evaluate("myObject.dynamicProperty = 'foo';" "myObject.dynamicProperty") .strictlyEquals(QScriptValue(m_engine, QLatin1String("foo"))), true); QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo")); // delete the property QCOMPARE(m_engine->evaluate("delete myObject.dynamicProperty").toBoolean(), true); QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false); QCOMPARE(m_engine->evaluate("myObject.dynamicProperty").isUndefined(), true); QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('dynamicProperty')").toBoolean(), false); } void tst_QScriptExtQObject::getSetDynamicProperty_deleteFromCpp() { QScriptValue val = m_engine->newQObject(m_myObject); m_myObject->setProperty("dynamicFromCpp", 1234); QVERIFY(val.property("dynamicFromCpp").strictlyEquals(QScriptValue(m_engine, 1234))); m_myObject->setProperty("dynamicFromCpp", 4321); QVERIFY(val.property("dynamicFromCpp").strictlyEquals(QScriptValue(m_engine, 4321))); QCOMPARE(m_myObject->property("dynamicFromCpp").toInt(), 4321); // In this case we delete the property from C++ m_myObject->setProperty("dynamicFromCpp", QVariant()); QVERIFY(!m_myObject->property("dynamicFromCpp").isValid()); QVERIFY(!val.property("dynamicFromCpp").isValid()); } void tst_QScriptExtQObject::getSetDynamicProperty_hideChildObject() { QScriptValue val = m_engine->newQObject(m_myObject); // Add our named child and access it QObject *child = new QObject(m_myObject); child->setObjectName("testName"); QCOMPARE(val.property("testName").toQObject(), child); // Dynamic properties have precedence, hiding the child object m_myObject->setProperty("testName", 42); QVERIFY(val.property("testName").strictlyEquals(QScriptValue(m_engine, 42))); // Remove dynamic property m_myObject->setProperty("testName", QVariant()); QCOMPARE(val.property("testName").toQObject(), child); } void tst_QScriptExtQObject::getSetDynamicProperty_setBeforeGet() { QScriptValue val = m_engine->newQObject(m_myObject); m_myObject->setProperty("dynamic", 1111); val.setProperty("dynamic", 42); QVERIFY(val.property("dynamic").strictlyEquals(QScriptValue(m_engine, 42))); QCOMPARE(m_myObject->property("dynamic").toInt(), 42); } void tst_QScriptExtQObject::getSetDynamicProperty_doNotHideJSProperty() { QScriptValue val = m_engine->newQObject(m_myObject); // Set property on JS and dynamically on our QObject val.setProperty("x", 42); m_myObject->setProperty("x", 2222); QEXPECT_FAIL("", "QTBUG-17612: Dynamic C++ property overrides JS property", Continue); // JS should see the original JS value QVERIFY(val.property("x").strictlyEquals(QScriptValue(m_engine, 42))); // The dynamic property is intact QCOMPARE(m_myObject->property("x").toInt(), 2222); } void tst_QScriptExtQObject::getSetChildren() { QScriptValue mobj = m_engine->evaluate("myObject"); // initially the object does not have the child QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('child')") .strictlyEquals(QScriptValue(m_engine, false)), true); // add a child MyQObject *child = new MyQObject(m_myObject); child->setObjectName("child"); QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('child')") .strictlyEquals(QScriptValue(m_engine, true)), true); QVERIFY(mobj.propertyFlags("child") & QScriptValue::ReadOnly); QVERIFY(mobj.propertyFlags("child") & QScriptValue::Undeletable); QVERIFY(mobj.propertyFlags("child") & QScriptValue::SkipInEnumeration); QVERIFY(!(mobj.propertyFlags("child") & QScriptValue::QObjectMember)); { QScriptValue scriptChild = m_engine->evaluate("myObject.child"); QVERIFY(scriptChild.isQObject()); QCOMPARE(scriptChild.toQObject(), (QObject*)child); QScriptValue sameChild = m_engine->evaluate("myObject.child"); QVERIFY(sameChild.strictlyEquals(scriptChild)); } // add a grandchild MyQObject *grandChild = new MyQObject(child); grandChild->setObjectName("grandChild"); QCOMPARE(m_engine->evaluate("myObject.child.hasOwnProperty('grandChild')") .strictlyEquals(QScriptValue(m_engine, true)), true); // delete grandchild delete grandChild; QCOMPARE(m_engine->evaluate("myObject.child.hasOwnProperty('grandChild')") .strictlyEquals(QScriptValue(m_engine, false)), true); // delete child delete child; QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('child')") .strictlyEquals(QScriptValue(m_engine, false)), true); } Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) template static QScriptValue qobjectToScriptValue(QScriptEngine *engine, T* const &in) { return engine->newQObject(in); } template static void qobjectFromScriptValue(const QScriptValue &object, T* &out) { out = qobject_cast(object.toQObject()); } void tst_QScriptExtQObject::callQtInvokable() { m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokable()").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 0); QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); // extra arguments should silently be ignored m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokable(10, 20, 30)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 0); QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithIntArg(123)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 1); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithIntArg('123')").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 1); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithLonglongArg(123)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 2); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123)); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithFloatArg(123.5)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 3); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithDoubleArg(123.5)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 4); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithStringArg('ciao')").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 5); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao")); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithStringArg(123)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 5); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123")); m_myObject->resetQtFunctionInvoked(); QVERIFY(m_engine->evaluate("myObject.myInvokableWithStringArg(null)").isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 5); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).type(), QVariant::String); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString()); m_myObject->resetQtFunctionInvoked(); QVERIFY(m_engine->evaluate("myObject.myInvokableWithStringArg(undefined)").isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 5); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).type(), QVariant::String); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithIntArgs(123, 456)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 6); QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableReturningInt()") .strictlyEquals(QScriptValue(m_engine, 123)), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 7); QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableReturningLongLong()") .strictlyEquals(QScriptValue(m_engine, 456)), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 39); QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableReturningString()") .strictlyEquals(QScriptValue(m_engine, QLatin1String("ciao"))), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 8); QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); m_myObject->resetQtFunctionInvoked(); QVERIFY(m_engine->evaluate("myObject.myInvokableReturningVariant()") .strictlyEquals(QScriptValue(m_engine, 123))); QCOMPARE(m_myObject->qtFunctionInvoked(), 60); m_myObject->resetQtFunctionInvoked(); QVERIFY(m_engine->evaluate("myObject.myInvokableReturningScriptValue()") .strictlyEquals(QScriptValue(m_engine, 456))); QCOMPARE(m_myObject->qtFunctionInvoked(), 61); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithIntArg(123, 456)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 9); QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); } void tst_QScriptExtQObject::callQtInvokable2() { m_myObject->resetQtFunctionInvoked(); QVERIFY(m_engine->evaluate("myObject.myInvokableWithVoidStarArg(null)").isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 44); m_myObject->resetQtFunctionInvoked(); QVERIFY(m_engine->evaluate("myObject.myInvokableWithVoidStarArg(123)").isError()); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithAmbiguousArg(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n myInvokableWithAmbiguousArg(int)\n myInvokableWithAmbiguousArg(uint)")); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithDefaultArgs(123, 'hello')"); QVERIFY(ret.isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 47); QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello")); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithDefaultArgs(456)"); QVERIFY(ret.isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 47); QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString()); } { QScriptValue fun = m_engine->evaluate("myObject.myInvokableWithPointArg"); QVERIFY(fun.isFunction()); m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = fun.call(m_engine->evaluate("myObject"), QScriptValueList() << qScriptValueFromValue(m_engine, QPoint(10, 20))); QVERIFY(ret.isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 50); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toPoint(), QPoint(10, 20)); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = fun.call(m_engine->evaluate("myObject"), QScriptValueList() << qScriptValueFromValue(m_engine, QPointF(30, 40))); QVERIFY(ret.isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 51); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toPointF(), QPointF(30, 40)); } } // calling function that returns (const)ref m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningRef()"); QVERIFY(ret.isUndefined()); QVERIFY(!m_engine->hasUncaughtException()); QCOMPARE(m_myObject->qtFunctionInvoked(), 48); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningConstRef()"); QVERIFY(ret.isUndefined()); QVERIFY(!m_engine->hasUncaughtException()); QCOMPARE(m_myObject->qtFunctionInvoked(), 49); } // first time we expect failure because the metatype is not registered m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableReturningVectorOfInt()").isError(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithVectorOfIntArg(0)").isError(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); // now we register it, and it should work qScriptRegisterSequenceMetaType >(m_engine); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningVectorOfInt()"); QCOMPARE(ret.isArray(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 11); } } void tst_QScriptExtQObject::callQtInvokable3() { { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVectorOfIntArg(myObject.myInvokableReturningVectorOfInt())"); QCOMPARE(ret.isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 12); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningQObjectStar()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 13); QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); QCOMPARE(ret.isQObject(), true); QCOMPARE(ret.toQObject(), (QObject *)m_myObject); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQObjectListArg([myObject])"); QCOMPARE(m_myObject->qtFunctionInvoked(), 14); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(ret.isArray(), true); QCOMPARE(ret.property(QLatin1String("length")) .strictlyEquals(QScriptValue(m_engine, 1)), true); QCOMPARE(ret.property(QLatin1String("0")).isQObject(), true); QCOMPARE(ret.property(QLatin1String("0")).toQObject(), (QObject *)m_myObject); } m_myObject->resetQtFunctionInvoked(); { m_myObject->setVariantProperty(QVariant(123)); QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(myObject.variantProperty)"); QVERIFY(ret.isNumber()); QCOMPARE(m_myObject->qtFunctionInvoked(), 15); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty()); QVERIFY(ret.strictlyEquals(QScriptValue(m_engine, 123))); } m_myObject->resetQtFunctionInvoked(); { m_myObject->setVariantProperty(qVariantFromValue(QBrush())); QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(myObject.variantProperty)"); QVERIFY(ret.isVariant()); QCOMPARE(m_myObject->qtFunctionInvoked(), 15); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0)); QCOMPARE(ret.toVariant(), m_myObject->variantProperty()); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(123)"); QVERIFY(ret.isNumber()); QCOMPARE(m_myObject->qtFunctionInvoked(), 15); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123)); QVERIFY(ret.strictlyEquals(QScriptValue(m_engine, 123))); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg('ciao')"); QVERIFY(ret.isString()); QCOMPARE(m_myObject->qtFunctionInvoked(), 15); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(QString::fromLatin1("ciao"))); QVERIFY(ret.strictlyEquals(QScriptValue(m_engine, QString::fromLatin1("ciao")))); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(null)"); QVERIFY(ret.isUndefined()); // invalid QVariant is converted to Undefined QCOMPARE(m_myObject->qtFunctionInvoked(), 15); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant()); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(undefined)"); QVERIFY(ret.isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 15); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant()); } m_engine->globalObject().setProperty("fishy", m_engine->newVariant(123)); m_engine->evaluate("myObject.myInvokableWithStringArg(fishy)"); m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })"); QCOMPARE(m_myObject->qtFunctionInvoked(), 16); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::QVariantMap)); QVariantMap vmap = qvariant_cast(v); QCOMPARE(vmap.keys().size(), 2); QCOMPARE(vmap.keys().at(0), QLatin1String("a")); QCOMPARE(vmap.value("a"), QVariant(123)); QCOMPARE(vmap.keys().at(1), QLatin1String("b")); QCOMPARE(vmap.value("b"), QVariant("ciao")); QCOMPARE(ret.isObject(), true); QCOMPARE(ret.property("a").strictlyEquals(QScriptValue(m_engine, 123)), true); QCOMPARE(ret.property("b").strictlyEquals(QScriptValue(m_engine, "ciao")), true); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithListOfIntArg([1, 5])"); QCOMPARE(m_myObject->qtFunctionInvoked(), 17); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), qMetaTypeId >()); QList ilst = qvariant_cast >(v); QCOMPARE(ilst.size(), 2); QCOMPARE(ilst.at(0), 1); QCOMPARE(ilst.at(1), 5); QCOMPARE(ret.isArray(), true); QCOMPARE(ret.property("0").strictlyEquals(QScriptValue(m_engine, 1)), true); QCOMPARE(ret.property("1").strictlyEquals(QScriptValue(m_engine, 5)), true); } } void tst_QScriptExtQObject::callQtInvokable4() { m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQObjectStarArg(myObject)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 18); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::QObjectStar)); QCOMPARE(qvariant_cast(v), (QObject *)m_myObject); QCOMPARE(ret.isQObject(), true); QCOMPARE(qscriptvalue_cast(ret), (QObject *)m_myObject); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQWidgetStarArg(null)"); QVERIFY(ret.isNull()); QCOMPARE(m_myObject->qtFunctionInvoked(), 63); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(qvariant_cast(v), (QObject *)0); } m_myObject->resetQtFunctionInvoked(); { // no implicit conversion from integer to QObject* QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQObjectStarArg(123)"); QCOMPARE(ret.isError(), true); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithShortArg(123)"); QVERIFY(ret.isNumber()); QCOMPARE(m_myObject->qtFunctionInvoked(), 64); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::Short)); QCOMPARE(qvariant_cast(v), short(123)); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithUShortArg(123)"); QVERIFY(ret.isNumber()); QCOMPARE(m_myObject->qtFunctionInvoked(), 65); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::UShort)); QCOMPARE(qvariant_cast(v), ushort(123)); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithCharArg(123)"); QVERIFY(ret.isNumber()); QCOMPARE(m_myObject->qtFunctionInvoked(), 66); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::Char)); QCOMPARE(qvariant_cast(v), char(123)); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithUCharArg(123)"); QVERIFY(ret.isNumber()); QCOMPARE(m_myObject->qtFunctionInvoked(), 67); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::UChar)); QCOMPARE(qvariant_cast(v), uchar(123)); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithULonglongArg(123)"); QVERIFY(ret.isNumber()); QCOMPARE(m_myObject->qtFunctionInvoked(), 68); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::ULongLong)); QCOMPARE(qvariant_cast(v), qulonglong(123)); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithLongArg(123)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 69); QVERIFY(ret.isNumber()); QCOMPARE(long(ret.toInteger()), long(123)); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::Long)); QCOMPARE(qvariant_cast(v), long(123)); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithULongArg(456)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 70); QVERIFY(ret.isNumber()); QCOMPARE(ulong(ret.toInteger()), ulong(456)); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::ULong)); QCOMPARE(qvariant_cast(v), ulong(456)); } } void tst_QScriptExtQObject::callQtInvokable5() { m_myObject->resetQtFunctionInvoked(); { QScriptValue fun = m_engine->evaluate("myObject.myInvokableWithQBrushArg"); QVERIFY(fun.isFunction()); QColor color(10, 20, 30, 40); // QColor should be converted to a QBrush QScriptValue ret = fun.call(QScriptValue(), QScriptValueList() << qScriptValueFromValue(m_engine, color)); QCOMPARE(m_myObject->qtFunctionInvoked(), 19); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QVariant v = m_myObject->qtFunctionActuals().at(0); QCOMPARE(v.userType(), int(QMetaType::QBrush)); QCOMPARE(qvariant_cast(v), color); QCOMPARE(qscriptvalue_cast(ret), color); } // private slots should not be part of the QObject binding QCOMPARE(m_engine->evaluate("myObject.myPrivateSlot").isUndefined(), true); // protected slots should be fine m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myProtectedSlot()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 36); // call with too few arguments { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithIntArg()"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n myInvokableWithIntArg(int,int)\n myInvokableWithIntArg(int)")); } // call function where not all types have been registered m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithBrushStyleArg(0)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): argument 1 has unknown type `Qt::BrushStyle' (register the type with qScriptRegisterMetaType())")); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); } // call function with incompatible argument type m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQBrushArg(null)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n myInvokableWithQBrushArg(QBrush)")); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); } // ability to call a slot with QObject-based arguments, even if those types haven't been registered m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithMyQObjectArg(myObject)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 52); QVERIFY(ret.isUndefined()); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(qvariant_cast(m_myObject->qtFunctionActuals().at(0)), (QObject*)m_myObject); } // inability to call a slot returning QObject-based type, when that type hasn't been registered m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningMyQObject()"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: cannot call myInvokableReturningMyQObject(): unknown return type `MyQObject*' (register the type with qScriptRegisterMetaType())")); } // ability to call a slot returning QObject-based type when that type has been registered qRegisterMetaType("MyQObject*"); m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningMyQObject()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 53); QVERIFY(ret.isQObject()); QCOMPARE(*reinterpret_cast(ret.toVariant().constData()), m_myObject); } // ability to call a slot with QObject-based argument, when the argument is const m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithConstMyQObjectArg(myObject)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 54); QVERIFY(ret.isUndefined()); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(qvariant_cast(m_myObject->qtFunctionActuals().at(0)), (QObject*)m_myObject); } } void tst_QScriptExtQObject::callQtInvokable6() { // QScriptValue arguments should be passed on without conversion m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithScriptValueArg(123)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 56); QVERIFY(ret.isNumber()); QCOMPARE(ret.toInt32(), 123); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithScriptValueArg('ciao')"); QCOMPARE(m_myObject->qtFunctionInvoked(), 56); QVERIFY(ret.isString()); QCOMPARE(ret.toString(), QString::fromLatin1("ciao")); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithScriptValueArg(this)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 56); QVERIFY(ret.isObject()); QVERIFY(ret.strictlyEquals(m_engine->globalObject())); } // the prototype specified by a conversion function should not be "down-graded" m_myObject->resetQtFunctionInvoked(); { QScriptValue qobjectProto = m_engine->newObject(); qScriptRegisterMetaType(m_engine, qobjectToScriptValue, qobjectFromScriptValue, qobjectProto); QScriptValue myQObjectProto = m_engine->newObject(); myQObjectProto.setPrototype(qobjectProto); qScriptRegisterMetaType(m_engine, qobjectToScriptValue, qobjectFromScriptValue, myQObjectProto); QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningMyQObjectAsQObject()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 57); QVERIFY(ret.isQObject()); QVERIFY(ret.prototype().strictlyEquals(myQObjectProto)); qScriptRegisterMetaType(m_engine, 0, 0, QScriptValue()); qScriptRegisterMetaType(m_engine, 0, 0, QScriptValue()); } // detect exceptions during argument conversion m_myObject->resetQtFunctionInvoked(); { QScriptValue (*dummy)(QScriptEngine *, const QDir &) = 0; qScriptRegisterMetaType(m_engine, dummy, dirFromScript); { QVERIFY(!m_engine->hasUncaughtException()); QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQDirArg({})"); QVERIFY(m_engine->hasUncaughtException()); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QString::fromLatin1("Error: No path")); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); } m_engine->clearExceptions(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQDirArg({path:'.'})"); QVERIFY(!m_engine->hasUncaughtException()); QVERIFY(ret.isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 55); } } } void tst_QScriptExtQObject::callQtInvokable7() { // qscript_call() { m_myObject->resetQtFunctionInvoked(); QScriptValue ret = m_engine->evaluate("new myObject(123)"); QVERIFY(ret.isError()); QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { m_myObject->resetQtFunctionInvoked(); QScriptValue ret = m_engine->evaluate("myObject(123)"); QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } // task 233624 { MyNS::A a; m_engine->globalObject().setProperty("anObject", m_engine->newQObject(&a)); QScriptValue ret = m_engine->evaluate("anObject.slotTakingScopedEnumArg(1)"); QVERIFY(!ret.isError()); QVERIFY(ret.isNumber()); QCOMPARE(ret.toInt32(), 1); m_engine->globalObject().setProperty("anObject", QScriptValue()); } // virtual slot redeclared in subclass (task 236467) { MyOtherQObject moq; m_engine->globalObject().setProperty("myOtherQObject", m_engine->newQObject(&moq)); moq.resetQtFunctionInvoked(); QScriptValue ret = m_engine->evaluate("myOtherQObject.myVirtualSlot(123)"); QCOMPARE(moq.qtFunctionInvoked(), 59); QVERIFY(!ret.isError()); QVERIFY(ret.isNumber()); QCOMPARE(ret.toInt32(), 123); } } void tst_QScriptExtQObject::connectAndDisconnect() { // connect(function) QCOMPARE(m_engine->evaluate("myObject.mySignal.connect(123)").isError(), true); m_engine->evaluate("myHandler = function() { global.gotSignal = true; global.signalArgs = arguments; global.slotThisObject = this; }"); m_myObject->clearConnectNotifySignals(); QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myHandler)").isUndefined()); QVERIFY(m_myObject->hasSingleConnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignal))); m_engine->evaluate("gotSignal = false"); m_engine->evaluate("myObject.mySignal()"); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 0.0); QVERIFY(m_engine->evaluate("slotThisObject").strictlyEquals(m_engine->globalObject())); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignal(); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 0.0); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myHandler)").isUndefined()); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignalWithIntArg(123); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QCOMPARE(m_engine->evaluate("signalArgs[0]").toNumber(), 123.0); m_myObject->clearDisconnectNotifySignals(); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myHandler)").isUndefined()); QVERIFY(m_myObject->hasSingleDisconnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignal))); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myHandler)").isError()); QVERIFY(m_engine->evaluate("myObject.mySignal2.connect(myHandler)").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myHandler)").isUndefined()); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignal2(false); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QCOMPARE(m_engine->evaluate("signalArgs[0]").toBoolean(), false); m_engine->evaluate("gotSignal = false"); QVERIFY(m_engine->evaluate("myObject.mySignal2.connect(myHandler)").isUndefined()); m_myObject->emitMySignal2(true); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QCOMPARE(m_engine->evaluate("signalArgs[0]").toBoolean(), true); QVERIFY(m_engine->evaluate("myObject.mySignal2.disconnect(myHandler)").isUndefined()); QVERIFY(m_engine->evaluate("myObject['mySignal2()'].connect(myHandler)").isUndefined()); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignal2(); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QVERIFY(m_engine->evaluate("myObject['mySignal2()'].disconnect(myHandler)").isUndefined()); // connecting to signal with default args should pick the most generic version (i.e. with all args) m_myObject->clearConnectNotifySignals(); QVERIFY(m_engine->evaluate("myObject.mySignalWithDefaultArg.connect(myHandler)").isUndefined()); QVERIFY(m_myObject->hasSingleConnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignalWithDefaultArg))); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignalWithDefaultArgWithArg(456); QVERIFY(m_engine->evaluate("gotSignal").toBoolean()); QCOMPARE(m_engine->evaluate("signalArgs.length").toInt32(), 1); QCOMPARE(m_engine->evaluate("signalArgs[0]").toInt32(), 456); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignalWithDefaultArg(); QVERIFY(m_engine->evaluate("gotSignal").toBoolean()); QCOMPARE(m_engine->evaluate("signalArgs.length").toInt32(), 1); QCOMPARE(m_engine->evaluate("signalArgs[0]").toInt32(), 123); QVERIFY(m_engine->evaluate("myObject.mySignalWithDefaultArg.disconnect(myHandler)").isUndefined()); m_engine->evaluate("gotSignal = false"); // connecting to overloaded signal should throw an error { QScriptValue ret = m_engine->evaluate("myObject.myOverloadedSignal.connect(myHandler)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QString::fromLatin1("Error: Function.prototype.connect: ambiguous connect to MyQObject::myOverloadedSignal(); candidates are\n" " myOverloadedSignal(int)\n" " myOverloadedSignal(QString)\n" "Use e.g. object['myOverloadedSignal(QString)'].connect() to connect to a particular overload")); } m_myObject->emitMyOverloadedSignal(123); QVERIFY(!m_engine->evaluate("gotSignal").toBoolean()); m_myObject->emitMyOverloadedSignal("ciao"); QVERIFY(!m_engine->evaluate("gotSignal").toBoolean()); m_engine->evaluate("gotSignal = false"); { QScriptValue ret = m_engine->evaluate("myObject.myOtherOverloadedSignal.connect(myHandler)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QString::fromLatin1("Error: Function.prototype.connect: ambiguous connect to MyQObject::myOtherOverloadedSignal(); candidates are\n" " myOtherOverloadedSignal(QString)\n" " myOtherOverloadedSignal(int)\n" "Use e.g. object['myOtherOverloadedSignal(int)'].connect() to connect to a particular overload")); } m_myObject->emitMyOtherOverloadedSignal("ciao"); QVERIFY(!m_engine->evaluate("gotSignal").toBoolean()); m_myObject->emitMyOtherOverloadedSignal(123); QVERIFY(!m_engine->evaluate("gotSignal").toBoolean()); // signal with QVariant arg: argument conversion should work m_myObject->clearConnectNotifySignals(); QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.connect(myHandler)").isUndefined()); QVERIFY(m_myObject->hasSingleConnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignalWithVariantArg))); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignalWithVariantArg(123); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QVERIFY(m_engine->evaluate("signalArgs[0]").isNumber()); QCOMPARE(m_engine->evaluate("signalArgs[0]").toNumber(), 123.0); QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.disconnect(myHandler)").isUndefined()); // signal with argument type that's unknown to the meta-type system m_myObject->clearConnectNotifySignals(); QVERIFY(m_engine->evaluate("myObject.mySignalWithScriptEngineArg.connect(myHandler)").isUndefined()); QVERIFY(m_myObject->hasSingleConnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignalWithScriptEngineArg))); m_engine->evaluate("gotSignal = false"); QTest::ignoreMessage(QtWarningMsg, "QScriptEngine: Unable to handle unregistered datatype 'QScriptEngine*' when invoking handler of signal MyQObject::mySignalWithScriptEngineArg(QScriptEngine*)"); m_myObject->emitMySignalWithScriptEngineArg(m_engine); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QVERIFY(m_engine->evaluate("signalArgs[0]").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignalWithScriptEngineArg.disconnect(myHandler)").isUndefined()); // signal with QVariant arg: QVariant should be unwrapped only once m_myObject->clearConnectNotifySignals(); QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.connect(myHandler)").isUndefined()); QVERIFY(m_myObject->hasSingleConnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignalWithVariantArg))); m_engine->evaluate("gotSignal = false"); QVariant tmp(123); QVariant signalArg(QMetaType::QVariant, &tmp); m_myObject->emitMySignalWithVariantArg(signalArg); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QVERIFY(m_engine->evaluate("signalArgs[0]").isVariant()); QCOMPARE(m_engine->evaluate("signalArgs[0]").toVariant().toDouble(), 123.0); QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.disconnect(myHandler)").isUndefined()); // signal with QVariant arg: with an invalid QVariant m_myObject->clearConnectNotifySignals(); QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.connect(myHandler)").isUndefined()); QVERIFY(m_myObject->hasSingleConnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignalWithVariantArg))); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignalWithVariantArg(QVariant()); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QVERIFY(m_engine->evaluate("signalArgs[0]").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.disconnect(myHandler)").isUndefined()); // signal with argument type that's unknown to the meta-type system m_myObject->clearConnectNotifySignals(); QVERIFY(m_engine->evaluate("myObject.mySignalWithScriptEngineArg.connect(myHandler)").isUndefined()); QVERIFY(m_myObject->hasSingleConnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignalWithScriptEngineArg))); m_engine->evaluate("gotSignal = false"); QTest::ignoreMessage(QtWarningMsg, "QScriptEngine: Unable to handle unregistered datatype 'QScriptEngine*' when invoking handler of signal MyQObject::mySignalWithScriptEngineArg(QScriptEngine*)"); m_myObject->emitMySignalWithScriptEngineArg(m_engine); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QVERIFY(m_engine->evaluate("signalArgs[0]").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignalWithScriptEngineArg.disconnect(myHandler)").isUndefined()); // connect(object, function) m_engine->evaluate("otherObject = { name:'foo' }"); QVERIFY(m_engine->evaluate("myObject.mySignal.connect(otherObject, myHandler)").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(otherObject, myHandler)").isUndefined()); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignal(); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), false); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(otherObject, myHandler)").isError()); QVERIFY(m_engine->evaluate("myObject.mySignal.connect(otherObject, myHandler)").isUndefined()); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignal(); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 0.0); QVERIFY(m_engine->evaluate("slotThisObject").strictlyEquals(m_engine->evaluate("otherObject"))); QCOMPARE(m_engine->evaluate("slotThisObject").property("name").toString(), QLatin1String("foo")); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(otherObject, myHandler)").isUndefined()); m_engine->evaluate("yetAnotherObject = { name:'bar', func : function() { } }"); QVERIFY(m_engine->evaluate("myObject.mySignal2.connect(yetAnotherObject, myHandler)").isUndefined()); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignal2(true); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QVERIFY(m_engine->evaluate("slotThisObject").strictlyEquals(m_engine->evaluate("yetAnotherObject"))); QCOMPARE(m_engine->evaluate("slotThisObject").property("name").toString(), QLatin1String("bar")); QVERIFY(m_engine->evaluate("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignal2.connect(myObject, myHandler)").isUndefined()); m_engine->evaluate("gotSignal = false"); m_myObject->emitMySignal2(true); QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true); QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0); QCOMPARE(m_engine->evaluate("slotThisObject").toQObject(), (QObject *)m_myObject); QVERIFY(m_engine->evaluate("myObject.mySignal2.disconnect(myObject, myHandler)").isUndefined()); // connect(obj, string) QVERIFY(m_engine->evaluate("myObject.mySignal.connect(yetAnotherObject, 'func')").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject, 'mySlot')").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(yetAnotherObject, 'func')").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myObject, 'mySlot')").isUndefined()); } void tst_QScriptExtQObject::connectAndDisconnect_emitFromJS() { // no arguments QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject.mySlot)").isUndefined()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.mySignal()").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 20); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myObject.mySlot)").isUndefined()); // one argument QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)").isUndefined()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(123)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 21); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)").isUndefined()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(123)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 22); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)").isUndefined()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(123)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 23); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123")); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)").isUndefined()); // connecting to overloaded slot QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)").isUndefined()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(123)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])").isUndefined()); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(456)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])").isUndefined()); } void tst_QScriptExtQObject::connectAndDisconnect_senderWrapperCollected() { // when the wrapper dies, the connection stays alive QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject.mySlot)").isUndefined()); m_myObject->resetQtFunctionInvoked(); m_myObject->emitMySignal(); QCOMPARE(m_myObject->qtFunctionInvoked(), 20); m_engine->evaluate("myObject = null"); m_engine->collectGarbage(); m_myObject->resetQtFunctionInvoked(); m_myObject->emitMySignal(); QCOMPARE(m_myObject->qtFunctionInvoked(), 20); } void tst_QScriptExtQObject::connectAndDisconnectWithBadArgs() { { QScriptValue ret = m_engine->evaluate("(function() { }).connect()"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.connect: no arguments given")); } { QScriptValue ret = m_engine->evaluate("var o = { }; o.connect = Function.prototype.connect; o.connect()"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.connect: no arguments given")); } { QScriptValue ret = m_engine->evaluate("(function() { }).connect(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: this object is not a signal")); } { QScriptValue ret = m_engine->evaluate("var o = { }; o.connect = Function.prototype.connect; o.connect(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: this object is not a signal")); } { QScriptValue ret = m_engine->evaluate("myObject.myInvokable.connect(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: MyQObject::myInvokable() is not a signal")); } { QScriptValue ret = m_engine->evaluate("myObject.myInvokable.connect(function() { })"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: MyQObject::myInvokable() is not a signal")); } { QScriptValue ret = m_engine->evaluate("myObject.mySignal.connect(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: target is not a function")); } { QScriptValue ret = m_engine->evaluate("(function() { }).disconnect()"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.disconnect: no arguments given")); } { QScriptValue ret = m_engine->evaluate("var o = { }; o.disconnect = Function.prototype.disconnect; o.disconnect()"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.disconnect: no arguments given")); } { QScriptValue ret = m_engine->evaluate("(function() { }).disconnect(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: this object is not a signal")); } { QScriptValue ret = m_engine->evaluate("var o = { }; o.disconnect = Function.prototype.disconnect; o.disconnect(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: this object is not a signal")); } { QScriptValue ret = m_engine->evaluate("myObject.myInvokable.disconnect(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: MyQObject::myInvokable() is not a signal")); } { QScriptValue ret = m_engine->evaluate("myObject.myInvokable.disconnect(function() { })"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: MyQObject::myInvokable() is not a signal")); } { QScriptValue ret = m_engine->evaluate("myObject.mySignal.disconnect(123)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: target is not a function")); } { QScriptValue ret = m_engine->evaluate("myObject.mySignal.disconnect(function() { })"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.disconnect: failed to disconnect from MyQObject::mySignal()")); } } void tst_QScriptExtQObject::connectAndDisconnect_senderDeleted() { QScriptEngine eng; QObject *obj = new QObject; eng.globalObject().setProperty("obj", eng.newQObject(obj)); eng.evaluate("signal = obj.destroyed"); delete obj; { QScriptValue ret = eng.evaluate("signal.connect(function(){})"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Function.prototype.connect: cannot connect to deleted QObject")); } { QScriptValue ret = eng.evaluate("signal.disconnect(function(){})"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Function.prototype.discconnect: cannot disconnect from deleted QObject")); } } void tst_QScriptExtQObject::cppConnectAndDisconnect() { QScriptEngine eng; QLineEdit edit; QLineEdit edit2; QScriptValue fun = eng.evaluate("function fun(text) { signalObject = this; signalArg = text; }; fun"); QVERIFY(fun.isFunction()); for (int z = 0; z < 2; ++z) { QScriptValue receiver; if (z == 0) receiver = QScriptValue(); else receiver = eng.newObject(); for (int y = 0; y < 2; ++y) { QVERIFY(qScriptConnect(&edit, SIGNAL(textChanged(const QString &)), receiver, fun)); QVERIFY(qScriptConnect(&edit2, SIGNAL(textChanged(const QString &)), receiver, fun)); // check signal emission for (int x = 0; x < 4; ++x) { QLineEdit *ed = (x < 2) ? &edit : &edit2; ed->setText((x % 2) ? "foo" : "bar"); { QScriptValue ret = eng.globalObject().property("signalObject"); if (receiver.isObject()) QVERIFY(ret.strictlyEquals(receiver)); else QVERIFY(ret.strictlyEquals(eng.globalObject())); } { QScriptValue ret = eng.globalObject().property("signalArg"); QVERIFY(ret.isString()); QCOMPARE(ret.toString(), ed->text()); } eng.collectGarbage(); } // check disconnect QVERIFY(qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), receiver, fun)); eng.globalObject().setProperty("signalObject", QScriptValue()); eng.globalObject().setProperty("signalArg", QScriptValue()); edit.setText("something else"); QVERIFY(!eng.globalObject().property("signalObject").isValid()); QVERIFY(!eng.globalObject().property("signalArg").isValid()); QVERIFY(!qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), receiver, fun)); // other object's connection should remain edit2.setText(edit.text()); { QScriptValue ret = eng.globalObject().property("signalObject"); if (receiver.isObject()) QVERIFY(ret.strictlyEquals(receiver)); else QVERIFY(ret.strictlyEquals(eng.globalObject())); } { QScriptValue ret = eng.globalObject().property("signalArg"); QVERIFY(ret.isString()); QCOMPARE(ret.toString(), edit2.text()); } // disconnect other object too QVERIFY(qScriptDisconnect(&edit2, SIGNAL(textChanged(const QString &)), receiver, fun)); eng.globalObject().setProperty("signalObject", QScriptValue()); eng.globalObject().setProperty("signalArg", QScriptValue()); edit2.setText("even more different"); QVERIFY(!eng.globalObject().property("signalObject").isValid()); QVERIFY(!eng.globalObject().property("signalArg").isValid()); QVERIFY(!qScriptDisconnect(&edit2, SIGNAL(textChanged(const QString &)), receiver, fun)); } } } void tst_QScriptExtQObject::cppConnectAndDisconnect2() { QScriptEngine eng; QLineEdit edit; QLineEdit edit2; QScriptValue fun = eng.evaluate("function fun(text) { signalObject = this; signalArg = text; }; fun"); // make sure we don't crash when engine is deleted { QScriptEngine *eng2 = new QScriptEngine; QScriptValue fun2 = eng2->evaluate("(function(text) { signalObject = this; signalArg = text; })"); QVERIFY(fun2.isFunction()); QVERIFY(qScriptConnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun2)); delete eng2; edit.setText("ciao"); QVERIFY(!qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun2)); } // mixing script-side and C++-side connect { eng.globalObject().setProperty("edit", eng.newQObject(&edit)); QVERIFY(eng.evaluate("edit.textChanged.connect(fun)").isUndefined()); QVERIFY(qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun)); QVERIFY(qScriptConnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun)); QVERIFY(eng.evaluate("edit.textChanged.disconnect(fun)").isUndefined()); } // signalHandlerException() { connect(&eng, SIGNAL(signalHandlerException(QScriptValue)), this, SLOT(onSignalHandlerException(QScriptValue))); eng.globalObject().setProperty("edit", eng.newQObject(&edit)); QScriptValue fun = eng.evaluate("(function() { nonExistingFunction(); })"); QVERIFY(fun.isFunction()); QVERIFY(qScriptConnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun)); m_signalHandlerException = QScriptValue(); QScriptValue ret = eng.evaluate("edit.text = 'trigger a signal handler exception from script'"); QVERIFY(ret.isError()); QVERIFY(m_signalHandlerException.strictlyEquals(ret)); m_signalHandlerException = QScriptValue(); edit.setText("trigger a signal handler exception from C++"); QVERIFY(m_signalHandlerException.isError()); QVERIFY(qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun)); m_signalHandlerException = QScriptValue(); eng.evaluate("edit.text = 'no more exception from script'"); QVERIFY(!m_signalHandlerException.isValid()); edit.setText("no more exception from C++"); QVERIFY(!m_signalHandlerException.isValid()); disconnect(&eng, SIGNAL(signalHandlerException(QScriptValue)), this, SLOT(onSignalHandlerException(QScriptValue))); } // check that connectNotify() and disconnectNotify() are called (task 232987) { m_myObject->clearConnectNotifySignals(); QVERIFY(qScriptConnect(m_myObject, SIGNAL(mySignal()), QScriptValue(), fun)); QCOMPARE(m_myObject->connectNotifySignals().size(), 2); QVERIFY(m_myObject->hasConnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignal))); // We get a destroyed() connection as well, used internally by Qt Script QVERIFY(m_myObject->hasConnectNotifySignal(QMetaMethod::fromSignal(&QObject::destroyed))); m_myObject->clearDisconnectNotifySignals(); QVERIFY(qScriptDisconnect(m_myObject, SIGNAL(mySignal()), QScriptValue(), fun)); QVERIFY(m_myObject->hasSingleDisconnectNotifySignal(QMetaMethod::fromSignal(&MyQObject::mySignal))); } // bad args QVERIFY(!qScriptConnect(0, SIGNAL(foo()), QScriptValue(), fun)); QVERIFY(!qScriptConnect(&edit, 0, QScriptValue(), fun)); QVERIFY(!qScriptConnect(&edit, SIGNAL(foo()), QScriptValue(), fun)); QVERIFY(!qScriptConnect(&edit, SIGNAL(textChanged(QString)), QScriptValue(), QScriptValue())); QVERIFY(!qScriptDisconnect(0, SIGNAL(foo()), QScriptValue(), fun)); QVERIFY(!qScriptDisconnect(&edit, 0, QScriptValue(), fun)); QVERIFY(!qScriptDisconnect(&edit, SIGNAL(foo()), QScriptValue(), fun)); QVERIFY(!qScriptDisconnect(&edit, SIGNAL(textChanged(QString)), QScriptValue(), QScriptValue())); { QScriptEngine eng2; QScriptValue receiverInDifferentEngine = eng2.newObject(); QVERIFY(!qScriptConnect(&edit, SIGNAL(textChanged(QString)), receiverInDifferentEngine, fun)); QVERIFY(!qScriptDisconnect(&edit, SIGNAL(textChanged(QString)), receiverInDifferentEngine, fun)); } } void tst_QScriptExtQObject::classEnums() { QScriptValue myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue()); m_engine->globalObject().setProperty("MyQObject", myClass); QVERIFY(m_engine->evaluate("MyQObject.FooPolicy").isNumber()); // no strong typing QCOMPARE(static_cast(m_engine->evaluate("MyQObject.FooPolicy").toInt32()), MyQObject::FooPolicy); QCOMPARE(static_cast(m_engine->evaluate("MyQObject.BarPolicy").toInt32()), MyQObject::BarPolicy); QCOMPARE(static_cast(m_engine->evaluate("MyQObject.BazPolicy").toInt32()), MyQObject::BazPolicy); QCOMPARE(static_cast(m_engine->evaluate("MyQObject.FooStrategy").toInt32()), MyQObject::FooStrategy); QCOMPARE(static_cast(m_engine->evaluate("MyQObject.BarStrategy").toInt32()), MyQObject::BarStrategy); QCOMPARE(static_cast(m_engine->evaluate("MyQObject.BazStrategy").toInt32()), MyQObject::BazStrategy); QVERIFY(m_engine->evaluate("MyQObject.NoAbility").isNumber()); // no strong typing QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.NoAbility").toInt32()), MyQObject::NoAbility); QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.FooAbility").toInt32()), MyQObject::FooAbility); QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.BarAbility").toInt32()), MyQObject::BarAbility); QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.BazAbility").toInt32()), MyQObject::BazAbility); QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.AllAbility").toInt32()), MyQObject::AllAbility); // Constructors for flags are not provided QVERIFY(m_engine->evaluate("MyQObject.Ability").isUndefined()); QScriptValue::PropertyFlags expectedEnumFlags = QScriptValue::ReadOnly | QScriptValue::Undeletable; QCOMPARE(myClass.propertyFlags("FooPolicy"), expectedEnumFlags); QCOMPARE(myClass.propertyFlags("BarPolicy"), expectedEnumFlags); QCOMPARE(myClass.propertyFlags("BazPolicy"), expectedEnumFlags); // enums from Qt are inherited through prototype QCOMPARE(static_cast(m_engine->evaluate("MyQObject.StrongFocus").toInt32()), Qt::StrongFocus); QCOMPARE(static_cast(m_engine->evaluate("MyQObject.Key_Left").toInt32()), Qt::Key_Left); QCOMPARE(m_engine->evaluate("MyQObject.className()").toString(), QLatin1String("MyQObject")); qRegisterMetaType("Policy"); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 10); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithEnumArg('BarPolicy')").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 10); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BarPolicy)); m_myObject->resetQtFunctionInvoked(); QVERIFY(m_engine->evaluate("myObject.myInvokableWithEnumArg('NoSuchPolicy')").isError()); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); m_myObject->resetQtFunctionInvoked(); QCOMPARE(m_engine->evaluate("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)").isUndefined(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 36); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningEnum()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 37); QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); QCOMPARE(ret.isVariant(), true); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningQualifiedEnum()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 38); QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); QCOMPARE(ret.isNumber(), true); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithFlagsArg(MyQObject.FooAbility)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 58); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::FooAbility)); QCOMPARE(ret.isNumber(), true); QCOMPARE(ret.toInt32(), int(MyQObject::FooAbility)); } m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQualifiedFlagsArg(MyQObject.BarAbility)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 59); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BarAbility)); QCOMPARE(ret.isNumber(), true); QCOMPARE(ret.toInt32(), int(MyQObject::BarAbility)); } // enum properties are not deletable or writable QVERIFY(!m_engine->evaluate("delete MyQObject.BazPolicy").toBool()); myClass.setProperty("BazPolicy", QScriptValue()); QCOMPARE(static_cast(myClass.property("BazPolicy").toInt32()), MyQObject::BazPolicy); myClass.setProperty("BazPolicy", MyQObject::FooPolicy); QCOMPARE(static_cast(myClass.property("BazPolicy").toInt32()), MyQObject::BazPolicy); } QT_BEGIN_NAMESPACE Q_SCRIPT_DECLARE_QMETAOBJECT(MyQObject, QObject*) Q_SCRIPT_DECLARE_QMETAOBJECT(QObject, QObject*) QT_END_NAMESPACE class ConstructorTest : public QObject { Q_OBJECT public: Q_INVOKABLE ConstructorTest(QObject *parent) : QObject(parent) { setProperty("ctorIndex", 0); } Q_INVOKABLE ConstructorTest(int arg, QObject *parent = 0) : QObject(parent) { setProperty("ctorIndex", 1); setProperty("arg", arg); } Q_INVOKABLE ConstructorTest(const QString &arg, QObject *parent = 0) : QObject(parent) { setProperty("ctorIndex", 2); setProperty("arg", arg); } Q_INVOKABLE ConstructorTest(int arg, const QString &arg2, QObject *parent = 0) : QObject(parent) { setProperty("ctorIndex", 3); setProperty("arg", arg); setProperty("arg2", arg2); } Q_INVOKABLE ConstructorTest(const QBrush &arg, QObject *parent = 0) : QObject(parent) { setProperty("ctorIndex", 4); setProperty("arg", arg); } }; void tst_QScriptExtQObject::classConstructor() { QScriptValue myClass = qScriptValueFromQMetaObject(m_engine); m_engine->globalObject().setProperty("MyQObject", myClass); QScriptValue myObj = m_engine->evaluate("myObj = MyQObject()"); QObject *qobj = myObj.toQObject(); QVERIFY(qobj != 0); QCOMPARE(qobj->metaObject()->className(), "MyQObject"); QCOMPARE(qobj->parent(), (QObject *)0); QScriptValue qobjectClass = qScriptValueFromQMetaObject(m_engine); m_engine->globalObject().setProperty("QObject", qobjectClass); QScriptValue otherObj = m_engine->evaluate("otherObj = QObject(myObj)"); QObject *qqobj = otherObj.toQObject(); QVERIFY(qqobj != 0); QCOMPARE(qqobj->metaObject()->className(), "QObject"); QCOMPARE(qqobj->parent(), qobj); delete qobj; // Q_INVOKABLE constructors { QScriptValue klazz = m_engine->newQMetaObject(&ConstructorTest::staticMetaObject); { QScriptValue obj = klazz.construct(); QVERIFY(obj.isError()); QCOMPARE(obj.toString(), QString::fromLatin1("SyntaxError: too few arguments in call to ConstructorTest(); candidates are\n" " ConstructorTest(QBrush)\n" " ConstructorTest(QBrush,QObject*)\n" " ConstructorTest(int,QString)\n" " ConstructorTest(int,QString,QObject*)\n" " ConstructorTest(QString)\n" " ConstructorTest(QString,QObject*)\n" " ConstructorTest(int)\n" " ConstructorTest(int,QObject*)\n" " ConstructorTest(QObject*)")); } { QObject objobj; QScriptValue arg = m_engine->newQObject(&objobj); QScriptValue obj = klazz.construct(QScriptValueList() << arg); QVERIFY(!obj.isError()); QVERIFY(obj.instanceOf(klazz)); QVERIFY(obj.property("ctorIndex").isNumber()); QCOMPARE(obj.property("ctorIndex").toInt32(), 0); } { int arg = 123; QScriptValue obj = klazz.construct(QScriptValueList() << arg); QVERIFY(!obj.isError()); QVERIFY(obj.instanceOf(klazz)); QVERIFY(obj.property("ctorIndex").isNumber()); QCOMPARE(obj.property("ctorIndex").toInt32(), 1); QVERIFY(obj.property("arg").isNumber()); QCOMPARE(obj.property("arg").toInt32(), arg); } { QString arg = "foo"; QScriptValue obj = klazz.construct(QScriptValueList() << arg); QVERIFY(!obj.isError()); QVERIFY(obj.instanceOf(klazz)); QVERIFY(obj.property("ctorIndex").isNumber()); QCOMPARE(obj.property("ctorIndex").toInt32(), 2); QVERIFY(obj.property("arg").isString()); QCOMPARE(obj.property("arg").toString(), arg); } { int arg = 123; QString arg2 = "foo"; QScriptValue obj = klazz.construct(QScriptValueList() << arg << arg2); QVERIFY(!obj.isError()); QVERIFY(obj.instanceOf(klazz)); QVERIFY(obj.property("ctorIndex").isNumber()); QCOMPARE(obj.property("ctorIndex").toInt32(), 3); QVERIFY(obj.property("arg").isNumber()); QCOMPARE(obj.property("arg").toInt32(), arg); QVERIFY(obj.property("arg2").isString()); QCOMPARE(obj.property("arg2").toString(), arg2); } { QBrush arg(Qt::red); QScriptValue obj = klazz.construct(QScriptValueList() << qScriptValueFromValue(m_engine, arg)); QVERIFY(!obj.isError()); QVERIFY(obj.instanceOf(klazz)); QVERIFY(obj.property("ctorIndex").isNumber()); QCOMPARE(obj.property("ctorIndex").toInt32(), 4); QVERIFY(obj.property("arg").isVariant()); QCOMPARE(qvariant_cast(obj.property("arg").toVariant()), arg); } { QDir arg; QScriptValue obj = klazz.construct(QScriptValueList() << qScriptValueFromValue(m_engine, arg)); QVERIFY(obj.isError()); QCOMPARE(obj.toString(), QString::fromLatin1("TypeError: ambiguous call of overloaded function ConstructorTest(); candidates were\n" " ConstructorTest(int)\n" " ConstructorTest(QString)")); } } } void tst_QScriptExtQObject::overrideInvokable() { m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myInvokable()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 0); m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myInvokable = function() { global.a = 123; }"); m_engine->evaluate("myObject.myInvokable()"); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); QCOMPARE(m_engine->evaluate("global.a").toNumber(), 123.0); m_engine->evaluate("myObject.myInvokable = function() { global.a = 456; }"); m_engine->evaluate("myObject.myInvokable()"); QCOMPARE(m_myObject->qtFunctionInvoked(), -1); QCOMPARE(m_engine->evaluate("global.a").toNumber(), 456.0); m_engine->evaluate("delete myObject.myInvokable"); m_engine->evaluate("myObject.myInvokable()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 0); m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myInvokable = myObject.myInvokableWithIntArg"); m_engine->evaluate("myObject.myInvokable(123)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 1); m_engine->evaluate("delete myObject.myInvokable"); m_myObject->resetQtFunctionInvoked(); // this form (with the '()') is read-only m_engine->evaluate("myObject['myInvokable()'] = function() { global.a = 123; }"); m_engine->evaluate("myObject.myInvokable()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 0); } void tst_QScriptExtQObject::transferInvokable() { m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.foozball = myObject.myInvokable"); m_engine->evaluate("myObject.foozball()"); QCOMPARE(m_myObject->qtFunctionInvoked(), 0); m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.foozball = myObject.myInvokableWithIntArg"); m_engine->evaluate("myObject.foozball(123)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 1); m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myInvokable = myObject.myInvokableWithIntArg"); m_engine->evaluate("myObject.myInvokable(123)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 1); MyOtherQObject other; m_engine->globalObject().setProperty( "myOtherObject", m_engine->newQObject(&other)); m_engine->evaluate("myOtherObject.foo = myObject.foozball"); other.resetQtFunctionInvoked(); m_engine->evaluate("myOtherObject.foo(456)"); QCOMPARE(other.qtFunctionInvoked(), 1); } void tst_QScriptExtQObject::findChild() { QObject *child = new QObject(m_myObject); child->setObjectName(QLatin1String("myChildObject")); { QScriptValue result = m_engine->evaluate("myObject.findChild('noSuchChild')"); QCOMPARE(result.isNull(), true); } { QScriptValue result = m_engine->evaluate("myObject.findChild('myChildObject')"); QCOMPARE(result.isQObject(), true); QCOMPARE(result.toQObject(), child); } delete child; } void tst_QScriptExtQObject::findChildren() { QObject *child = new QObject(m_myObject); child->setObjectName(QLatin1String("myChildObject")); { QScriptValue result = m_engine->evaluate("myObject.findChildren('noSuchChild')"); QCOMPARE(result.isArray(), true); QCOMPARE(result.property(QLatin1String("length")).toNumber(), 0.0); } { QScriptValue result = m_engine->evaluate("myObject.findChildren('myChildObject')"); QCOMPARE(result.isArray(), true); QCOMPARE(result.property(QLatin1String("length")).toNumber(), 1.0); QCOMPARE(result.property(QLatin1String("0")).toQObject(), child); } QObject *namelessChild = new QObject(m_myObject); { QScriptValue result = m_engine->evaluate("myObject.findChildren('myChildObject')"); QCOMPARE(result.isArray(), true); QCOMPARE(result.property(QLatin1String("length")).toNumber(), 1.0); QCOMPARE(result.property(QLatin1String("0")).toQObject(), child); } QObject *anotherChild = new QObject(m_myObject); anotherChild->setObjectName(QLatin1String("anotherChildObject")); { QScriptValue result = m_engine->evaluate("myObject.findChildren('anotherChildObject')"); QCOMPARE(result.isArray(), true); QCOMPARE(result.property(QLatin1String("length")).toNumber(), 1.0); QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild); } anotherChild->setObjectName(QLatin1String("myChildObject")); { QScriptValue result = m_engine->evaluate("myObject.findChildren('myChildObject')"); QCOMPARE(result.isArray(), true); QCOMPARE(result.property(QLatin1String("length")).toNumber(), 2.0); QObject *o1 = result.property(QLatin1String("0")).toQObject(); QObject *o2 = result.property(QLatin1String("1")).toQObject(); if (o1 != child) { QCOMPARE(o1, anotherChild); QCOMPARE(o2, child); } else { QCOMPARE(o1, child); QCOMPARE(o2, anotherChild); } } // find all { QScriptValue result = m_engine->evaluate("myObject.findChildren()"); QVERIFY(result.isArray()); int count = 3; QCOMPARE(result.property("length").toInt32(), count); for (int i = 0; i < 3; ++i) { QObject *o = result.property(i).toQObject(); if (o == namelessChild || o == child || o == anotherChild) --count; } QVERIFY(count == 0); } // matchall regexp { QScriptValue result = m_engine->evaluate("myObject.findChildren(/.*/)"); QVERIFY(result.isArray()); int count = 3; QCOMPARE(result.property("length").toInt32(), count); for (int i = 0; i < 3; ++i) { QObject *o = result.property(i).toQObject(); if (o == namelessChild || o == child || o == anotherChild) --count; } QVERIFY(count == 0); } // matchall regexp my* { QScriptValue result = m_engine->evaluate("myObject.findChildren(new RegExp(\"^my.*\"))"); QCOMPARE(result.isArray(), true); QCOMPARE(result.property(QLatin1String("length")).toNumber(), 2.0); QObject *o1 = result.property(QLatin1String("0")).toQObject(); QObject *o2 = result.property(QLatin1String("1")).toQObject(); if (o1 != child) { QCOMPARE(o1, anotherChild); QCOMPARE(o2, child); } else { QCOMPARE(o1, child); QCOMPARE(o2, anotherChild); } } delete anotherChild; delete namelessChild; delete child; } void tst_QScriptExtQObject::childObjects() { QObject *child1 = new QObject(m_myObject); child1->setObjectName("child1"); QObject *child2 = new QObject(m_myObject); QScriptValue wrapped = m_engine->newQObject(m_myObject); QVERIFY(wrapped.property("child1").isQObject()); QCOMPARE(wrapped.property("child1").toQObject(), child1); QVERIFY(!wrapped.property("child2").isQObject()); QVERIFY(!wrapped.property("child2").isValid()); // Setting the name later child2->setObjectName("child2"); QVERIFY(wrapped.property("child1").isQObject()); QCOMPARE(wrapped.property("child1").toQObject(), child1); QVERIFY(wrapped.property("child2").isQObject()); QCOMPARE(wrapped.property("child2").toQObject(), child2); // Adding a child later QObject *child3 = new QObject(m_myObject); child3->setObjectName("child3"); QVERIFY(wrapped.property("child3").isQObject()); QCOMPARE(wrapped.property("child3").toQObject(), child3); // Changing a child name child1->setObjectName("anotherName"); QVERIFY(!wrapped.property("child1").isValid()); QVERIFY(wrapped.property("anotherName").isQObject()); QCOMPARE(wrapped.property("anotherName").toQObject(), child1); // Creating another object wrapper excluding child from // properties. QScriptValue wrapped2 = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeChildObjects); QVERIFY(!wrapped2.property("anotherName").isValid()); QVERIFY(!wrapped2.property("child2").isValid()); QVERIFY(!wrapped2.property("child3").isValid()); } void tst_QScriptExtQObject::overloadedSlots() { // should pick myOverloadedSlot(double) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot(10)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // should pick myOverloadedSlot(double) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot(10.0)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // should pick myOverloadedSlot(QString) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot('10')"); QCOMPARE(m_myObject->qtFunctionInvoked(), 29); // should pick myOverloadedSlot(bool) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot(true)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 25); // should pick myOverloadedSlot(QDateTime) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot(new Date())"); QCOMPARE(m_myObject->qtFunctionInvoked(), 32); // should pick myOverloadedSlot(QRegExp) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot(new RegExp())"); QCOMPARE(m_myObject->qtFunctionInvoked(), 34); // should pick myOverloadedSlot(QVariant) m_myObject->resetQtFunctionInvoked(); QScriptValue f = m_engine->evaluate("myObject.myOverloadedSlot"); f.call(QScriptValue(), QScriptValueList() << m_engine->newVariant(QVariant("ciao"))); QCOMPARE(m_myObject->qtFunctionInvoked(), 35); // should pick myOverloadedSlot(QObject*) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot(myObject)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 41); // should pick myOverloadedSlot(QObject*) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot(null)"); QCOMPARE(m_myObject->qtFunctionInvoked(), 41); // should pick myOverloadedSlot(QStringList) m_myObject->resetQtFunctionInvoked(); m_engine->evaluate("myObject.myOverloadedSlot(['hello'])"); QCOMPARE(m_myObject->qtFunctionInvoked(), 42); } void tst_QScriptExtQObject::enumerate_data() { QTest::addColumn("wrapOptions"); QTest::addColumn("expectedNames"); QTest::newRow( "enumerate all" ) << 0 << (QStringList() // meta-object-defined properties: // inherited << "objectName" // non-inherited << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3" // inherited signals << "destroyed(QObject*)" << "destroyed()" << "objectNameChanged(QString)" // inherited slots << "deleteLater()" // not included because it's private: // << "_q_reregisterTimers(void*)" // signals << "mySignal()" // slots << "mySlot()" << "myOtherSlot()"); QTest::newRow( "don't enumerate inherited properties" ) << int(QScriptEngine::ExcludeSuperClassProperties) << (QStringList() // meta-object-defined properties: // non-inherited << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3" // inherited signals << "destroyed(QObject*)" << "destroyed()" << "objectNameChanged(QString)" // inherited slots << "deleteLater()" // not included because it's private: // << "_q_reregisterTimers(void*)" // signals << "mySignal()" // slots << "mySlot()" << "myOtherSlot()"); QTest::newRow( "don't enumerate inherited methods" ) << int(QScriptEngine::ExcludeSuperClassMethods) << (QStringList() // meta-object-defined properties: // inherited << "objectName" // non-inherited << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3" // signals << "mySignal()" // slots << "mySlot()" << "myOtherSlot()"); QTest::newRow( "don't enumerate inherited members" ) << int(QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties) << (QStringList() // meta-object-defined properties << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3" // signals << "mySignal()" // slots << "mySlot()" << "myOtherSlot()"); QTest::newRow( "enumerate properties, not methods" ) << int(QScriptEngine::SkipMethodsInEnumeration) << (QStringList() // meta-object-defined properties: // inherited << "objectName" // non-inherited << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3"); QTest::newRow( "don't enumerate inherited properties + methods" ) << int(QScriptEngine::ExcludeSuperClassProperties | QScriptEngine::SkipMethodsInEnumeration) << (QStringList() // meta-object-defined properties: // non-inherited << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3"); QTest::newRow( "don't enumerate inherited members" ) << int(QScriptEngine::ExcludeSuperClassContents) << (QStringList() // meta-object-defined properties << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3" // signals << "mySignal()" // slots << "mySlot()" << "myOtherSlot()"); QTest::newRow( "don't enumerate deleteLater()" ) << int(QScriptEngine::ExcludeDeleteLater) << (QStringList() // meta-object-defined properties: // inherited << "objectName" // non-inherited << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3" // inherited signals << "destroyed(QObject*)" << "destroyed()" << "objectNameChanged(QString)" // not included because it's private: // << "_q_reregisterTimers(void*)" // signals << "mySignal()" // slots << "mySlot()" << "myOtherSlot()"); QTest::newRow( "don't enumerate slots" ) << int(QScriptEngine::ExcludeSlots) << (QStringList() // meta-object-defined properties: // inherited << "objectName" // non-inherited << "p1" << "p2" << "p4" << "p6" // dynamic properties << "dp1" << "dp2" << "dp3" // inherited signals << "destroyed(QObject*)" << "destroyed()" << "objectNameChanged(QString)" // signals << "mySignal()"); } // Message for easily identifying mismatches in string list. static QByteArray msgEnumerationFail(const QStringList &actual, const QStringList &expected) { QString result; QTextStream str(&result); str << "\nActual " << actual.size() << ":\n"; for (int i = 0; i < actual.size(); ++i) { const int index = expected.indexOf(actual.at(i)); if (index < 0) str << "*** "; str << " #" << i << " '"<< actual.at(i) << "'\tin expected at: " << index << '\n'; } str << "Expected " << expected.size() << ":\n"; for (int i = 0; i < expected.size(); ++i) { const int index = actual.indexOf(expected.at(i)); if (index < 0) str << "*** "; str << " #" << i << " '"<< expected.at(i) << "'\t in actual at: " << index << '\n'; } return result.toLocal8Bit(); } void tst_QScriptExtQObject::enumerate() { QFETCH( int, wrapOptions ); QFETCH( QStringList, expectedNames ); QScriptEngine eng; MyEnumTestQObject enumQObject; // give it some dynamic properties enumQObject.setProperty("dp1", "dp1"); enumQObject.setProperty("dp2", "dp2"); enumQObject.setProperty("dp3", "dp3"); QScriptValue obj = eng.newQObject(&enumQObject, QScriptEngine::QtOwnership, QScriptEngine::QObjectWrapOptions(wrapOptions)); // enumerate in script { eng.globalObject().setProperty("myEnumObject", obj); eng.evaluate("var enumeratedProperties = []"); eng.evaluate("for (var p in myEnumObject) { enumeratedProperties.push(p); }"); QStringList result = qscriptvalue_cast(eng.evaluate("enumeratedProperties")); QVERIFY2(result == expectedNames, msgEnumerationFail(result, expectedNames).constData()); } // enumerate in C++ { QScriptValueIterator it(obj); QStringList result; while (it.hasNext()) { it.next(); QCOMPARE(it.flags(), obj.propertyFlags(it.name())); result.append(it.name()); } QVERIFY2(result == expectedNames, msgEnumerationFail(result, expectedNames).constData()); } } class SpecialEnumTestObject : public QObject { Q_OBJECT // overriding a property in the super-class to make it non-scriptable Q_PROPERTY(QString objectName READ objectName SCRIPTABLE false) public: SpecialEnumTestObject(QObject *parent = 0) : QObject(parent) {} }; class SpecialEnumTestObject2 : public QObject { Q_OBJECT // overriding a property in the super-class to make it non-designable (but still scriptable) Q_PROPERTY(QString objectName READ objectName DESIGNABLE false) public: SpecialEnumTestObject2(QObject *parent = 0) : QObject(parent) {} }; void tst_QScriptExtQObject::enumerateSpecial() { QScriptEngine eng; { SpecialEnumTestObject testObj; QScriptValueIterator it(eng.newQObject(&testObj)); bool objectNameEncountered = false; while (it.hasNext()) { it.next(); if (it.name() == QLatin1String("objectName")) { objectNameEncountered = true; break; } } QVERIFY(!objectNameEncountered); } { SpecialEnumTestObject2 testObj; testObj.setObjectName("foo"); QScriptValueList values; QScriptValueIterator it(eng.newQObject(&testObj)); while (it.hasNext()) { it.next(); if (it.name() == "objectName") values.append(it.value()); } QCOMPARE(values.size(), 1); QCOMPARE(values.at(0).toString(), QString::fromLatin1("foo")); } } void tst_QScriptExtQObject::wrapOptions() { QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false); MyQObject *child = new MyQObject(m_myObject); child->setObjectName("child"); // exclude child objects { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeChildObjects); QCOMPARE(obj.property("child").isValid(), false); obj.setProperty("child", QScriptValue(m_engine, 123)); QCOMPARE(obj.property("child") .strictlyEquals(QScriptValue(m_engine, 123)), true); } // don't auto-create dynamic properties { QScriptValue obj = m_engine->newQObject(m_myObject); QVERIFY(!m_myObject->dynamicPropertyNames().contains("anotherDynamicProperty")); obj.setProperty("anotherDynamicProperty", QScriptValue(m_engine, 123)); QVERIFY(!m_myObject->dynamicPropertyNames().contains("anotherDynamicProperty")); QCOMPARE(obj.property("anotherDynamicProperty") .strictlyEquals(QScriptValue(m_engine, 123)), true); } // auto-create dynamic properties { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::AutoCreateDynamicProperties); QVERIFY(!m_myObject->dynamicPropertyNames().contains("anotherDynamicProperty")); obj.setProperty("anotherDynamicProperty", QScriptValue(m_engine, 123)); QVERIFY(m_myObject->dynamicPropertyNames().contains("anotherDynamicProperty")); QCOMPARE(obj.property("anotherDynamicProperty") .strictlyEquals(QScriptValue(m_engine, 123)), true); // task 236685 { QScriptValue obj2 = m_engine->newObject(); obj2.setProperty("notADynamicProperty", 456); obj.setPrototype(obj2); QScriptValue ret = obj.property("notADynamicProperty"); QVERIFY(ret.isNumber()); QVERIFY(ret.strictlyEquals(obj2.property("notADynamicProperty"))); } } // don't exclude super-class properties { QScriptValue obj = m_engine->newQObject(m_myObject); QVERIFY(obj.property("objectName").isValid()); QVERIFY(obj.propertyFlags("objectName") & QScriptValue::QObjectMember); } // exclude super-class properties { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassProperties); QVERIFY(!obj.property("objectName").isValid()); QVERIFY(!(obj.propertyFlags("objectName") & QScriptValue::QObjectMember)); QVERIFY(obj.property("intProperty").isValid()); QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember); } // don't exclude super-class methods { QScriptValue obj = m_engine->newQObject(m_myObject); QVERIFY(obj.property("deleteLater").isValid()); QVERIFY(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember); } // exclude super-class methods { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassMethods); QVERIFY(!obj.property("deleteLater").isValid()); QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember)); QVERIFY(obj.property("mySlot").isValid()); QVERIFY(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember); } // exclude all super-class contents { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassContents); QVERIFY(!obj.property("deleteLater").isValid()); QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember)); QVERIFY(obj.property("mySlot").isValid()); QVERIFY(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember); QVERIFY(!obj.property("objectName").isValid()); QVERIFY(!(obj.propertyFlags("objectName") & QScriptValue::QObjectMember)); QVERIFY(obj.property("intProperty").isValid()); QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember); } // exclude deleteLater() { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); QVERIFY(!obj.property("deleteLater").isValid()); QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember)); QVERIFY(obj.property("mySlot").isValid()); QVERIFY(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember); QVERIFY(obj.property("objectName").isValid()); QVERIFY(obj.propertyFlags("objectName") & QScriptValue::QObjectMember); QVERIFY(obj.property("intProperty").isValid()); QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember); } // exclude slots { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSlots); QVERIFY(!obj.property("deleteLater").isValid()); QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember)); QVERIFY(!obj.property("mySlot").isValid()); QVERIFY(!(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember)); QVERIFY(obj.property("myInvokable").isFunction()); QVERIFY(obj.propertyFlags("myInvokable") & QScriptValue::QObjectMember); QVERIFY(obj.property("mySignal").isFunction()); QVERIFY(obj.propertyFlags("mySignal") & QScriptValue::QObjectMember); QVERIFY(obj.property("destroyed").isFunction()); QVERIFY(obj.propertyFlags("destroyed") & QScriptValue::QObjectMember); QVERIFY(obj.property("objectName").isValid()); QVERIFY(obj.propertyFlags("objectName") & QScriptValue::QObjectMember); QVERIFY(obj.property("intProperty").isValid()); QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember); } // exclude all that we can { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties | QScriptEngine::ExcludeChildObjects); QVERIFY(!obj.property("deleteLater").isValid()); QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember)); QVERIFY(obj.property("mySlot").isValid()); QVERIFY(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember); QVERIFY(!obj.property("objectName").isValid()); QVERIFY(!(obj.propertyFlags("objectName") & QScriptValue::QObjectMember)); QVERIFY(obj.property("intProperty").isValid()); QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember); QCOMPARE(obj.property("child").isValid(), false); obj.setProperty("child", QScriptValue(m_engine, 123)); QCOMPARE(obj.property("child") .strictlyEquals(QScriptValue(m_engine, 123)), true); } // exclude absolutely all that we can { QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties | QScriptEngine::ExcludeChildObjects | QScriptEngine::ExcludeSlots); QVERIFY(!obj.property("deleteLater").isValid()); QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember)); QVERIFY(!obj.property("mySlot").isValid()); QVERIFY(!(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember)); QVERIFY(obj.property("mySignal").isFunction()); QVERIFY(obj.propertyFlags("mySignal") & QScriptValue::QObjectMember); QVERIFY(obj.property("myInvokable").isFunction()); QVERIFY(obj.propertyFlags("myInvokable") & QScriptValue::QObjectMember); QVERIFY(!obj.property("objectName").isValid()); QVERIFY(!(obj.propertyFlags("objectName") & QScriptValue::QObjectMember)); QVERIFY(obj.property("intProperty").isValid()); QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember); QVERIFY(!obj.property("child").isValid()); } delete child; } Q_DECLARE_METATYPE(QWidget*) Q_DECLARE_METATYPE(QPushButton*) void tst_QScriptExtQObject::prototypes() { QScriptEngine eng; QScriptValue widgetProto = eng.newQObject(new QWidget(), QScriptEngine::ScriptOwnership); eng.setDefaultPrototype(qMetaTypeId(), widgetProto); QPushButton *pbp = new QPushButton(); QScriptValue buttonProto = eng.newQObject(pbp, QScriptEngine::ScriptOwnership); buttonProto.setPrototype(widgetProto); eng.setDefaultPrototype(qMetaTypeId(), buttonProto); QPushButton *pb = new QPushButton(); QScriptValue button = eng.newQObject(pb, QScriptEngine::ScriptOwnership); QVERIFY(button.prototype().strictlyEquals(buttonProto)); buttonProto.setProperty("text", QScriptValue(&eng, "prototype button")); QCOMPARE(pbp->text(), QLatin1String("prototype button")); button.setProperty("text", QScriptValue(&eng, "not the prototype button")); QCOMPARE(pb->text(), QLatin1String("not the prototype button")); QCOMPARE(pbp->text(), QLatin1String("prototype button")); buttonProto.setProperty("objectName", QScriptValue(&eng, "prototype button")); QCOMPARE(pbp->objectName(), QLatin1String("prototype button")); button.setProperty("objectName", QScriptValue(&eng, "not the prototype button")); QCOMPARE(pb->objectName(), QLatin1String("not the prototype button")); QCOMPARE(pbp->objectName(), QLatin1String("prototype button")); } void tst_QScriptExtQObject::objectDeleted() { QScriptEngine eng; MyQObject *qobj = new MyQObject(); QScriptValue v = eng.newQObject(qobj); v.setProperty("objectName", QScriptValue(&eng, "foo")); QCOMPARE(qobj->objectName(), QLatin1String("foo")); v.setProperty("intProperty", QScriptValue(&eng, 123)); QCOMPARE(qobj->intProperty(), 123); qobj->resetQtFunctionInvoked(); QScriptValue invokable = v.property("myInvokable"); invokable.call(v); QCOMPARE(qobj->qtFunctionInvoked(), 0); // now delete the object delete qobj; // the documented behavior is: isQObject() should still return true, // but toQObject() should return 0 QVERIFY(v.isQObject()); QCOMPARE(v.toQObject(), (QObject *)0); // any attempt to access properties of the object should result in an exception { QScriptValue ret = v.property("objectName"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `objectName' of deleted QObject")); } { eng.evaluate("Object"); QVERIFY(!eng.hasUncaughtException()); v.setProperty("objectName", QScriptValue(&eng, "foo")); QVERIFY(eng.hasUncaughtException()); QVERIFY(eng.uncaughtException().isError()); QCOMPARE(eng.uncaughtException().toString(), QLatin1String("Error: cannot access member `objectName' of deleted QObject")); } { QScriptValue ret = v.call(); QVERIFY(!ret.isValid()); } // myInvokableWithIntArg is not stored in member table (since we've not accessed it) { QScriptValue ret = v.property("myInvokableWithIntArg"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject")); } // Meta-method wrappers are still valid, but throw error when called QVERIFY(invokable.isFunction()); { QScriptValue ret = invokable.call(v); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QString::fromLatin1("Error: cannot call function of deleted QObject")); } // access from script eng.globalObject().setProperty("o", v); { QScriptValue ret = eng.evaluate("o()"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("TypeError: Result of expression 'o' [] is not a function.")); } { QScriptValue ret = eng.evaluate("o.objectName"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `objectName' of deleted QObject")); } { QScriptValue ret = eng.evaluate("o.myInvokable()"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `myInvokable' of deleted QObject")); } { QScriptValue ret = eng.evaluate("o.myInvokableWithIntArg(10)"); QVERIFY(ret.isError()); QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject")); } } void tst_QScriptExtQObject::connectToDestroyedSignal() { // ### the following test currently depends on signal emission order #if 0 { // case 1: deleted when the engine is not doing GC QScriptEngine eng; QObject *obj = new QObject(); eng.globalObject().setProperty("o", eng.newQObject(obj, QScriptEngine::QtOwnership)); eng.evaluate("o.destroyed.connect(function() { wasDestroyed = true; })"); eng.evaluate("wasDestroyed = false"); delete obj; QVERIFY(eng.evaluate("wasDestroyed").toBoolean()); } { // case 2: deleted when the engine is doing GC QScriptEngine eng; QObject *obj = new QObject(); eng.globalObject().setProperty("o", eng.newQObject(obj, QScriptEngine::ScriptOwnership)); eng.evaluate("o.destroyed.connect(function() { wasDestroyed = true; })"); eng.evaluate("wasDestroyed = false"); eng.evaluate("o = null"); eng.collectGarbage(); QVERIFY(eng.evaluate("wasDestroyed").toBoolean()); } { // case 3: deleted when the engine is destroyed QScriptEngine eng; QObject *obj = new QObject(); eng.globalObject().setProperty("o", eng.newQObject(obj, QScriptEngine::ScriptOwnership)); eng.evaluate("o.destroyed.connect(function() { })"); // the signal handler won't get called -- we don't want to crash } #endif } void tst_QScriptExtQObject::emitAfterReceiverDeleted() { for (int x = 0; x < 2; ++x) { MyQObject *obj = new MyQObject; QScriptValue scriptObj = m_engine->newQObject(obj); if (x == 0) { // Connecting from JS m_engine->globalObject().setProperty("obj", scriptObj); QVERIFY(m_engine->evaluate("myObject.mySignal.connect(obj, 'mySlot()')").isUndefined()); } else { // Connecting from C++ qScriptConnect(m_myObject, SIGNAL(mySignal()), scriptObj, scriptObj.property("mySlot")); } delete obj; QSignalSpy signalHandlerExceptionSpy(m_engine, SIGNAL(signalHandlerException(QScriptValue))); QVERIFY(!m_engine->hasUncaughtException()); m_myObject->emitMySignal(); QCOMPARE(signalHandlerExceptionSpy.count(), 0); QVERIFY(!m_engine->hasUncaughtException()); } } void tst_QScriptExtQObject::inheritedSlots() { QScriptEngine eng; QPushButton prototypeButton; QScriptValue scriptPrototypeButton = eng.newQObject(&prototypeButton); QPushButton button; QScriptValue scriptButton = eng.newQObject(&button, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSlots); scriptButton.setPrototype(scriptPrototypeButton); QVERIFY(scriptButton.property("click").isFunction()); QVERIFY(scriptButton.property("click").strictlyEquals(scriptPrototypeButton.property("click"))); QSignalSpy prototypeButtonClickedSpy(&prototypeButton, SIGNAL(clicked())); QSignalSpy buttonClickedSpy(&button, SIGNAL(clicked())); scriptButton.property("click").call(scriptButton); QCOMPARE(buttonClickedSpy.count(), 1); QCOMPARE(prototypeButtonClickedSpy.count(), 0); } void tst_QScriptExtQObject::enumerateMetaObject() { QScriptValue myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue()); QStringList expectedNames; expectedNames << "FooPolicy" << "BarPolicy" << "BazPolicy" << "FooStrategy" << "BarStrategy" << "BazStrategy" << "NoAbility" << "FooAbility" << "BarAbility" << "BazAbility" << "AllAbility"; for (int x = 0; x < 2; ++x) { QSet actualNames; if (x == 0) { // From C++ QScriptValueIterator it(myClass); while (it.hasNext()) { it.next(); actualNames.insert(it.name()); } } else { // From JS m_engine->globalObject().setProperty("MyClass", myClass); QScriptValue ret = m_engine->evaluate("a=[]; for (var p in MyClass) if (MyClass.hasOwnProperty(p)) a.push(p); a"); QVERIFY(ret.isArray()); QStringList strings = qscriptvalue_cast(ret); for (int i = 0; i < strings.size(); ++i) actualNames.insert(strings.at(i)); } QCOMPARE(actualNames.size(), expectedNames.size()); for (int i = 0; i < expectedNames.size(); ++i) QVERIFY(actualNames.contains(expectedNames.at(i))); } } void tst_QScriptExtQObject::nestedArrayAsSlotArgument_data() { QTest::addColumn("program"); QTest::addColumn("expected"); QTest::newRow("[[]]") << QString::fromLatin1("[[]]") << (QVariantList() << (QVariant(QVariantList()))); QTest::newRow("[[123]]") << QString::fromLatin1("[[123]]") << (QVariantList() << (QVariant(QVariantList() << 123))); QTest::newRow("[[], 123]") << QString::fromLatin1("[[], 123]") << (QVariantList() << QVariant(QVariantList()) << 123); // Cyclic QTest::newRow("var a=[]; a.push(a)") << QString::fromLatin1("var a=[]; a.push(a); a") << (QVariantList() << QVariant(QVariantList())); QTest::newRow("var a=[]; a.push(123, a)") << QString::fromLatin1("var a=[]; a.push(123, a); a") << (QVariantList() << 123 << QVariant(QVariantList())); QTest::newRow("var a=[]; var b=[]; a.push(b); b.push(a)") << QString::fromLatin1("var a=[]; var b=[]; a.push(b); b.push(a); a") << (QVariantList() << QVariant(QVariantList() << QVariant(QVariantList()))); QTest::newRow("var a=[]; var b=[]; a.push(123, b); b.push(456, a)") << QString::fromLatin1("var a=[]; var b=[]; a.push(123, b); b.push(456, a); a") << (QVariantList() << 123 << QVariant(QVariantList() << 456 << QVariant(QVariantList()))); } void tst_QScriptExtQObject::nestedArrayAsSlotArgument() { QFETCH(QString, program); QFETCH(QVariantList, expected); QScriptValue a = m_engine->evaluate(program); QVERIFY(!a.isError()); QVERIFY(a.isArray()); // Slot that takes QVariantList { QVERIFY(!m_engine->evaluate("myObject.myInvokableWithVariantListArg") .call(QScriptValue(), QScriptValueList() << a).isError()); QCOMPARE(m_myObject->qtFunctionInvoked(), 62); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).type(), QVariant::List); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toList(), expected); } // Slot that takes QVariant { m_myObject->resetQtFunctionInvoked(); QVERIFY(!m_engine->evaluate("myObject.myInvokableWithVariantArg") .call(QScriptValue(), QScriptValueList() << a).isError()); QCOMPARE(m_myObject->qtFunctionInvoked(), 15); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).type(), QVariant::List); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toList(), expected); } } void tst_QScriptExtQObject::nestedObjectAsSlotArgument_data() { QTest::addColumn("program"); QTest::addColumn("expected"); { QVariantMap m; m["a"] = QVariantMap(); QTest::newRow("{ a:{} }") << QString::fromLatin1("({ a:{} })") << m; } { QVariantMap m, m2; m2["b"] = 10; m2["c"] = 20; m["a"] = m2; QTest::newRow("{ a:{b:10, c:20} }") << QString::fromLatin1("({ a:{b:10, c:20} })") << m; } { QVariantMap m; m["a"] = 10; m["b"] = QVariantList() << 20 << 30; QTest::newRow("{ a:10, b:[20, 30]}") << QString::fromLatin1("({ a:10, b:[20,30]})") << m; } // Cyclic { QVariantMap m; m["p"] = QVariantMap(); QTest::newRow("var o={}; o.p=o") << QString::fromLatin1("var o={}; o.p=o; o") << m; } { QVariantMap m; m["p"] = 123; m["q"] = QVariantMap(); QTest::newRow("var o={}; o.p=123; o.q=o") << QString::fromLatin1("var o={}; o.p=123; o.q=o; o") << m; } } void tst_QScriptExtQObject::nestedObjectAsSlotArgument() { QFETCH(QString, program); QFETCH(QVariantMap, expected); QScriptValue o = m_engine->evaluate(program); QVERIFY(!o.isError()); QVERIFY(o.isObject()); // Slot that takes QVariantMap { QVERIFY(!m_engine->evaluate("myObject.myInvokableWithVariantMapArg") .call(QScriptValue(), QScriptValueList() << o).isError()); QCOMPARE(m_myObject->qtFunctionInvoked(), 16); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).type(), QVariant::Map); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toMap(), expected); } // Slot that takes QVariant { m_myObject->resetQtFunctionInvoked(); QVERIFY(!m_engine->evaluate("myObject.myInvokableWithVariantArg") .call(QScriptValue(), QScriptValueList() << o).isError()); QCOMPARE(m_myObject->qtFunctionInvoked(), 15); QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).type(), QVariant::Map); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toMap(), expected); } } // QTBUG-21760 void tst_QScriptExtQObject::propertyAccessThroughActivationObject() { QScriptContext *ctx = m_engine->pushContext(); ctx->setActivationObject(m_engine->newQObject(m_myObject)); QVERIFY(m_engine->evaluate("intProperty").isNumber()); QVERIFY(m_engine->evaluate("mySlot()").isUndefined()); QVERIFY(m_engine->evaluate("mySlotWithStringArg('test')").isUndefined()); QVERIFY(m_engine->evaluate("dynamicProperty").isError()); m_myObject->setProperty("dynamicProperty", 123); QCOMPARE(m_engine->evaluate("dynamicProperty").toInt32(), 123); m_engine->popContext(); } class SignalEmitterThread : public QThread { public: SignalEmitterThread(MyQObject *sender) : m_sender(sender) { } void run() { m_sender->emitMySignal(); } private: MyQObject *m_sender; }; // QTBUG-26261 void tst_QScriptExtQObject::connectionRemovedAfterQueuedCall() { QVERIFY(m_engine->evaluate("var pass = true; function onMySignal() { pass = false; }").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignal.connect(onMySignal)").isUndefined()); SignalEmitterThread thread(m_myObject); QVERIFY(m_myObject->thread() != &thread); // Premise for queued call thread.start(); QVERIFY(thread.wait()); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(onMySignal)").isUndefined()); // Should not crash QCoreApplication::processEvents(); QVERIFY(m_engine->evaluate("pass").toBool()); } // QTBUG-26590 void tst_QScriptExtQObject::collectQObjectWithClosureSlot() { QScriptEngine eng; QScriptValue fun = eng.evaluate("(function(obj) {\n" " obj.mySignal.connect(function() { obj.mySlot(); });\n" "})"); QVERIFY(fun.isFunction()); QPointer obj = new MyQObject; { QScriptValue wrapper = eng.newQObject(obj, QScriptEngine::ScriptOwnership); QVERIFY(fun.call(QScriptValue(), QScriptValueList() << wrapper).isUndefined()); } QVERIFY(obj != 0); QCOMPARE(obj->qtFunctionInvoked(), -1); obj->emitMySignal(); QCOMPARE(obj->qtFunctionInvoked(), 20); collectGarbage_helper(eng); // The closure that's connected to obj's signal has the only JS reference // to obj, and the closure is only referenced by the connection. Hence, obj // should have been collected. if (obj != 0) QEXPECT_FAIL("", "Test can fail because the garbage collector is conservative", Continue); QVERIFY(obj == 0); } void tst_QScriptExtQObject::collectQObjectWithClosureSlot2() { QScriptEngine eng; QScriptValue fun = eng.evaluate("(function(obj1, obj2) {\n" " obj2.mySignal.connect(function() { obj1.mySlot(); });\n" " obj1.mySignal.connect(function() { obj2.mySlot(); });\n" "})"); QVERIFY(fun.isFunction()); QPointer obj1 = new MyQObject; QScriptValue wrapper1 = eng.newQObject(obj1, QScriptEngine::ScriptOwnership); QPointer obj2 = new MyQObject; { QScriptValue wrapper2 = eng.newQObject(obj2, QScriptEngine::ScriptOwnership); QVERIFY(fun.call(QScriptValue(), QScriptValueList() << wrapper1 << wrapper2).isUndefined()); } QVERIFY(obj1 != 0); QVERIFY(obj2 != 0); collectGarbage_helper(eng); // obj1 is referenced by a QScriptValue, so it (and its connections) should not be collected. QVERIFY(obj1 != 0); // obj2 is referenced from the closure that's connected to obj1's signal, so it // (and its connections) should not be collected. QVERIFY(obj2 != 0); QCOMPARE(obj2->qtFunctionInvoked(), -1); obj1->emitMySignal(); QCOMPARE(obj2->qtFunctionInvoked(), 20); QCOMPARE(obj1->qtFunctionInvoked(), -1); obj2->emitMySignal(); QCOMPARE(obj1->qtFunctionInvoked(), 20); } QTEST_MAIN(tst_QScriptExtQObject) #include "tst_qscriptextqobject.moc"