/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qscriptengine.h>
#include <qscriptcontext.h>
#include <qscriptvalueiterator.h>
#include <qwidget.h>
#include <qpushbutton.h>
#include <qlineedit.h>
//TESTED_CLASS=
//TESTED_FILES=script/qscriptextqobject_p.h script/qscriptextqobject.cpp
struct CustomType
{
#if defined (Q_CC_NOKIAX86)
// Compiler crash workaround
CustomType() {}
#endif
QString string;
};
Q_DECLARE_METATYPE(CustomType)
Q_DECLARE_METATYPE(QBrush*)
Q_DECLARE_METATYPE(QObjectList)
Q_DECLARE_METATYPE(QList<int>)
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 clearConnectedSignal()
{ m_connectedSignal = QByteArray(); }
void clearDisconnectedSignal()
{ m_disconnectedSignal = QByteArray(); }
QByteArray connectedSignal() const
{ return m_connectedSignal; }
QByteArray disconnectedSignal() const
{ return m_disconnectedSignal; }
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<int> myInvokableReturningVectorOfInt()
{ m_qtFunctionInvoked = 11; return QVector<int>(); }
Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &)
{ 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 QList<int> myInvokableWithListOfIntArg(const QList<int> &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<MyQObject*>(this)->m_qtFunctionInvoked = 49; return *this; }
Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg)
{ const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50; m_actuals << qVariantFromValue(arg); }
Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg)
{ const_cast<MyQObject*>(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 QObjectList findObjects() const
{ return findChildren<QObject *>(); }
Q_INVOKABLE QList<int> myInvokableNumbers() const
{ return QList<int>() << 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 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 char *signal) {
m_connectedSignal = signal;
}
void disconnectNotify(const char *signal) {
m_disconnectedSignal = 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;
QByteArray m_connectedSignal;
QByteArray m_disconnectedSignal;
};
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 getSetDynamicProperty();
void getSetChildren();
void callQtInvokable();
void connectAndDisconnect();
void connectAndDisconnectWithBadArgs();
void cppConnectAndDisconnect();
void classEnums();
void classConstructor();
void overrideInvokable();
void transferInvokable();
void findChild();
void findChildren();
void overloadedSlots();
void enumerate_data();
void enumerate();
void enumerateSpecial();
void wrapOptions();
void prototypes();
void objectDeleted();
void connectToDestroyedSignal();
void emitAfterReceiverDeleted();
void enumerateMetaObject();
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<QList<int> >(v2), (QList<int>() << 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"));
// 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);
}
// 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);
// 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<QVariantList>(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<QStringList>(m_engine->evaluate("myObject.stringListProperty"));
QCOMPARE(sl, QStringList()
<< QLatin1String("1")
<< QLatin1String("two")
<< QLatin1String("true"));
}
// 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);
}
// 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<QBrush>(br), m_myObject->brushProperty());
QCOMPARE(qscriptvalue_cast<QColor>(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<QBrush>(br), QBrush(newColor));
QCOMPARE(qscriptvalue_cast<QColor>(br), newColor);
m_engine->globalObject().setProperty("myColor", QScriptValue());
}
// try to delete
QCOMPARE(m_engine->evaluate("delete myObject.intProperty").toBoolean(), false);
QCOMPARE(m_engine->evaluate("myObject.intProperty").toNumber(), 123.0);
QCOMPARE(m_engine->evaluate("delete myObject.variantProperty").toBoolean(), false);
QCOMPARE(m_engine->evaluate("myObject.variantProperty").toNumber(), 42.0);
// 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);
// 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);
// 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);
}
// 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'");
// ### ouch! Shouldn't QMetaProperty::write() rather not change the value...?
QCOMPARE(m_myObject->enumProperty(), (MyQObject::Policy)-1);
// enum property with custom conversion
qScriptRegisterMetaType<MyQObject::Policy>(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);
// 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'");
// ### ouch! Shouldn't QMetaProperty::write() rather not change the value...?
QCOMPARE(m_myObject->flagsProperty(), (MyQObject::Ability)-1);
// 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<QBrush>(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<QBrush>(m_engine->evaluate("myObject.brushProperty")), b);
}
m_engine->globalObject().setProperty("brushPointer", QScriptValue());
}
// 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);
}
// 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()]");
QEXPECT_FAIL("", "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::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<int>)
Q_DECLARE_METATYPE(QVector<double>)
Q_DECLARE_METATYPE(QVector<QString>)
template <class T>
static QScriptValue qobjectToScriptValue(QScriptEngine *engine, T* const &in)
{ return engine->newQObject(in); }
template <class T>
static void qobjectFromScriptValue(const QScriptValue &object, T* &out)
{ out = qobject_cast<T*>(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);
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<QVector<int> >(m_engine);
{
QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningVectorOfInt()");
QCOMPARE(ret.isArray(), true);
QCOMPARE(m_myObject->qtFunctionInvoked(), 11);
}
{
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<QVariantMap>(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<int> >());
QList<int> ilst = qvariant_cast<QList<int> >(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);
}
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<QObject*>(v), (QObject *)m_myObject);
QCOMPARE(ret.isQObject(), true);
QCOMPARE(qscriptvalue_cast<QObject*>(ret), (QObject *)m_myObject);
}
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 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<QColor>(v), color);
QCOMPARE(qscriptvalue_cast<QColor>(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<QObject*>(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*>("MyQObject*");
m_myObject->resetQtFunctionInvoked();
{
QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningMyQObject()");
QCOMPARE(m_myObject->qtFunctionInvoked(), 53);
QVERIFY(ret.isVariant());
QCOMPARE(*reinterpret_cast<MyQObject* const *>(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<QObject*>(m_myObject->qtFunctionActuals().at(0)), (QObject*)m_myObject);
}
// 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<QObject*>(m_engine, qobjectToScriptValue,
qobjectFromScriptValue, qobjectProto);
QScriptValue myQObjectProto = m_engine->newObject();
myQObjectProto.setPrototype(qobjectProto);
qScriptRegisterMetaType<MyQObject*>(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<QObject*>(m_engine, 0, 0, QScriptValue());
qScriptRegisterMetaType<MyQObject*>(m_engine, 0, 0, QScriptValue());
}
// detect exceptions during argument conversion
m_myObject->resetQtFunctionInvoked();
{
QScriptValue (*dummy)(QScriptEngine *, const QDir &) = 0;
qScriptRegisterMetaType<QDir>(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);
}
}
// qscript_call()
{
m_myObject->resetQtFunctionInvoked();
QScriptValue ret = m_engine->evaluate("new myObject(123)");
QVERIFY(ret.isError());
QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'myObject' [MyQObject(name = \"\")] is not a constructor."));
}
{
m_myObject->resetQtFunctionInvoked();
QScriptValue ret = m_engine->evaluate("myObject(123)");
QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'myObject' [MyQObject(name = \"\")] is not a function."));
}
// 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->clearConnectedSignal();
QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myHandler)").isUndefined());
QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(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->clearDisconnectedSignal();
QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myHandler)").isUndefined());
QCOMPARE(m_myObject->disconnectedSignal().constData(), SIGNAL(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)
QVERIFY(m_engine->evaluate("myObject.mySignalWithDefaultArg.connect(myHandler)").isUndefined());
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->clearConnectedSignal();
QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.connect(myHandler)").isUndefined());
QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignalWithVariantArg(QVariant)));
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);
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->clearConnectedSignal();
QVERIFY(m_engine->evaluate("myObject.mySignalWithScriptEngineArg.connect(myHandler)").isUndefined());
QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignalWithScriptEngineArg(QScriptEngine*)));
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: argument conversion should work
m_myObject->clearConnectedSignal();
QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.connect(myHandler)").isUndefined());
QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignalWithVariantArg(QVariant)));
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->clearConnectedSignal();
QVERIFY(m_engine->evaluate("myObject.mySignalWithScriptEngineArg.connect(myHandler)").isUndefined());
QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignalWithScriptEngineArg(QScriptEngine*)));
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());
// check that emitting signals from script works
// 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());
// 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::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));
}
}
// 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->clearConnectedSignal();
QVERIFY(qScriptConnect(m_myObject, SIGNAL(mySignal()), QScriptValue(), fun));
QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignal()));
m_myObject->clearDisconnectedSignal();
QVERIFY(qScriptDisconnect(m_myObject, SIGNAL(mySignal()), QScriptValue(), fun));
QCOMPARE(m_myObject->disconnectedSignal().constData(), SIGNAL(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<MyQObject::Policy>(m_engine->evaluate("MyQObject.FooPolicy").toInt32()),
MyQObject::FooPolicy);
QCOMPARE(static_cast<MyQObject::Policy>(m_engine->evaluate("MyQObject.BarPolicy").toInt32()),
MyQObject::BarPolicy);
QCOMPARE(static_cast<MyQObject::Policy>(m_engine->evaluate("MyQObject.BazPolicy").toInt32()),
MyQObject::BazPolicy);
QCOMPARE(static_cast<MyQObject::Strategy>(m_engine->evaluate("MyQObject.FooStrategy").toInt32()),
MyQObject::FooStrategy);
QCOMPARE(static_cast<MyQObject::Strategy>(m_engine->evaluate("MyQObject.BarStrategy").toInt32()),
MyQObject::BarStrategy);
QCOMPARE(static_cast<MyQObject::Strategy>(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<Qt::FocusPolicy>(m_engine->evaluate("MyQObject.StrongFocus").toInt32()),
Qt::StrongFocus);
QCOMPARE(static_cast<Qt::Key>(m_engine->evaluate("MyQObject.Key_Left").toInt32()),
Qt::Key_Left);
QCOMPARE(m_engine->evaluate("MyQObject.className()").toString(), QLatin1String("MyQObject"));
qRegisterMetaType<MyQObject::Policy>("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<MyQObject::Policy>(myClass.property("BazPolicy").toInt32()),
MyQObject::BazPolicy);
myClass.setProperty("BazPolicy", MyQObject::FooPolicy);
QCOMPARE(static_cast<MyQObject::Policy>(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<MyQObject>(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<QObject>(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<QBrush>(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::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<int>("wrapOptions");
QTest::addColumn<QStringList>("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 slots
<< "destroyed(QObject*)" << "destroyed()"
<< "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 slots
<< "destroyed(QObject*)" << "destroyed()"
<< "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 slots
<< "destroyed(QObject*)" << "destroyed()"
// not included because it's private:
// << "_q_reregisterTimers(void*)"
// signals
<< "mySignal()"
// slots
<< "mySlot()" << "myOtherSlot()");
}
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<QStringList>(eng.evaluate("enumeratedProperties"));
QCOMPARE(result.size(), expectedNames.size());
for (int i = 0; i < expectedNames.size(); ++i)
QCOMPARE(result.at(i), expectedNames.at(i));
}
// enumerate in C++
{
QScriptValueIterator it(obj);
QStringList result;
while (it.hasNext()) {
it.next();
QCOMPARE(it.flags(), obj.propertyFlags(it.name()));
result.append(it.name());
}
QCOMPARE(result.size(), expectedNames.size());
for (int i = 0; i < expectedNames.size(); ++i)
QCOMPARE(result.at(i), expectedNames.at(i));
}
}
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 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);
}
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<QWidget*>(), widgetProto);
QPushButton *pbp = new QPushButton();
QScriptValue buttonProto = eng.newQObject(pbp, QScriptEngine::ScriptOwnership);
buttonProto.setPrototype(widgetProto);
eng.setDefaultPrototype(qMetaTypeId<QPushButton*>(), 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::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<QString> 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<QStringList>(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)));
}
}
QTEST_MAIN(tst_QScriptExtQObject)
#include "tst_qscriptextqobject.moc"