1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <qdebug.h>
32
33#include <QtScript/qscriptengine.h>
34#include <QtScript/qscriptable.h>
35
36class MyScriptable : public QObject, public QScriptable
37{
38 Q_OBJECT
39 Q_PROPERTY(int baz READ baz WRITE setBaz)
40 Q_PROPERTY(QObject* zab READ zab WRITE setZab)
41 Q_PROPERTY(int 0 READ baz)
42 Q_PROPERTY(QObject* 1 READ zab)
43 Q_PROPERTY(int oof WRITE setOof)
44public:
45 MyScriptable(QObject *parent = 0)
46 : QObject(parent), m_lastEngine(0)
47 { }
48 ~MyScriptable() { }
49
50 QScriptEngine *lastEngine() const;
51
52 void setOof(int)
53 { m_oofThisObject = context()->thisObject(); }
54 QScriptValue oofThisObject() const
55 { return m_oofThisObject; }
56
57 void emitSig(int value)
58 { emit sig(value); }
59
60public slots:
61 void foo();
62 void setX(int x);
63 void setX(const QString &x);
64 void setX2(int x);
65 bool isBar();
66 int baz();
67 void setBaz(int x);
68 void evalIsBar();
69 bool useInAnotherEngine();
70 void setOtherEngine();
71 QObject *zab();
72 QObject *setZab(QObject *);
73 QScriptValue getArguments();
74 int getArgumentCount();
75
76 QString toString() const;
77 int valueOf() const;
78
79signals:
80 void sig(int);
81
82private:
83 QScriptEngine *m_lastEngine;
84 QScriptEngine *m_otherEngine;
85 QScriptValue m_oofThisObject;
86};
87
88QScriptEngine *MyScriptable::lastEngine() const
89{
90 return m_lastEngine;
91}
92
93int MyScriptable::baz()
94{
95 m_lastEngine = engine();
96 return 123;
97}
98
99void MyScriptable::setBaz(int)
100{
101 m_lastEngine = engine();
102}
103
104QObject *MyScriptable::zab()
105{
106 return thisObject().toQObject();
107}
108
109QObject *MyScriptable::setZab(QObject *)
110{
111 return thisObject().toQObject();
112}
113
114QScriptValue MyScriptable::getArguments()
115{
116 return context()->argumentsObject();
117}
118
119int MyScriptable::getArgumentCount()
120{
121 return argumentCount();
122}
123
124void MyScriptable::foo()
125{
126 m_lastEngine = engine();
127 if (engine())
128 context()->throwError(text: "MyScriptable.foo");
129}
130
131void MyScriptable::evalIsBar()
132{
133 engine()->evaluate(program: "this.isBar()");
134 m_lastEngine = engine();
135}
136
137bool MyScriptable::useInAnotherEngine()
138{
139 QScriptEngine eng;
140 eng.globalObject().setProperty(name: "foo", value: eng.newQObject(object: this));
141 eng.evaluate(program: "foo.baz()");
142 m_lastEngine = engine();
143 return (m_otherEngine == &eng);
144}
145
146void MyScriptable::setOtherEngine()
147{
148 m_otherEngine = engine();
149}
150
151void MyScriptable::setX(int x)
152{
153 m_lastEngine = engine();
154 if (engine())
155 thisObject().setProperty(name: "x", value: QScriptValue(engine(), x));
156}
157
158void MyScriptable::setX(const QString &x)
159{
160 m_lastEngine = engine();
161 if (engine())
162 thisObject().setProperty(name: "x", value: QScriptValue(engine(), x));
163}
164
165void MyScriptable::setX2(int)
166{
167 m_lastEngine = engine();
168 thisObject().setProperty(name: "x", value: argument(index: 0));
169}
170
171bool MyScriptable::isBar()
172{
173 m_lastEngine = engine();
174 QString str = thisObject().toString();
175 return str.contains(c: QLatin1Char('@'));
176}
177
178QString MyScriptable::toString() const
179{
180 return thisObject().property(name: "objectName").toString();
181}
182
183int MyScriptable::valueOf() const
184{
185 return thisObject().property(name: "baz").toInt32();
186}
187
188class tst_QScriptable : public QObject
189{
190 Q_OBJECT
191
192public:
193 tst_QScriptable();
194 virtual ~tst_QScriptable();
195
196private slots:
197 void initTestCase();
198 void cleanupTestCase();
199
200 void engine();
201 void thisObject();
202 void arguments();
203 void throwError();
204 void stringConstructor();
205 void numberConstructor();
206
207private:
208 QScriptEngine m_engine;
209 MyScriptable m_scriptable;
210};
211
212tst_QScriptable::tst_QScriptable()
213{
214}
215
216tst_QScriptable::~tst_QScriptable()
217{
218}
219
220void tst_QScriptable::initTestCase()
221{
222 QScriptValue obj = m_engine.newQObject(object: &m_scriptable);
223 m_engine.globalObject().setProperty(name: "scriptable", value: obj);
224}
225
226void tst_QScriptable::cleanupTestCase()
227{
228}
229
230void tst_QScriptable::engine()
231{
232 QCOMPARE(m_scriptable.engine(), (QScriptEngine*)0);
233 QCOMPARE(m_scriptable.context(), (QScriptContext*)0);
234 QCOMPARE(m_scriptable.lastEngine(), (QScriptEngine *)0);
235
236 // reading property
237 {
238 QScriptValue ret = m_engine.evaluate(program: "scriptable.baz");
239 QCOMPARE(ret.strictlyEquals(QScriptValue(&m_engine, 123)), true);
240 }
241 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
242 {
243 QScriptValue ret = m_engine.evaluate(program: "scriptable[0]");
244 QCOMPARE(ret.strictlyEquals(QScriptValue(&m_engine, 123)), true);
245 }
246 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
247 // when reading from C++, engine() should be 0
248 (void)m_scriptable.property(name: "baz");
249 QCOMPARE(m_scriptable.lastEngine(), (QScriptEngine *)0);
250
251 // writing property
252 m_engine.evaluate(program: "scriptable.baz = 123");
253 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
254 (void)m_scriptable.setProperty(name: "baz", value: 123);
255 QCOMPARE(m_scriptable.lastEngine(), (QScriptEngine *)0);
256
257 // calling slot
258 m_engine.evaluate(program: "scriptable.setX(123)");
259 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
260 QCOMPARE(m_engine.evaluate("scriptable.x")
261 .strictlyEquals(QScriptValue(&m_engine, 123)), true);
262 (void)m_scriptable.setProperty(name: "baz", value: 123);
263 QCOMPARE(m_scriptable.lastEngine(), (QScriptEngine *)0);
264
265 // calling overloaded slot
266 m_engine.evaluate(program: "scriptable.setX('123')");
267 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
268 QCOMPARE(m_engine.evaluate("scriptable.x")
269 .strictlyEquals(QScriptValue(&m_engine, QLatin1String("123"))), true);
270
271 // calling a slot from another slot
272 m_engine.evaluate(program: "scriptable.evalIsBar()");
273 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
274
275 // calling a slot that registers m_scriptable in a different engine
276 // and calls evaluate()
277 {
278 QScriptValue ret = m_engine.evaluate(program: "scriptable.useInAnotherEngine()");
279 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
280 }
281}
282
283void tst_QScriptable::thisObject()
284{
285 QVERIFY(!m_scriptable.thisObject().isValid());
286
287 m_engine.evaluate(program: "o = { }");
288 {
289 QScriptValue ret = m_engine.evaluate(program: "o.__proto__ = scriptable;"
290 "o.setX(123);"
291 "o.__proto__ = Object.prototype;"
292 "o.x");
293 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
294 QCOMPARE(ret.strictlyEquals(QScriptValue(&m_engine, 123)), true);
295 }
296 {
297 QScriptValue ret = m_engine.evaluate(program: "o.__proto__ = scriptable;"
298 "o.setX2(456);"
299 "o.__proto__ = Object.prototype;"
300 "o.x");
301 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
302 QCOMPARE(ret.strictlyEquals(QScriptValue(&m_engine, 456)), true);
303 }
304 m_engine.evaluate(program: "o.__proto__ = scriptable");
305 {
306 QScriptValue ret = m_engine.evaluate(program: "o.isBar()");
307 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
308 QCOMPARE(ret.strictlyEquals(QScriptValue(&m_engine, false)), true);
309 }
310 {
311 QScriptValue ret = m_engine.evaluate(program: "o.toString = function() { return 'foo@bar'; }; o.isBar()");
312 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
313 QCOMPARE(ret.strictlyEquals(QScriptValue(&m_engine, true)), true);
314 }
315
316 // property getter
317 {
318 QScriptValue ret = m_engine.evaluate(program: "scriptable.zab");
319 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
320 QCOMPARE(ret.isQObject(), true);
321 QCOMPARE(ret.toQObject(), (QObject *)&m_scriptable);
322 }
323 {
324 QScriptValue ret = m_engine.evaluate(program: "scriptable[1]");
325 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
326 QCOMPARE(ret.isQObject(), true);
327 QCOMPARE(ret.toQObject(), (QObject *)&m_scriptable);
328 }
329 {
330 QScriptValue ret = m_engine.evaluate(program: "o.zab");
331 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
332 QCOMPARE(ret.toQObject(), (QObject *)0);
333 }
334 // property setter
335 {
336 QScriptValue ret = m_engine.evaluate(program: "scriptable.setZab(null)");
337 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
338 QCOMPARE(ret.isQObject(), true);
339 QCOMPARE(ret.toQObject(), (QObject *)&m_scriptable);
340 }
341 {
342 QVERIFY(!m_scriptable.oofThisObject().isValid());
343 m_engine.evaluate(program: "o.oof = 123");
344 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
345 QVERIFY(m_scriptable.oofThisObject().strictlyEquals(m_engine.evaluate("o")));
346 }
347 {
348 m_engine.evaluate(program: "scriptable.oof = 123");
349 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
350 QVERIFY(m_scriptable.oofThisObject().strictlyEquals(m_engine.evaluate("scriptable")));
351 }
352
353 // target of signal
354 {
355 {
356 QScriptValue ret = m_engine.evaluate(program: "scriptable.sig.connect(o, scriptable.setX)");
357 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
358 QVERIFY(ret.isUndefined());
359 }
360 QVERIFY(m_engine.evaluate("o.x").strictlyEquals(QScriptValue(&m_engine, 456)));
361 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
362 m_scriptable.emitSig(value: 654321);
363 QVERIFY(m_engine.evaluate("o.x").strictlyEquals(QScriptValue(&m_engine, 654321)));
364 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
365 {
366 QScriptValue ret = m_engine.evaluate(program: "scriptable.sig.disconnect(o, scriptable.setX)");
367 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
368 QVERIFY(ret.isUndefined());
369 }
370 }
371
372 m_engine.evaluate(program: "delete o");
373}
374
375void tst_QScriptable::arguments()
376{
377 // even though the C++ slot accepts zero arguments, it should
378 // still be invoked; the arguments should be accessible through
379 // the QScriptable API
380 QScriptValue args = m_engine.evaluate(program: "scriptable.getArguments(10, 20, 30, 'hi')");
381 QVERIFY(args.property("length").strictlyEquals(QScriptValue(&m_engine, 4)));
382 QVERIFY(args.property("0").strictlyEquals(QScriptValue(&m_engine, 10)));
383 QVERIFY(args.property("1").strictlyEquals(QScriptValue(&m_engine, 20)));
384 QVERIFY(args.property("2").strictlyEquals(QScriptValue(&m_engine, 30)));
385 QVERIFY(args.property("3").strictlyEquals(QScriptValue(&m_engine, "hi")));
386
387 QScriptValue argc = m_engine.evaluate(program: "scriptable.getArgumentCount(1, 2, 3)");
388 QVERIFY(argc.isNumber());
389 QCOMPARE(argc.toInt32(), 3);
390
391 QCOMPARE(m_scriptable.argumentCount(), -1);
392 QVERIFY(!m_scriptable.argument(-1).isValid());
393 QVERIFY(!m_scriptable.argument(0).isValid());
394}
395
396void tst_QScriptable::throwError()
397{
398 QScriptValue ret = m_engine.evaluate(program: "scriptable.foo()");
399 QCOMPARE(m_scriptable.lastEngine(), &m_engine);
400 QCOMPARE(ret.isError(), true);
401 QCOMPARE(ret.toString(), QString("Error: MyScriptable.foo"));
402}
403
404void tst_QScriptable::stringConstructor()
405{
406 m_scriptable.setObjectName("TestObject");
407
408 m_engine.globalObject().setProperty(name: "js_obj", value: m_engine.newObject());
409 m_engine.evaluate(
410 program: "js_obj.str = scriptable.toString();"
411 "js_obj.toString = function() { return this.str }");
412
413 QCOMPARE(m_engine.evaluate("String(scriptable)").toString(),
414 m_engine.evaluate("String(js_obj)").toString());
415
416 QCOMPARE(m_engine.evaluate("String(scriptable)").toString(),
417 m_engine.evaluate("scriptable.toString()").toString());
418}
419
420void tst_QScriptable::numberConstructor()
421{
422 m_engine.globalObject().setProperty(name: "js_obj", value: m_engine.newObject());
423 m_engine.evaluate(
424 program: "js_obj.num = scriptable.valueOf();"
425 "js_obj.valueOf = function() { return this.num }");
426
427 QCOMPARE(m_engine.evaluate("Number(scriptable)").toInt32(),
428 m_engine.evaluate("Number(js_obj)").toInt32());
429
430 QCOMPARE(m_engine.evaluate("Number(scriptable)").toInt32(),
431 m_engine.evaluate("scriptable.valueOf()").toInt32());
432}
433
434QTEST_MAIN(tst_QScriptable)
435#include "tst_qscriptable.moc"
436

source code of qtscript/tests/auto/qscriptable/tst_qscriptable.cpp