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