Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library db_test; | 5 library db_test; |
| 6 | 6 |
| 7 /// NOTE: In order to run these tests, the following datastore indices must | 7 /// NOTE: In order to run these tests, the following datastore indices must |
| 8 /// exist: | 8 /// exist: |
| 9 /// $ cat index.yaml | 9 /// $ cat index.yaml |
| 10 /// indexes: | 10 /// indexes: |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 54 import '../../common_e2e.dart'; | 54 import '../../common_e2e.dart'; |
| 55 | 55 |
| 56 @db.Kind() | 56 @db.Kind() |
| 57 class Person extends db.Model { | 57 class Person extends db.Model { |
| 58 @db.StringProperty() | 58 @db.StringProperty() |
| 59 String name; | 59 String name; |
| 60 | 60 |
| 61 @db.IntProperty() | 61 @db.IntProperty() |
| 62 int age; | 62 int age; |
| 63 | 63 |
| 64 @db.ModelKeyProperty() | 64 @db.ModelKeyProperty(propertyName: 'mangledWife') |
| 65 db.Key wife; | 65 db.Key wife; |
| 66 | 66 |
| 67 operator==(Object other) => sameAs(other); | 67 operator==(Object other) => sameAs(other); |
| 68 | 68 |
| 69 sameAs(Object other) { | 69 sameAs(Object other) { |
| 70 return other is Person && | 70 return other is Person && |
| 71 id == other.id && | 71 id == other.id && |
| 72 parentKey == other.parentKey && | 72 parentKey == other.parentKey && |
| 73 name == other.name && | 73 name == other.name && |
| 74 age == other.age && | 74 age == other.age && |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 131 return false; | 131 return false; |
| 132 } | 132 } |
| 133 } | 133 } |
| 134 return true; | 134 return true; |
| 135 } | 135 } |
| 136 return false; | 136 return false; |
| 137 } | 137 } |
| 138 } | 138 } |
| 139 | 139 |
| 140 | 140 |
| 141 Future sleep(Duration duration) { | 141 Future sleep(Duration duration) => new Future.delayed(duration); |
| 142 var completer = new Completer(); | |
| 143 new Timer(duration, completer.complete); | |
| 144 return completer.future; | |
| 145 } | |
| 146 | 142 |
| 147 runTests(db.DatastoreDB store, String namespace) { | 143 runTests(db.DatastoreDB store, String namespace) { |
| 148 var partition = store.newPartition(namespace); | 144 var partition = store.newPartition(namespace); |
| 149 | 145 |
| 150 void compareModels(List<db.Model> expectedModels, | 146 void compareModels(List<db.Model> expectedModels, |
| 151 List<db.Model> models, | 147 List<db.Model> models, |
| 152 {bool anyOrder: false}) { | 148 {bool anyOrder: false}) { |
| 153 expect(models.length, equals(expectedModels.length)); | 149 expect(models.length, equals(expectedModels.length)); |
| 154 if (anyOrder) { | 150 if (anyOrder) { |
| 155 // Do expensive O(n^2) search. | 151 // Do expensive O(n^2) search. |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 388 for (var i = 1; i <= 10; i++) { | 384 for (var i = 1; i <= 10; i++) { |
| 389 var languages = []; | 385 var languages = []; |
| 390 if (i == 9) { | 386 if (i == 9) { |
| 391 languages = ['foo']; | 387 languages = ['foo']; |
| 392 } else if (i == 10) { | 388 } else if (i == 10) { |
| 393 languages = ['foo', 'bar']; | 389 languages = ['foo', 'bar']; |
| 394 } | 390 } |
| 395 users.add(new User() | 391 users.add(new User() |
| 396 ..id = i | 392 ..id = i |
| 397 ..parentKey = root | 393 ..parentKey = root |
| 394 ..wife = root.append(User, id: 42 + i) | |
| 398 ..age = 42 + i | 395 ..age = 42 + i |
| 399 ..name = 'user$i' | 396 ..name = 'user$i' |
| 400 ..nickname = 'nickname${i%3}' | 397 ..nickname = 'nickname${i%3}' |
| 401 ..languages = languages); | 398 ..languages = languages); |
| 402 } | 399 } |
| 403 | 400 |
| 404 var expandoPersons = []; | 401 var expandoPersons = []; |
| 405 for (var i = 1; i <= 3; i++) { | 402 for (var i = 1; i <= 3; i++) { |
| 406 var expandoPerson = new ExpandoPerson() | 403 var expandoPerson = new ExpandoPerson() |
| 407 ..parentKey = root | 404 ..parentKey = root |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 438 | 435 |
| 439 var usersSortedAndFilteredNameDescNicknameDesc = | 436 var usersSortedAndFilteredNameDescNicknameDesc = |
| 440 usersSortedNameDescNicknameDesc.where((User u) { | 437 usersSortedNameDescNicknameDesc.where((User u) { |
| 441 return LOWER_BOUND.compareTo(u.name) <= 0; | 438 return LOWER_BOUND.compareTo(u.name) <= 0; |
| 442 }).toList(); | 439 }).toList(); |
| 443 | 440 |
| 444 var fooUsers = users.where( | 441 var fooUsers = users.where( |
| 445 (User u) => u.languages.contains('foo')).toList(); | 442 (User u) => u.languages.contains('foo')).toList(); |
| 446 var barUsers = users.where( | 443 var barUsers = users.where( |
| 447 (User u) => u.languages.contains('bar')).toList(); | 444 (User u) => u.languages.contains('bar')).toList(); |
| 445 var usersWithWife = users.where( | |
| 446 (User u) => u.wife == root.append(User, id: 42 + 3)); | |
| 448 | 447 |
| 449 var allInserts = [] | 448 var allInserts = [] |
| 450 ..addAll(users) | 449 ..addAll(users) |
| 451 ..addAll(expandoPersons); | 450 ..addAll(expandoPersons); |
| 452 var allKeys = allInserts.map((db.Model model) => model.key).toList(); | 451 var allKeys = allInserts.map((db.Model model) => model.key).toList(); |
| 453 return store.commit(inserts: allInserts).then((_) { | 452 return store.commit(inserts: allInserts).then((_) { |
| 454 return waitUntilEntitiesReady(store, allKeys, partition).then((_) { | 453 return waitUntilEntitiesReady(store, allKeys, partition).then((_) { |
| 455 var tests = [ | 454 var tests = [ |
| 456 // Queries for [Person] return no results, we only have [User] | 455 // Queries for [Person] return no results, we only have [User] |
| 457 // objects. | 456 // objects. |
| 458 () { | 457 () { |
| 459 return store.query(Person, partition: partition).run().toList() | 458 return store.query(Person, partition: partition).run().toList() |
| 460 .then((List<db.Model> models) { | 459 .then((List<db.Model> models) { |
| 461 compareModels([], models); | 460 compareModels([], models); |
| 462 }); | 461 }); |
| 463 }, | 462 }, |
| 464 | 463 |
| 465 // All users query | 464 // All users query |
| 466 () { | 465 () { |
| 467 return store.query(User, partition: partition).run().toList() | 466 return store.query(User, partition: partition).run().toList() |
| 468 .then((List<db.Model> models) { | 467 .then((List<db.Model> models) { |
| 469 compareModels(users, models, anyOrder: true); | 468 compareModels(users, models, anyOrder: true); |
| 470 }); | 469 }); |
| 471 }, | 470 }, |
| 472 | 471 |
| 473 // Sorted query | 472 // Sorted query |
| 474 () { | 473 () async { |
| 475 return store.query(User, partition: partition) | 474 var query = store.query(User, partition: partition) |
| 476 ..order('-name') | 475 ..order('-name') |
| 477 ..order('nickname') | 476 ..order('nickname'); |
| 478 ..run().toList().then((List<db.Model> models) { | 477 var models = await runQueryWithExponentialBackoff( |
| 479 compareModels( | 478 query, usersSortedNameDescNicknameAsc.length); |
|
kustermann
2015/12/11 11:08:44
There is still some flakiness on the buildbot. I d
| |
| 480 usersSortedNameDescNicknameAsc, models); | 479 compareModels( |
| 481 }); | 480 usersSortedNameDescNicknameAsc, models); |
| 482 }, | 481 }, |
| 483 () { | 482 () async { |
| 484 return store.query(User, partition: partition) | 483 var query = store.query(User, partition: partition) |
| 485 ..order('-name') | 484 ..order('-name') |
| 486 ..order('-nickname') | 485 ..order('-nickname') |
| 487 ..run().toList().then((List<db.Model> models) { | 486 ..run(); |
| 488 compareModels( | 487 var models = await runQueryWithExponentialBackoff( |
| 489 usersSortedNameDescNicknameDesc, models); | 488 query, usersSortedNameDescNicknameDesc.length); |
| 490 }); | 489 compareModels( |
| 490 usersSortedNameDescNicknameDesc, models); | |
| 491 }, | 491 }, |
| 492 | 492 |
| 493 // Sorted query with filter | 493 // Sorted query with filter |
| 494 () { | 494 () async { |
| 495 return store.query(User, partition: partition) | 495 var query = store.query(User, partition: partition) |
| 496 ..filter('name >=', LOWER_BOUND) | 496 ..filter('name >=', LOWER_BOUND) |
| 497 ..order('-name') | 497 ..order('-name') |
| 498 ..order('nickname') | 498 ..order('nickname'); |
| 499 ..run().toList().then((List<db.Model> models) { | 499 var models = await runQueryWithExponentialBackoff( |
| 500 compareModels(usersSortedAndFilteredNameDescNicknameAsc, | 500 query, usersSortedAndFilteredNameDescNicknameAsc.length); |
| 501 models); | 501 compareModels(usersSortedAndFilteredNameDescNicknameAsc, |
| 502 }); | 502 models); |
| 503 }, | 503 }, |
| 504 () { | 504 () async { |
| 505 return store.query(User, partition: partition) | 505 var query = store.query(User, partition: partition) |
| 506 ..filter('name >=', LOWER_BOUND) | 506 ..filter('name >=', LOWER_BOUND) |
| 507 ..order('-name') | 507 ..order('-name') |
| 508 ..order('-nickname') | 508 ..order('-nickname') |
| 509 ..run().toList().then((List<db.Model> models) { | 509 ..run(); |
| 510 compareModels(usersSortedAndFilteredNameDescNicknameDesc, | 510 var models = await runQueryWithExponentialBackoff( |
| 511 models); | 511 query, usersSortedAndFilteredNameDescNicknameDesc.length); |
| 512 }); | 512 compareModels(usersSortedAndFilteredNameDescNicknameDesc, |
| 513 models); | |
| 513 }, | 514 }, |
| 514 | 515 |
| 515 // Filter lists | 516 // Filter lists |
| 516 /* FIXME: TODO: FIXME: "IN" not supported in public proto/apiary */ | 517 /* FIXME: TODO: FIXME: "IN" not supported in public proto/apiary */ |
| 517 () { | 518 () async { |
| 518 return store.query(User, partition: partition) | 519 var query = store.query(User, partition: partition) |
| 519 ..filter('languages IN', ['foo']) | 520 ..filter('languages IN', ['foo']) |
| 520 ..order('name') | 521 ..order('name') |
| 521 ..run().toList().then((List<db.Model> models) { | 522 ..run(); |
| 522 compareModels(fooUsers, models, anyOrder: true); | 523 var models = await runQueryWithExponentialBackoff(query, fooUsers. length); |
|
Søren Gjesse
2015/12/14 07:53:24
Long line - and a few more below.
kustermann
2015/12/14 09:41:16
Done.
| |
| 523 }); | 524 compareModels(fooUsers, models, anyOrder: true); |
| 524 }, | 525 }, |
| 525 () { | 526 () async { |
| 526 return store.query(User, partition: partition) | 527 var query = store.query(User, partition: partition) |
| 527 ..filter('languages IN', ['bar']) | 528 ..filter('languages IN', ['bar']) |
| 528 ..order('name') | 529 ..order('name') |
| 529 ..run().toList().then((List<db.Model> models) { | 530 ..run(); |
| 530 compareModels(barUsers, models, anyOrder: true); | 531 var models = await runQueryWithExponentialBackoff(query, barUsers. length); |
| 531 }); | 532 compareModels(barUsers, models, anyOrder: true); |
| 533 }, | |
| 534 | |
| 535 // Filter equals | |
| 536 () async { | |
| 537 var wifeKey = root.append(User, id: usersWithWife.first.wife.id); | |
| 538 var query = store.query(User, partition: partition) | |
| 539 ..filter('wife =', wifeKey) | |
| 540 ..run(); | |
| 541 var models = await runQueryWithExponentialBackoff(query, usersWith Wife.length); | |
| 542 compareModels(usersWithWife, models, anyOrder: true); | |
| 532 }, | 543 }, |
| 533 | 544 |
| 534 // Simple limit/offset test. | 545 // Simple limit/offset test. |
| 535 () { | 546 () async { |
| 536 return store.query(User, partition: partition) | 547 var query = store.query(User, partition: partition) |
| 537 ..order('-name') | 548 ..order('-name') |
| 538 ..order('nickname') | 549 ..order('nickname') |
| 539 ..offset(3) | 550 ..offset(3) |
| 540 ..limit(4) | 551 ..limit(4); |
| 541 ..run().toList().then((List<db.Model> models) { | 552 var expectedModels = |
| 542 var expectedModels = | 553 usersSortedAndFilteredNameDescNicknameAsc.sublist(3, 7); |
| 543 usersSortedAndFilteredNameDescNicknameAsc.sublist(3, 7); | 554 var models = await runQueryWithExponentialBackoff( |
| 544 compareModels(expectedModels, models); | 555 query, expectedModels.length); |
| 545 }); | 556 compareModels(expectedModels, models); |
| 546 }, | 557 }, |
| 547 | 558 |
| 548 // Expando queries: Filter on normal property. | 559 // Expando queries: Filter on normal property. |
| 549 () { | 560 () async { |
| 550 return store.query(ExpandoPerson, partition: partition) | 561 var query = store.query(ExpandoPerson, partition: partition) |
| 551 ..filter('name =', expandoPersons.last.name) | 562 ..filter('name =', expandoPersons.last.name) |
| 552 ..run().toList().then((List<db.Model> models) { | 563 ..run(); |
| 553 compareModels([expandoPersons.last], models); | 564 var models = await runQueryWithExponentialBackoff(query, 1); |
| 554 }); | 565 compareModels([expandoPersons.last], models); |
| 555 }, | 566 }, |
| 556 // Expando queries: Filter on expanded String property | 567 // Expando queries: Filter on expanded String property |
| 557 () { | 568 () async { |
| 558 return store.query(ExpandoPerson, partition: partition) | 569 var query = store.query(ExpandoPerson, partition: partition) |
| 559 ..filter('foo =', expandoPersons.last.foo) | 570 ..filter('foo =', expandoPersons.last.foo) |
| 560 ..run().toList().then((List<db.Model> models) { | 571 ..run(); |
| 561 compareModels([expandoPersons.last], models); | 572 var models = await runQueryWithExponentialBackoff(query, 1); |
| 562 }); | 573 compareModels([expandoPersons.last], models); |
| 563 }, | 574 }, |
| 564 // Expando queries: Filter on expanded int property | 575 // Expando queries: Filter on expanded int property |
| 565 () { | 576 () async { |
| 566 return store.query(ExpandoPerson, partition: partition) | 577 var query = store.query(ExpandoPerson, partition: partition) |
| 567 ..filter('bar =', expandoPersons.last.bar) | 578 ..filter('bar =', expandoPersons.last.bar) |
| 568 ..run().toList().then((List<db.Model> models) { | 579 ..run(); |
| 569 compareModels([expandoPersons.last], models); | 580 var models = await runQueryWithExponentialBackoff(query, 1); |
| 570 }); | 581 compareModels([expandoPersons.last], models); |
| 571 }, | 582 }, |
| 572 // Expando queries: Filter normal property with different | 583 // Expando queries: Filter normal property with different |
| 573 // propertyName (datastore name is 'NN'). | 584 // propertyName (datastore name is 'NN'). |
| 574 () { | 585 () async { |
| 575 return store.query(ExpandoPerson, partition: partition) | 586 var query = store.query(ExpandoPerson, partition: partition) |
| 576 ..filter('nickname =', expandoPersons.last.nickname) | 587 ..filter('nickname =', expandoPersons.last.nickname) |
| 577 ..run().toList().then((List<db.Model> models) { | 588 ..run(); |
| 578 compareModels([expandoPersons.last], models); | 589 var models = await runQueryWithExponentialBackoff(query, 1); |
| 579 }); | 590 compareModels([expandoPersons.last], models); |
| 580 }, | 591 }, |
| 581 | 592 |
| 582 // Delete results | 593 // Delete results |
| 583 () => store.commit(deletes: allKeys), | 594 () => store.commit(deletes: allKeys), |
| 584 | 595 |
| 585 // Wait until the entity deletes are reflected in the indices. | 596 // Wait until the entity deletes are reflected in the indices. |
| 586 () => waitUntilEntitiesGone(store, allKeys, partition), | 597 () => waitUntilEntitiesGone(store, allKeys, partition), |
| 587 | 598 |
| 588 // Make sure queries don't return results | 599 // Make sure queries don't return results |
| 589 () => store.lookup(allKeys).then((List<db.Model> models) { | 600 () => store.lookup(allKeys).then((List<db.Model> models) { |
| 590 expect(models.length, equals(allKeys.length)); | 601 expect(models.length, equals(allKeys.length)); |
| 591 for (var model in models) { | 602 for (var model in models) { |
| 592 expect(model, isNull); | 603 expect(model, isNull); |
| 593 } | 604 } |
| 594 }), | 605 }), |
| 595 ]; | 606 ]; |
| 596 return Future.forEach(tests, (f) => f()); | 607 return Future.forEach(tests, (f) => f()); |
| 597 }); | 608 }); |
| 598 }); | 609 }); |
| 599 }); | 610 }); |
| 600 }); | 611 }); |
| 601 } | 612 } |
| 602 | 613 |
| 614 Future<List<db.Model>> runQueryWithExponentialBackoff( | |
|
Søren Gjesse
2015/12/14 07:53:24
This looks like a good idea.
| |
| 615 db.Query query, int expectedResults) async { | |
| 616 for (int i = 0; i <= 6; i++) { | |
| 617 if (i > 0) { | |
| 618 // Wait for 0.1s, 0.2s, ..., 12.8s | |
| 619 var duration = new Duration(milliseconds: 100 * (2 << i)); | |
| 620 print("Running query did return less results than expected." | |
| 621 "Using exponential backoff: Sleeping for $duration."); | |
| 622 await sleep(duration); | |
| 623 } | |
| 624 | |
| 625 List<db.Model> models = await query.run().toList(); | |
| 626 if (models.length >= expectedResults) { | |
| 627 return models; | |
| 628 } | |
| 629 } | |
| 630 | |
| 631 throw new Exception( | |
| 632 "Tried running a query with exponential backoff, giving up now."); | |
| 633 } | |
| 634 | |
| 603 Future waitUntilEntitiesReady(db.DatastoreDB mdb, | 635 Future waitUntilEntitiesReady(db.DatastoreDB mdb, |
| 604 List<db.Key> keys, | 636 List<db.Key> keys, |
| 605 db.Partition partition) { | 637 db.Partition partition) { |
| 606 return waitUntilEntitiesHelper(mdb, keys, true, partition); | 638 return waitUntilEntitiesHelper(mdb, keys, true, partition); |
| 607 } | 639 } |
| 608 | 640 |
| 609 Future waitUntilEntitiesGone(db.DatastoreDB mdb, | 641 Future waitUntilEntitiesGone(db.DatastoreDB mdb, |
| 610 List<db.Key> keys, | 642 List<db.Key> keys, |
| 611 db.Partition partition) { | 643 db.Partition partition) { |
| 612 return waitUntilEntitiesHelper(mdb, keys, false, partition); | 644 return waitUntilEntitiesHelper(mdb, keys, false, partition); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 648 var scopes = datastore_impl.DatastoreImpl.SCOPES; | 680 var scopes = datastore_impl.DatastoreImpl.SCOPES; |
| 649 | 681 |
| 650 withAuthClient(scopes, (String project, httpClient) { | 682 withAuthClient(scopes, (String project, httpClient) { |
| 651 var datastore = new datastore_impl.DatastoreImpl(httpClient, 's~$project'); | 683 var datastore = new datastore_impl.DatastoreImpl(httpClient, 's~$project'); |
| 652 return datastore_test.cleanupDB(datastore, null).then((_) { | 684 return datastore_test.cleanupDB(datastore, null).then((_) { |
| 653 return runE2EUnittest( | 685 return runE2EUnittest( |
| 654 () => runTests(new db.DatastoreDB(datastore), null)); | 686 () => runTests(new db.DatastoreDB(datastore), null)); |
| 655 }); | 687 }); |
| 656 }); | 688 }); |
| 657 } | 689 } |
| OLD | NEW |