Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: pkg/observe/test/path_observer_test.dart

Issue 420673002: Roll polymer packages to version 0.3.4 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 import 'dart:async'; 5 import 'dart:async';
6 import 'package:observe/observe.dart'; 6 import 'package:observe/observe.dart';
7 import 'package:unittest/unittest.dart'; 7 import 'package:unittest/unittest.dart';
8 import 'package:observe/src/path_observer.dart'
9 show getSegmentsOfPropertyPathForTesting,
10 observerSentinelForTesting;
11
8 import 'observe_test_utils.dart'; 12 import 'observe_test_utils.dart';
9 13
10 // This file contains code ported from: 14 // This file contains code ported from:
11 // https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js 15 // https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js
12 // Dart note: getting invalid properties is an error, unlike in JS where it 16 // Dart note: getting invalid properties is an error, unlike in JS where it
13 // returns undefined. This difference comes up where we check for _throwsNSM in 17 // returns undefined. This difference comes up where we check for _throwsNSM in
14 // the tests below. 18 // the tests below.
15 main() => dirtyCheckZone().run(() { 19 main() => dirtyCheckZone().run(() {
16 group('PathObserver', observePathTests); 20 group('PathObserver', observePathTests);
17 21
18 group('PropertyPath', () { 22 group('PropertyPath', () {
19 test('toString length', () { 23 test('toString length', () {
20 expectPath(p, str, len) { 24 expectPath(p, str, len, [keys]) {
21 var path = new PropertyPath(p); 25 var path = new PropertyPath(p);
22 expect(path.toString(), str); 26 expect(path.toString(), str);
23 expect(path.length, len, reason: 'expected path length $len for $path'); 27 expect(path.length, len, reason: 'expected path length $len for $path');
28 if (keys == null) {
29 expect(path.isValid, isFalse);
30 } else {
31 expect(path.isValid, isTrue);
32 expect(getSegmentsOfPropertyPathForTesting(path), keys);
33 }
24 } 34 }
25 35
26 expectPath('/foo', '<invalid path>', 0); 36 expectPath('/foo', '<invalid path>', 0);
27 expectPath('abc', 'abc', 1); 37 expectPath('1.abc', '<invalid path>', 0);
28 expectPath('a.b.c', 'a.b.c', 3); 38 expectPath('abc', 'abc', 1, [#abc]);
29 expectPath('a.b.c ', 'a.b.c', 3); 39 expectPath('a.b.c', 'a.b.c', 3, [#a, #b, #c]);
30 expectPath(' a.b.c', 'a.b.c', 3); 40 expectPath('a.b.c ', 'a.b.c', 3, [#a, #b, #c]);
31 expectPath(' a.b.c ', 'a.b.c', 3); 41 expectPath(' a.b.c', 'a.b.c', 3, [#a, #b, #c]);
32 expectPath('1.abc', '1.abc', 2); 42 expectPath(' a.b.c ', 'a.b.c', 3, [#a, #b, #c]);
33 expectPath([#qux], 'qux', 1); 43 expectPath('[1].abc', '[1].abc', 2, [1, #abc]);
34 expectPath([1, #foo, #bar], '1.foo.bar', 3); 44 expectPath([#qux], 'qux', 1, [#qux]);
45 expectPath([1, #foo, #bar], '[1].foo.bar', 3, [1, #foo, #bar]);
46 expectPath([1, #foo, 'bar'], '[1].foo["bar"]', 3, [1, #foo, 'bar']);
47
48 // From test.js: "path validity" test:
49
50 expectPath('', '', 0, []);
51 expectPath(' ', '', 0, []);
52 expectPath(null, '', 0, []);
53 expectPath('a', 'a', 1, [#a]);
54 expectPath('a.b', 'a.b', 2, [#a, #b]);
55 expectPath('a. b', 'a.b', 2, [#a, #b]);
56 expectPath('a .b', 'a.b', 2, [#a, #b]);
57 expectPath('a . b', 'a.b', 2, [#a, #b]);
58 expectPath(' a . b ', 'a.b', 2, [#a, #b]);
59 expectPath('a[0]', 'a[0]', 2, [#a, 0]);
60 expectPath('a [0]', 'a[0]', 2, [#a, 0]);
61 expectPath('a[0][1]', 'a[0][1]', 3, [#a, 0, 1]);
62 expectPath('a [ 0 ] [ 1 ] ', 'a[0][1]', 3, [#a, 0, 1]);
63 expectPath('[1234567890] ', '[1234567890]', 1, [1234567890]);
64 expectPath(' [1234567890] ', '[1234567890]', 1, [1234567890]);
65 expectPath('opt0', 'opt0', 1, [#opt0]);
66 // Dart note: Modified to avoid a private Dart symbol:
67 expectPath(r'$foo.$bar.baz_', r'$foo.$bar.baz_', 3,
68 [#$foo, #$bar, #baz_]);
69 // Dart note: this test is different because we treat ["baz"] always as a
70 // indexing operation.
71 expectPath('foo["baz"]', 'foo.baz', 2, [#foo, #baz]);
72 expectPath('foo["b\\"az"]', 'foo["b\\"az"]', 2, [#foo, 'b"az']);
73 expectPath("foo['b\\'az']", 'foo["b\'az"]', 2, [#foo, "b'az"]);
74 expectPath([#a, #b], 'a.b', 2, [#a, #b]);
75
76 expectPath('.', '<invalid path>', 0);
77 expectPath(' . ', '<invalid path>', 0);
78 expectPath('..', '<invalid path>', 0);
79 expectPath('a[4', '<invalid path>', 0);
80 expectPath('a.b.', '<invalid path>', 0);
81 expectPath('a,b', '<invalid path>', 0);
82 expectPath('a["foo]', '<invalid path>', 0);
83 expectPath('[0x04]', '<invalid path>', 0);
84 expectPath('[0foo]', '<invalid path>', 0);
85 expectPath('[foo-bar]', '<invalid path>', 0);
86 expectPath('foo-bar', '<invalid path>', 0);
87 expectPath('42', '<invalid path>', 0);
88 expectPath('a[04]', '<invalid path>', 0);
89 expectPath(' a [ 04 ]', '<invalid path>', 0);
90 expectPath(' 42 ', '<invalid path>', 0);
91 expectPath('foo["bar]', '<invalid path>', 0);
92 expectPath("foo['bar]", '<invalid path>', 0);
35 }); 93 });
36 94
95 test('objects with toString are not supported', () {
96 // Dart note: this was intentionally not ported. See path_observer.dart.
97 expect(() => new PropertyPath([new Foo('a'), new Foo('b')]), throws);
98 });
99
100 test('invalid path returns null value', () {
101 var path = new PropertyPath('a b');
102 expect(path.isValid, isFalse);
103 expect(path.getValueFrom({'a': {'b': 2}}), isNull);
104 });
105
106
37 test('caching and ==', () { 107 test('caching and ==', () {
38 var start = new PropertyPath('abc.0'); 108 var start = new PropertyPath('abc[0]');
39 for (int i = 1; i <= 100; i++) { 109 for (int i = 1; i <= 100; i++) {
40 expect(identical(new PropertyPath('abc.0'), start), true, 110 expect(identical(new PropertyPath('abc[0]'), start), true,
41 reason: 'should return identical path'); 111 reason: 'should return identical path');
42 112
43 var p = new PropertyPath('abc.$i'); 113 var p = new PropertyPath('abc[$i]');
44 expect(identical(p, start), false, 114 expect(identical(p, start), false,
45 reason: 'different paths should not be merged'); 115 reason: 'different paths should not be merged');
46 } 116 }
47 var end = new PropertyPath('abc.0'); 117 var end = new PropertyPath('abc[0]');
48 expect(identical(end, start), false, 118 expect(identical(end, start), false,
49 reason: 'first entry expired'); 119 reason: 'first entry expired');
50 expect(end, start, reason: 'different instances are equal'); 120 expect(end, start, reason: 'different instances are equal');
51 }); 121 });
52 122
53 test('hashCode equal', () { 123 test('hashCode equal', () {
54 var a = new PropertyPath([#foo, 2, #bar]); 124 var a = new PropertyPath([#foo, 2, #bar]);
55 var b = new PropertyPath('foo.2.bar'); 125 var b = new PropertyPath('foo[2].bar');
56 expect(identical(a, b), false, reason: 'only strings cached'); 126 expect(identical(a, b), false, reason: 'only strings cached');
57 expect(a, b, reason: 'same paths are equal'); 127 expect(a, b, reason: 'same paths are equal');
58 expect(a.hashCode, b.hashCode, reason: 'equal hashCodes'); 128 expect(a.hashCode, b.hashCode, reason: 'equal hashCodes');
59 }); 129 });
60 130
61 test('hashCode not equal', () { 131 test('hashCode not equal', () {
62 expect(2.hashCode, isNot(3.hashCode), 132 expect(2.hashCode, isNot(3.hashCode),
63 reason: 'test depends on 2 and 3 having different hashcodes'); 133 reason: 'test depends on 2 and 3 having different hashcodes');
64 134
65 var a = new PropertyPath([2]); 135 var a = new PropertyPath([2]);
66 var b = new PropertyPath([3]); 136 var b = new PropertyPath([3]);
67 expect(a, isNot(b), reason: 'different paths'); 137 expect(a, isNot(b), reason: 'different paths');
68 expect(a.hashCode, isNot(b.hashCode), reason: 'different hashCodes'); 138 expect(a.hashCode, isNot(b.hashCode), reason: 'different hashCodes');
69 }); 139 });
70 }); 140 });
141
142 group('CompoundObserver', compoundObserverTests);
71 }); 143 });
72 144
73 observePathTests() { 145 observePathTests() {
74 test('Degenerate Values', () { 146 test('Degenerate Values', () {
75 expect(new PathObserver(null, '').value, null); 147 expect(new PathObserver(null, '').value, null);
76 expect(new PathObserver(123, '').value, 123); 148 expect(new PathObserver(123, '').value, 123);
77 expect(() => new PathObserver(123, 'foo.bar.baz').value, _throwsNSM('foo')); 149 expect(() => new PathObserver(123, 'foo.bar.baz').value, _throwsNSM('foo'));
78 150
79 // shouldn't throw: 151 // shouldn't throw:
80 new PathObserver(123, '')..open((_) {})..close(); 152 new PathObserver(123, '')..open((_) {})..close();
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
196 arr['bat'] = 'boot'; 268 arr['bat'] = 'boot';
197 269
198 }).then(newMicrotask).then((_) { 270 }).then(newMicrotask).then((_) {
199 expect(fooValues, ['baz']); 271 expect(fooValues, ['baz']);
200 expect(batValues, []); 272 expect(batValues, []);
201 }); 273 });
202 }); 274 });
203 275
204 test('Path Value With Indices', () { 276 test('Path Value With Indices', () {
205 var model = toObservable([]); 277 var model = toObservable([]);
206 var path = new PathObserver(model, '0'); 278 var path = new PathObserver(model, '[0]');
207 path.open(expectAsync((x) { 279 path.open(expectAsync((x) {
208 expect(path.value, 123); 280 expect(path.value, 123);
209 expect(x, 123); 281 expect(x, 123);
210 })); 282 }));
211 model.add(123); 283 model.add(123);
212 }); 284 });
213 285
214 group('ObservableList', () { 286 group('ObservableList', () {
215 test('isNotEmpty', () { 287 test('isNotEmpty', () {
216 var model = new ObservableList(); 288 var model = new ObservableList();
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 model.log.clear(); 426 model.log.clear();
355 observer = new PathObserver(model, 'bar'); 427 observer = new PathObserver(model, 'bar');
356 expect(observer.value, null, reason: 'path not found'); 428 expect(observer.value, null, reason: 'path not found');
357 expect(model.log, ['[] bar']); 429 expect(model.log, ['[] bar']);
358 model.log.clear(); 430 model.log.clear();
359 431
360 observer.value = 42; 432 observer.value = 42;
361 expect(model.log, ['[]= bar 42']); 433 expect(model.log, ['[]= bar 42']);
362 model.log.clear(); 434 model.log.clear();
363 }); 435 });
436
437 test('regression for TemplateBinding#161', () {
438 var model = toObservable({'obj': toObservable({'bar': false})});
439 var ob1 = new PathObserver(model, 'obj.bar');
440 var called = false;
441 ob1.open(() { called = true; });
442
443 var obj2 = new PathObserver(model, 'obj');
444 obj2.open(() { model['obj']['bar'] = true; });
445
446 model['obj'] = toObservable({ 'obj': 'obj' });
447
448 return new Future(() {})
449 .then((_) => expect(called, true));
450 });
364 } 451 }
365 452
453 compoundObserverTests() {
454 var model;
455 var observer;
456 bool called;
457 var newValues;
458 var oldValues;
459 var observed;
460
461 setUp(() {
462 model = new TestModel(1, 2, 3);
463 called = false;
464 });
465
466 callback(a, b, c) {
467 called = true;
468 newValues = a;
469 oldValues = b;
470 observed = c;
471 }
472
473 reset() {
474 called = false;
475 newValues = null;
476 oldValues = null;
477 observed = null;
478 }
479
480 expectNoChanges() {
481 observer.deliver();
482 expect(called, isFalse);
483 expect(newValues, isNull);
484 expect(oldValues, isNull);
485 expect(observed, isNull);
486 }
487
488 expectCompoundPathChanges(expectedNewValues,
489 expectedOldValues, expectedObserved, [dontDeliver = false]) {
jakemac 2014/07/25 16:40:33 [deliver = true] would be easier to comprehend her
Siggi Cherem (dart-lang) 2014/07/25 17:45:12 agree :) - I renamed it and made it a named argume
490 if (!dontDeliver) observer.deliver();
491 expect(called, isTrue);
492
493 expect(newValues, expectedNewValues);
494 var oldValuesAsMap = {};
495 for (int i = 0; i < expectedOldValues.length; i++) {
496 if (expectedOldValues[i] != null) {
497 oldValuesAsMap[i] = expectedOldValues[i];
498 }
499 }
500 expect(oldValues, oldValuesAsMap);
501 expect(observed, expectedObserved);
502
503 reset();
504 }
505
506 tearDown(() {
507 observer.close();
508 reset();
509 });
510
511 _path(s) => new PropertyPath(s);
512
513 test('simple', () {
514 observer = new CompoundObserver();
515 observer.addPath(model, 'a');
516 observer.addPath(model, 'b');
517 observer.addPath(model, _path('c'));
518 observer.open(callback);
519 expectNoChanges();
520
521 var expectedObs = [model, _path('a'), model, _path('b'), model, _path('c')];
522 model.a = -10;
523 model.b = 20;
524 model.c = 30;
525 expectCompoundPathChanges([-10, 20, 30], [1, 2, 3], expectedObs);
526
527 model.a = 'a';
528 model.c = 'c';
529 expectCompoundPathChanges(['a', 20, 'c'], [-10, null, 30], expectedObs);
530
531 model.a = 2;
532 model.b = 3;
533 model.c = 4;
534 expectCompoundPathChanges([2, 3, 4], ['a', 20, 'c'], expectedObs);
535
536 model.a = 'z';
537 model.b = 'y';
538 model.c = 'x';
539 expect(observer.value, ['z', 'y', 'x']);
540 expectNoChanges();
541
542 expect(model.a, 'z');
543 expect(model.b, 'y');
544 expect(model.c, 'x');
545 expectNoChanges();
546 });
547
548 test('reportChangesOnOpen', () {
549 observer = new CompoundObserver(true);
550 observer.addPath(model, 'a');
551 observer.addPath(model, 'b');
552 observer.addPath(model, _path('c'));
553
554 model.a = -10;
555 model.b = 20;
556 observer.open(callback);
557 var expectedObs = [model, _path('a'), model, _path('b'), model, _path('c')];
558 expectCompoundPathChanges([-10, 20, 3], [1, 2, null], expectedObs, true);
559 });
560
561 test('All Observers', () {
562 observer = new CompoundObserver();
563 var pathObserver1 = new PathObserver(model, 'a');
564 var pathObserver2 = new PathObserver(model, 'b');
565 var pathObserver3 = new PathObserver(model, _path('c'));
566
567 observer.addObserver(pathObserver1);
568 observer.addObserver(pathObserver2);
569 observer.addObserver(pathObserver3);
570 observer.open(callback);
571
572 var expectedObs = [observerSentinelForTesting, pathObserver1,
573 observerSentinelForTesting, pathObserver2,
574 observerSentinelForTesting, pathObserver3];
575 model.a = -10;
576 model.b = 20;
577 model.c = 30;
578 expectCompoundPathChanges([-10, 20, 30], [1, 2, 3], expectedObs);
579
580 model.a = 'a';
581 model.c = 'c';
582 expectCompoundPathChanges(['a', 20, 'c'], [-10, null, 30], expectedObs);
583 });
584
585 test('Degenerate Values', () {
586 observer = new CompoundObserver();
587 observer.addPath(model, '.'); // invalid path
588 observer.addPath('obj-value', ''); // empty path
589 // Dart note: we don't port these two tests because in Dart we produce
590 // exceptions for these invalid paths.
591 // observer.addPath(model, 'foo'); // unreachable
592 // observer.addPath(3, 'bar'); // non-object with non-empty path
593 var values = observer.open(callback);
594 expect(values.length, 2);
595 expect(values[0], null);
596 expect(values[1], 'obj-value');
597 observer.close();
598 });
599
600 test('Heterogeneous', () {
601 model.c = null;
602 var otherModel = new TestModel(null, null, 3);
603
604 twice(value) => value * 2;
605 half(value) => value ~/ 2;
606
607 var compound = new CompoundObserver();
608 compound.addPath(model, 'a');
609 compound.addObserver(new ObserverTransform(new PathObserver(model, 'b'),
610 twice, setValue: half));
611 compound.addObserver(new PathObserver(otherModel, 'c'));
612
613 combine(values) => values[0] + values[1] + values[2];
614 observer = new ObserverTransform(compound, combine);
615
616 var newValue;
617 transformCallback(v) {
618 newValue = v;
619 called = true;
620 }
621 expect(observer.open(transformCallback), 8);
622
623 model.a = 2;
624 model.b = 4;
625 observer.deliver();
626 expect(called, isTrue);
627 expect(newValue, 13);
628 called = false;
629
630 model.b = 10;
631 otherModel.c = 5;
632 observer.deliver();
633 expect(called, isTrue);
634 expect(newValue, 27);
635 called = false;
636
637 model.a = 20;
638 model.b = 1;
639 otherModel.c = 5;
640 observer.deliver();
641 expect(called, isFalse);
642 expect(newValue, 27);
643 });
644 }
645
366 /// A matcher that checks that a closure throws a NoSuchMethodError matching the 646 /// A matcher that checks that a closure throws a NoSuchMethodError matching the
367 /// given [name]. 647 /// given [name].
368 _throwsNSM(String name) => throwsA(_isNoSuchMethodOf(name)); 648 _throwsNSM(String name) => throwsA(_isNoSuchMethodOf(name));
369 649
370 /// A matcher that checkes whether an exception is a NoSuchMethodError matching 650 /// A matcher that checkes whether an exception is a NoSuchMethodError matching
371 /// the given [name]. 651 /// the given [name].
372 _isNoSuchMethodOf(String name) => predicate((e) => 652 _isNoSuchMethodOf(String name) => predicate((e) =>
373 e is NoSuchMethodError && 653 e is NoSuchMethodError &&
374 // Dart2js and VM error messages are a bit different, but they both contain 654 // Dart2js and VM error messages are a bit different, but they both contain
375 // the name. 655 // the name.
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
419 operator []=(index, value) { 699 operator []=(index, value) {
420 log.add('[]= $index $value'); 700 log.add('[]= $index $value');
421 if (index == 'foo') _foo = value; 701 if (index == 'foo') _foo = value;
422 } 702 }
423 } 703 }
424 704
425 @reflectable 705 @reflectable
426 class TestModel extends ChangeNotifier { 706 class TestModel extends ChangeNotifier {
427 var _a, _b, _c; 707 var _a, _b, _c;
428 708
429 TestModel(); 709 TestModel([this._a, this._b, this._c]);
430 710
431 get a => _a; 711 get a => _a;
432 712
433 void set a(newValue) { 713 void set a(newValue) {
434 _a = notifyPropertyChange(#a, _a, newValue); 714 _a = notifyPropertyChange(#a, _a, newValue);
435 } 715 }
436 716
437 get b => _b; 717 get b => _b;
438 718
439 void set b(newValue) { 719 void set b(newValue) {
440 _b = notifyPropertyChange(#b, _b, newValue); 720 _b = notifyPropertyChange(#b, _b, newValue);
441 } 721 }
442 722
443 get c => _c; 723 get c => _c;
444 724
445 void set c(newValue) { 725 void set c(newValue) {
446 _c = notifyPropertyChange(#c, _c, newValue); 726 _c = notifyPropertyChange(#c, _c, newValue);
447 } 727 }
448 } 728 }
449 729
450 class WatcherModel extends Observable { 730 class WatcherModel extends Observable {
451 // TODO(jmesserly): dart2js does not let these be on the same line: 731 // TODO(jmesserly): dart2js does not let these be on the same line:
452 // @observable var a, b, c; 732 // @observable var a, b, c;
453 @observable var a; 733 @observable var a;
454 @observable var b; 734 @observable var b;
455 @observable var c; 735 @observable var c;
456 736
457 WatcherModel(); 737 WatcherModel();
458 } 738 }
739
740 class Foo {
741 var value;
742 Foo(this.value);
743 String toString() => 'Foo$value';
744 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698