Home | History | Annotate | Download | only in qscriptvalueiterator
      1 /*
      2     Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
      3 
      4     This library is free software; you can redistribute it and/or
      5     modify it under the terms of the GNU Library General Public
      6     License as published by the Free Software Foundation; either
      7     version 2 of the License, or (at your option) any later version.
      8 
      9     This library is distributed in the hope that it will be useful,
     10     but WITHOUT ANY WARRANTY; without even the implied warranty of
     11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12     Library General Public License for more details.
     13 
     14     You should have received a copy of the GNU Library General Public License
     15     along with this library; see the file COPYING.LIB.  If not, write to
     16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17     Boston, MA 02110-1301, USA.
     18 */
     19 
     20 #ifndef tst_qscriptvalueiterator_h
     21 #define tst_qscriptvalueiterator_h
     22 
     23 #include "qscriptengine.h"
     24 #include "qscriptvalue.h"
     25 #include "qscriptvalueiterator.h"
     26 #include <QtCore/qhash.h>
     27 #include <QtTest/QtTest>
     28 
     29 class tst_QScriptValueIterator : public QObject {
     30     Q_OBJECT
     31 
     32 public:
     33     tst_QScriptValueIterator();
     34     virtual ~tst_QScriptValueIterator();
     35 
     36 private slots:
     37     void iterateForward_data();
     38     void iterateForward();
     39     void iterateBackward_data();
     40     void iterateBackward();
     41     void iterateArray_data();
     42     void iterateArray();
     43     void iterateBackAndForth();
     44     void setValue();
     45     void remove();
     46     void removeMixed();
     47     void removeUndeletable();
     48     void iterateString();
     49     void assignObjectToIterator();
     50 };
     51 
     52 tst_QScriptValueIterator::tst_QScriptValueIterator()
     53 {
     54 }
     55 
     56 tst_QScriptValueIterator::~tst_QScriptValueIterator()
     57 {
     58 }
     59 
     60 void tst_QScriptValueIterator::iterateForward_data()
     61 {
     62     QTest::addColumn<QStringList>("propertyNames");
     63     QTest::addColumn<QStringList>("propertyValues");
     64 
     65     QTest::newRow("no properties")
     66         << QStringList() << QStringList();
     67     QTest::newRow("foo=bar")
     68         << (QStringList() << "foo")
     69         << (QStringList() << "bar");
     70     QTest::newRow("foo=bar, baz=123")
     71         << (QStringList() << "foo" << "baz")
     72         << (QStringList() << "bar" << "123");
     73     QTest::newRow("foo=bar, baz=123, rab=oof")
     74         << (QStringList() << "foo" << "baz" << "rab")
     75         << (QStringList() << "bar" << "123" << "oof");
     76 }
     77 
     78 void tst_QScriptValueIterator::iterateForward()
     79 {
     80     QFETCH(QStringList, propertyNames);
     81     QFETCH(QStringList, propertyValues);
     82     QMap<QString, QString> pmap;
     83     Q_ASSERT(propertyNames.size() == propertyValues.size());
     84 
     85     QScriptEngine engine;
     86     QScriptValue object = engine.newObject();
     87     for (int i = 0; i < propertyNames.size(); ++i) {
     88         QString name = propertyNames.at(i);
     89         QString value = propertyValues.at(i);
     90         pmap.insert(name, value);
     91         object.setProperty(name, QScriptValue(&engine, value));
     92     }
     93     QScriptValue otherObject = engine.newObject();
     94     otherObject.setProperty("foo", QScriptValue(&engine, 123456));
     95     otherObject.setProperty("protoProperty", QScriptValue(&engine, 654321));
     96     object.setPrototype(otherObject); // should not affect iterator
     97 
     98     QStringList lst;
     99     QScriptValueIterator it(object);
    100     while (!pmap.isEmpty()) {
    101         QCOMPARE(it.hasNext(), true);
    102         QCOMPARE(it.hasNext(), true);
    103         it.next();
    104         QString name = it.name();
    105         QCOMPARE(pmap.contains(name), true);
    106         QCOMPARE(it.name(), name);
    107         QCOMPARE(it.flags(), object.propertyFlags(name));
    108         QCOMPARE(it.value().strictlyEquals(QScriptValue(&engine, pmap.value(name))), true);
    109         QCOMPARE(it.scriptName(), engine.toStringHandle(name));
    110         pmap.remove(name);
    111         lst.append(name);
    112     }
    113 
    114     QCOMPARE(it.hasNext(), false);
    115     QCOMPARE(it.hasNext(), false);
    116 
    117     it.toFront();
    118     for (int i = 0; i < lst.count(); ++i) {
    119         QCOMPARE(it.hasNext(), true);
    120         it.next();
    121         QCOMPARE(it.name(), lst.at(i));
    122     }
    123 
    124     for (int i = 0; i < lst.count(); ++i) {
    125         QCOMPARE(it.hasPrevious(), true);
    126         it.previous();
    127         QCOMPARE(it.name(), lst.at(lst.count()-1-i));
    128     }
    129     QCOMPARE(it.hasPrevious(), false);
    130 }
    131 
    132 void tst_QScriptValueIterator::iterateBackward_data()
    133 {
    134     iterateForward_data();
    135 }
    136 
    137 void tst_QScriptValueIterator::iterateBackward()
    138 {
    139     QFETCH(QStringList, propertyNames);
    140     QFETCH(QStringList, propertyValues);
    141     QMap<QString, QString> pmap;
    142     Q_ASSERT(propertyNames.size() == propertyValues.size());
    143 
    144     QScriptEngine engine;
    145     QScriptValue object = engine.newObject();
    146     for (int i = 0; i < propertyNames.size(); ++i) {
    147         QString name = propertyNames.at(i);
    148         QString value = propertyValues.at(i);
    149         pmap.insert(name, value);
    150         object.setProperty(name, QScriptValue(&engine, value));
    151     }
    152 
    153     QStringList lst;
    154     QScriptValueIterator it(object);
    155     it.toBack();
    156     while (!pmap.isEmpty()) {
    157         QCOMPARE(it.hasPrevious(), true);
    158         QCOMPARE(it.hasPrevious(), true);
    159         it.previous();
    160         QString name = it.name();
    161         QCOMPARE(pmap.contains(name), true);
    162         QCOMPARE(it.name(), name);
    163         QCOMPARE(it.flags(), object.propertyFlags(name));
    164         QCOMPARE(it.value().strictlyEquals(QScriptValue(&engine, pmap.value(name))), true);
    165         pmap.remove(name);
    166         lst.append(name);
    167     }
    168 
    169     QCOMPARE(it.hasPrevious(), false);
    170     QCOMPARE(it.hasPrevious(), false);
    171 
    172     it.toBack();
    173     for (int i = 0; i < lst.count(); ++i) {
    174         QCOMPARE(it.hasPrevious(), true);
    175         it.previous();
    176         QCOMPARE(it.name(), lst.at(i));
    177     }
    178 
    179     for (int i = 0; i < lst.count(); ++i) {
    180         QCOMPARE(it.hasNext(), true);
    181         it.next();
    182         QCOMPARE(it.name(), lst.at(lst.count()-1-i));
    183     }
    184     QCOMPARE(it.hasNext(), false);
    185 }
    186 
    187 void tst_QScriptValueIterator::iterateArray_data()
    188 {
    189     QTest::addColumn<QStringList>("inputPropertyNames");
    190     QTest::addColumn<QStringList>("inputPropertyValues");
    191     QTest::addColumn<QStringList>("propertyNames");
    192     QTest::addColumn<QStringList>("propertyValues");
    193     QTest::newRow("no elements") << QStringList() << QStringList() << QStringList() << QStringList();
    194 
    195     QTest::newRow("0=foo, 1=barr")
    196         << (QStringList() << "0" << "1")
    197         << (QStringList() << "foo" << "bar")
    198         << (QStringList() << "0" << "1")
    199         << (QStringList() << "foo" << "bar");
    200 
    201     QTest::newRow("0=foo, 3=barr")
    202         << (QStringList() << "0" << "1" << "2" << "3")
    203         << (QStringList() << "foo" << "" << "" << "bar")
    204         << (QStringList() << "0" << "1" << "2" << "3")
    205         << (QStringList() << "foo" << "" << "" << "bar");
    206 }
    207 
    208 void tst_QScriptValueIterator::iterateArray()
    209 {
    210     QFETCH(QStringList, inputPropertyNames);
    211     QFETCH(QStringList, inputPropertyValues);
    212     QFETCH(QStringList, propertyNames);
    213     QFETCH(QStringList, propertyValues);
    214 
    215     QScriptEngine engine;
    216     QScriptValue array = engine.newArray();
    217     for (int i = 0; i < inputPropertyNames.size(); ++i)
    218         array.setProperty(inputPropertyNames.at(i), inputPropertyValues.at(i));
    219 
    220     int length = array.property("length").toInt32();
    221     QCOMPARE(length, propertyNames.size());
    222     QScriptValueIterator it(array);
    223     for (int i = 0; i < length; ++i) {
    224         QCOMPARE(it.hasNext(), true);
    225         it.next();
    226         QCOMPARE(it.name(), propertyNames.at(i));
    227         QCOMPARE(it.flags(), array.propertyFlags(propertyNames.at(i)));
    228         QVERIFY(it.value().strictlyEquals(array.property(propertyNames.at(i))));
    229         QCOMPARE(it.value().toString(), propertyValues.at(i));
    230     }
    231     QVERIFY(it.hasNext());
    232     it.next();
    233     QCOMPARE(it.name(), QString::fromLatin1("length"));
    234     QVERIFY(it.value().isNumber());
    235     QCOMPARE(it.value().toInt32(), length);
    236     QCOMPARE(it.flags(), QScriptValue::PropertyFlags(QScriptValue::SkipInEnumeration | QScriptValue::Undeletable));
    237 
    238     it.previous();
    239     QCOMPARE(it.hasPrevious(), length > 0);
    240     for (int i = length - 1; i >= 0; --i) {
    241         it.previous();
    242         QCOMPARE(it.name(), propertyNames.at(i));
    243         QCOMPARE(it.flags(), array.propertyFlags(propertyNames.at(i)));
    244         QVERIFY(it.value().strictlyEquals(array.property(propertyNames.at(i))));
    245         QCOMPARE(it.value().toString(), propertyValues.at(i));
    246         QCOMPARE(it.hasPrevious(), i > 0);
    247     }
    248     QCOMPARE(it.hasPrevious(), false);
    249 
    250     // hasNext() and hasPrevious() cache their result; verify that the result is in sync
    251     if (length > 1) {
    252         QVERIFY(it.hasNext());
    253         it.next();
    254         QCOMPARE(it.name(), QString::fromLatin1("0"));
    255         QVERIFY(it.hasNext());
    256         it.previous();
    257         QCOMPARE(it.name(), QString::fromLatin1("0"));
    258         QVERIFY(!it.hasPrevious());
    259         it.next();
    260         QCOMPARE(it.name(), QString::fromLatin1("0"));
    261         QVERIFY(it.hasPrevious());
    262         it.next();
    263         QCOMPARE(it.name(), QString::fromLatin1("1"));
    264     }
    265     {
    266         // same test as object:
    267         QScriptValue originalArray = engine.newArray();
    268         for (int i = 0; i < inputPropertyNames.size(); ++i)
    269             originalArray.setProperty(inputPropertyNames.at(i), inputPropertyValues.at(i));
    270 
    271         QScriptValue array = originalArray.toObject();
    272         int length = array.property("length").toInt32();
    273         QCOMPARE(length, propertyNames.size());
    274         QScriptValueIterator it(array);
    275         for (int i = 0; i < length; ++i) {
    276             QCOMPARE(it.hasNext(), true);
    277             it.next();
    278             QCOMPARE(it.name(), propertyNames.at(i));
    279             QCOMPARE(it.flags(), array.propertyFlags(propertyNames.at(i)));
    280             QVERIFY(it.value().strictlyEquals(array.property(propertyNames.at(i))));
    281             QCOMPARE(it.value().toString(), propertyValues.at(i));
    282         }
    283         QCOMPARE(it.hasNext(), true);
    284         it.next();
    285         QCOMPARE(it.name(), QString::fromLatin1("length"));
    286     }
    287 }
    288 
    289 void tst_QScriptValueIterator::iterateBackAndForth()
    290 {
    291     QScriptEngine engine;
    292     {
    293         QScriptValue object = engine.newObject();
    294         object.setProperty("foo", QScriptValue(&engine, "bar"));
    295         object.setProperty("rab", QScriptValue(&engine, "oof"),
    296                            QScriptValue::SkipInEnumeration); // should not affect iterator
    297         QScriptValueIterator it(object);
    298         QVERIFY(it.hasNext());
    299         it.next();
    300         QCOMPARE(it.name(), QLatin1String("foo"));
    301         QVERIFY(it.hasPrevious());
    302         it.previous();
    303         QCOMPARE(it.name(), QLatin1String("foo"));
    304         QVERIFY(it.hasNext());
    305         it.next();
    306         QCOMPARE(it.name(), QLatin1String("foo"));
    307         QVERIFY(it.hasPrevious());
    308         it.previous();
    309         QCOMPARE(it.name(), QLatin1String("foo"));
    310         QVERIFY(it.hasNext());
    311         it.next();
    312         QCOMPARE(it.name(), QLatin1String("foo"));
    313         QVERIFY(it.hasNext());
    314         it.next();
    315         QCOMPARE(it.name(), QLatin1String("rab"));
    316         QVERIFY(it.hasPrevious());
    317         it.previous();
    318         QCOMPARE(it.name(), QLatin1String("rab"));
    319         QVERIFY(it.hasNext());
    320         it.next();
    321         QCOMPARE(it.name(), QLatin1String("rab"));
    322         QVERIFY(it.hasPrevious());
    323         it.previous();
    324         QCOMPARE(it.name(), QLatin1String("rab"));
    325     }
    326     {
    327         // hasNext() and hasPrevious() cache their result; verify that the result is in sync
    328         QScriptValue object = engine.newObject();
    329         object.setProperty("foo", QScriptValue(&engine, "bar"));
    330         object.setProperty("rab", QScriptValue(&engine, "oof"));
    331         QScriptValueIterator it(object);
    332         QVERIFY(it.hasNext());
    333         it.next();
    334         QCOMPARE(it.name(), QString::fromLatin1("foo"));
    335         QVERIFY(it.hasNext());
    336         it.previous();
    337         QCOMPARE(it.name(), QString::fromLatin1("foo"));
    338         QVERIFY(!it.hasPrevious());
    339         it.next();
    340         QCOMPARE(it.name(), QString::fromLatin1("foo"));
    341         QVERIFY(it.hasPrevious());
    342         it.next();
    343         QCOMPARE(it.name(), QString::fromLatin1("rab"));
    344     }
    345 }
    346 
    347 void tst_QScriptValueIterator::setValue()
    348 {
    349     QScriptEngine engine;
    350     QScriptValue object = engine.newObject();
    351     object.setProperty("foo", QScriptValue(&engine, "bar"));
    352     QScriptValueIterator it(object);
    353     it.next();
    354     QCOMPARE(it.name(), QLatin1String("foo"));
    355     it.setValue(QScriptValue(&engine, "baz"));
    356     QCOMPARE(it.value().strictlyEquals(QScriptValue(&engine, QLatin1String("baz"))), true);
    357     QCOMPARE(object.property("foo").toString(), QLatin1String("baz"));
    358     it.setValue(QScriptValue(&engine, "zab"));
    359     QCOMPARE(it.value().strictlyEquals(QScriptValue(&engine, QLatin1String("zab"))), true);
    360     QCOMPARE(object.property("foo").toString(), QLatin1String("zab"));
    361 }
    362 
    363 void tst_QScriptValueIterator::remove()
    364 {
    365     QScriptEngine engine;
    366     QScriptValue object = engine.newObject();
    367     object.setProperty("foo", QScriptValue(&engine, "bar"),
    368                        QScriptValue::SkipInEnumeration); // should not affect iterator
    369     object.setProperty("rab", QScriptValue(&engine, "oof"));
    370     QScriptValueIterator it(object);
    371     it.next();
    372     QCOMPARE(it.name(), QLatin1String("foo"));
    373     it.remove();
    374     QCOMPARE(it.hasPrevious(), false);
    375     QCOMPARE(object.property("foo").isValid(), false);
    376     QCOMPARE(object.property("rab").toString(), QLatin1String("oof"));
    377     it.next();
    378     QCOMPARE(it.name(), QLatin1String("rab"));
    379     QCOMPARE(it.value().toString(), QLatin1String("oof"));
    380     QCOMPARE(it.hasNext(), false);
    381     it.remove();
    382     QCOMPARE(object.property("rab").isValid(), false);
    383     QCOMPARE(it.hasPrevious(), false);
    384     QCOMPARE(it.hasNext(), false);
    385 }
    386 
    387 void tst_QScriptValueIterator::removeMixed()
    388 {
    389     // This test checks if QScriptValueIterator behaives correctly if an object's property got deleted
    390     // in different way.
    391     QScriptEngine engine;
    392     QScriptValue object = engine.evaluate("o = new Object; o");
    393     object.setProperty("a", QScriptValue(124), QScriptValue::SkipInEnumeration);
    394     object.setProperty("b", QScriptValue(816));
    395     object.setProperty("c", QScriptValue(3264));
    396     QScriptValueIterator it(object);
    397     it.next();
    398     it.next();
    399     QCOMPARE(it.name(), QLatin1String("b"));
    400     QCOMPARE(it.hasPrevious(), true);
    401     QCOMPARE(it.hasNext(), true);
    402     // Remove 'a'
    403     object.setProperty("a", QScriptValue());
    404     QEXPECT_FAIL("", "That would be a significant behavioral and performance change, new QtScript API should be developed (QTBUG-12087)", Abort);
    405     QCOMPARE(it.hasPrevious(), false);
    406     QCOMPARE(it.hasNext(), true);
    407     // Remove 'c'
    408     engine.evaluate("delete o.c");
    409     QCOMPARE(it.hasPrevious(), false);
    410     QCOMPARE(it.hasNext(), false);
    411     // Remove 'b'
    412     object.setProperty("b", QScriptValue());
    413     QCOMPARE(it.hasPrevious(), false);
    414     QCOMPARE(it.hasNext(), false);
    415     QCOMPARE(it.name(), QString());
    416     QCOMPARE(it.value().toString(), QString());
    417 
    418     // Try to remove a removed property.
    419     it.remove();
    420     QCOMPARE(it.hasPrevious(), false);
    421     QCOMPARE(it.hasNext(), false);
    422     QCOMPARE(it.name(), QString());
    423     QCOMPARE(it.value().toString(), QString());
    424 
    425     for (int i = 0; i < 2; ++i) {
    426         it.next();
    427         QCOMPARE(it.hasPrevious(), false);
    428         QCOMPARE(it.hasNext(), false);
    429         QCOMPARE(it.name(), QString());
    430         QCOMPARE(it.value().toString(), QString());
    431     }
    432 
    433     for (int i = 0; i < 2; ++i) {
    434         it.previous();
    435         QCOMPARE(it.hasPrevious(), false);
    436         QCOMPARE(it.hasNext(), false);
    437         QCOMPARE(it.name(), QString());
    438         QCOMPARE(it.value().toString(), QString());
    439     }
    440 }
    441 
    442 void tst_QScriptValueIterator::removeUndeletable()
    443 {
    444     // Undeletable property can't be deleted via iterator.
    445     QScriptEngine engine;
    446     QScriptValue object = engine.evaluate("o = new Object; o");
    447     object.setProperty("a", QScriptValue(&engine, 124));
    448     object.setProperty("b", QScriptValue(&engine, 816), QScriptValue::Undeletable);
    449     QVERIFY(object.property("b").isValid());
    450     QScriptValueIterator it(object);
    451     it.next();
    452     it.next();
    453     it.remove();
    454     it.toFront();
    455     QVERIFY(it.hasNext());
    456     QVERIFY(object.property("b").isValid());
    457 }
    458 
    459 void tst_QScriptValueIterator::iterateString()
    460 {
    461     QScriptEngine engine;
    462     QScriptValue str = QScriptValue(&engine, QString::fromLatin1("ciao"));
    463     QVERIFY(str.isString());
    464     QScriptValue obj = str.toObject();
    465     int length = obj.property("length").toInt32();
    466     QCOMPARE(length, 4);
    467     QScriptValueIterator it(obj);
    468     for (int i = 0; i < length; ++i) {
    469         QCOMPARE(it.hasNext(), true);
    470         QString indexStr = QScriptValue(&engine, i).toString();
    471         it.next();
    472         QCOMPARE(it.name(), indexStr);
    473         QCOMPARE(it.flags(), obj.propertyFlags(indexStr));
    474         QCOMPARE(it.value().strictlyEquals(obj.property(indexStr)), true);
    475     }
    476     QVERIFY(it.hasNext());
    477     it.next();
    478     QCOMPARE(it.name(), QString::fromLatin1("length"));
    479     QVERIFY(it.value().isNumber());
    480     QCOMPARE(it.value().toInt32(), length);
    481     QCOMPARE(it.flags(), QScriptValue::PropertyFlags(QScriptValue::ReadOnly | QScriptValue::SkipInEnumeration | QScriptValue::Undeletable));
    482 
    483     it.previous();
    484     QCOMPARE(it.hasPrevious(), length > 0);
    485     for (int i = length - 1; i >= 0; --i) {
    486         it.previous();
    487         QString indexStr = QScriptValue(&engine, i).toString();
    488         QCOMPARE(it.name(), indexStr);
    489         QCOMPARE(it.flags(), obj.propertyFlags(indexStr));
    490         QCOMPARE(it.value().strictlyEquals(obj.property(indexStr)), true);
    491         QCOMPARE(it.hasPrevious(), i > 0);
    492     }
    493     QCOMPARE(it.hasPrevious(), false);
    494 }
    495 
    496 void tst_QScriptValueIterator::assignObjectToIterator()
    497 {
    498     QScriptEngine eng;
    499     QScriptValue obj1 = eng.newObject();
    500     obj1.setProperty("foo", 123);
    501     QScriptValue obj2 = eng.newObject();
    502     obj2.setProperty("bar", 456);
    503 
    504     QScriptValueIterator it(obj1);
    505     QVERIFY(it.hasNext());
    506     it.next();
    507     it = obj2;
    508     QVERIFY(it.hasNext());
    509     it.next();
    510     QCOMPARE(it.name(), QString::fromLatin1("bar"));
    511 
    512     it = obj1;
    513     QVERIFY(it.hasNext());
    514     it.next();
    515     QCOMPARE(it.name(), QString::fromLatin1("foo"));
    516 
    517     it = obj2;
    518     QVERIFY(it.hasNext());
    519     it.next();
    520     QCOMPARE(it.name(), QString::fromLatin1("bar"));
    521 
    522     it = obj2;
    523     QVERIFY(it.hasNext());
    524     it.next();
    525     QCOMPARE(it.name(), QString::fromLatin1("bar"));
    526 }
    527 
    528 QTEST_MAIN(tst_QScriptValueIterator)
    529 #include "tst_qscriptvalueiterator.moc"
    530 
    531 #endif // tst_qscriptvalueiterator_h
    532