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