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

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

Issue 2989763002: Update charted to 0.4.8 and roll (Closed)
Patch Set: Removed Cutch from list of reviewers Created 3 years, 4 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:async';
6 import 'package:observe/observe.dart';
7 import 'package:unittest/unittest.dart';
8 import 'package:observe/src/path_observer.dart'
9 show getSegmentsOfPropertyPathForTesting,
10 observerSentinelForTesting;
11
12 import 'observe_test_utils.dart';
13
14 import 'package:observe/mirrors_used.dart'; // make test smaller.
15 import 'package:smoke/mirrors.dart';
16
17 // This file contains code ported from:
18 // https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js
19 // Dart note: getting invalid properties is an error, unlike in JS where it
20 // returns undefined. This difference comes up where we check for _throwsNSM in
21 // the tests below.
22 main() => dirtyCheckZone().run(() {
23 useMirrors();
24
25 group('PathObserver', observePathTests);
26
27 group('PropertyPath', () {
28 test('toString length', () {
29 expectPath(p, str, len, [keys]) {
30 var path = new PropertyPath(p);
31 expect(path.toString(), str);
32 expect(path.length, len, reason: 'expected path length $len for $path');
33 if (keys == null) {
34 expect(path.isValid, isFalse);
35 } else {
36 expect(path.isValid, isTrue);
37 expect(getSegmentsOfPropertyPathForTesting(path), keys);
38 }
39 }
40
41 expectPath('/foo', '<invalid path>', 0);
42 expectPath('1.abc', '<invalid path>', 0);
43 expectPath('abc', 'abc', 1, [#abc]);
44 expectPath('a.b.c', 'a.b.c', 3, [#a, #b, #c]);
45 expectPath('a.b.c ', 'a.b.c', 3, [#a, #b, #c]);
46 expectPath(' a.b.c', 'a.b.c', 3, [#a, #b, #c]);
47 expectPath(' a.b.c ', 'a.b.c', 3, [#a, #b, #c]);
48 expectPath('[1].abc', '[1].abc', 2, [1, #abc]);
49 expectPath([#qux], 'qux', 1, [#qux]);
50 expectPath([1, #foo, #bar], '[1].foo.bar', 3, [1, #foo, #bar]);
51 expectPath([1, #foo, 'bar'], '[1].foo["bar"]', 3, [1, #foo, 'bar']);
52
53 // From test.js: "path validity" test:
54
55 expectPath('', '', 0, []);
56 expectPath(' ', '', 0, []);
57 expectPath(null, '', 0, []);
58 expectPath('a', 'a', 1, [#a]);
59 expectPath('a.b', 'a.b', 2, [#a, #b]);
60 expectPath('a. b', 'a.b', 2, [#a, #b]);
61 expectPath('a .b', 'a.b', 2, [#a, #b]);
62 expectPath('a . b', 'a.b', 2, [#a, #b]);
63 expectPath(' a . b ', 'a.b', 2, [#a, #b]);
64 expectPath('a[0]', 'a[0]', 2, [#a, 0]);
65 expectPath('a [0]', 'a[0]', 2, [#a, 0]);
66 expectPath('a[0][1]', 'a[0][1]', 3, [#a, 0, 1]);
67 expectPath('a [ 0 ] [ 1 ] ', 'a[0][1]', 3, [#a, 0, 1]);
68 expectPath('[1234567890] ', '[1234567890]', 1, [1234567890]);
69 expectPath(' [1234567890] ', '[1234567890]', 1, [1234567890]);
70 expectPath('opt0', 'opt0', 1, [#opt0]);
71 // Dart note: Modified to avoid a private Dart symbol:
72 expectPath(r'$foo.$bar.baz_', r'$foo.$bar.baz_', 3,
73 [#$foo, #$bar, #baz_]);
74 // Dart note: this test is different because we treat ["baz"] always as a
75 // indexing operation.
76 expectPath('foo["baz"]', 'foo.baz', 2, [#foo, #baz]);
77 expectPath('foo["b\\"az"]', 'foo["b\\"az"]', 2, [#foo, 'b"az']);
78 expectPath("foo['b\\'az']", 'foo["b\'az"]', 2, [#foo, "b'az"]);
79 expectPath([#a, #b], 'a.b', 2, [#a, #b]);
80 expectPath([], '', 0, []);
81
82 expectPath('.', '<invalid path>', 0);
83 expectPath(' . ', '<invalid path>', 0);
84 expectPath('..', '<invalid path>', 0);
85 expectPath('a[4', '<invalid path>', 0);
86 expectPath('a.b.', '<invalid path>', 0);
87 expectPath('a,b', '<invalid path>', 0);
88 expectPath('a["foo]', '<invalid path>', 0);
89 expectPath('[0x04]', '<invalid path>', 0);
90 expectPath('[0foo]', '<invalid path>', 0);
91 expectPath('[foo-bar]', '<invalid path>', 0);
92 expectPath('foo-bar', '<invalid path>', 0);
93 expectPath('42', '<invalid path>', 0);
94 expectPath('a[04]', '<invalid path>', 0);
95 expectPath(' a [ 04 ]', '<invalid path>', 0);
96 expectPath(' 42 ', '<invalid path>', 0);
97 expectPath('foo["bar]', '<invalid path>', 0);
98 expectPath("foo['bar]", '<invalid path>', 0);
99 });
100
101 test('objects with toString are not supported', () {
102 // Dart note: this was intentionally not ported. See path_observer.dart.
103 expect(() => new PropertyPath([new Foo('a'), new Foo('b')]), throws);
104 });
105
106 test('invalid path returns null value', () {
107 var path = new PropertyPath('a b');
108 expect(path.isValid, isFalse);
109 expect(path.getValueFrom({'a': {'b': 2}}), isNull);
110 });
111
112
113 test('caching and ==', () {
114 var start = new PropertyPath('abc[0]');
115 for (int i = 1; i <= 100; i++) {
116 expect(identical(new PropertyPath('abc[0]'), start), true,
117 reason: 'should return identical path');
118
119 var p = new PropertyPath('abc[$i]');
120 expect(identical(p, start), false,
121 reason: 'different paths should not be merged');
122 }
123 var end = new PropertyPath('abc[0]');
124 expect(identical(end, start), false,
125 reason: 'first entry expired');
126 expect(end, start, reason: 'different instances are equal');
127 });
128
129 test('hashCode equal', () {
130 var a = new PropertyPath([#foo, 2, #bar]);
131 var b = new PropertyPath('foo[2].bar');
132 expect(identical(a, b), false, reason: 'only strings cached');
133 expect(a, b, reason: 'same paths are equal');
134 expect(a.hashCode, b.hashCode, reason: 'equal hashCodes');
135 });
136
137 test('hashCode not equal', () {
138 expect(2.hashCode, isNot(3.hashCode),
139 reason: 'test depends on 2 and 3 having different hashcodes');
140
141 var a = new PropertyPath([2]);
142 var b = new PropertyPath([3]);
143 expect(a, isNot(b), reason: 'different paths');
144 expect(a.hashCode, isNot(b.hashCode), reason: 'different hashCodes');
145 });
146 });
147
148 group('CompoundObserver', compoundObserverTests);
149 });
150
151 observePathTests() {
152 test('Degenerate Values', () {
153 expect(new PathObserver(null, '').value, null);
154 expect(new PathObserver(123, '').value, 123);
155 expect(() => new PathObserver(123, 'foo.bar.baz').value, _throwsNSM('foo'));
156
157 // shouldn't throw:
158 new PathObserver(123, '')..open((_) {})..close();
159 new PropertyPath('').setValueFrom(null, null);
160 new PropertyPath('').setValueFrom(123, 42);
161 expect(() => new PropertyPath('foo.bar.baz').setValueFrom(123, 42),
162 _throwsNSM('foo'));
163 var foo = {};
164 expect(new PathObserver(foo, '').value, foo);
165
166 foo = new Object();
167 expect(new PathObserver(foo, '').value, foo);
168
169 expect(new PathObserver(foo, 'a/3!').value, null);
170 });
171
172 test('get value at path ObservableBox', () {
173 var obj = new ObservableBox(new ObservableBox(new ObservableBox(1)));
174
175 expect(new PathObserver(obj, '').value, obj);
176 expect(new PathObserver(obj, 'value').value, obj.value);
177 expect(new PathObserver(obj, 'value.value').value, obj.value.value);
178 expect(new PathObserver(obj, 'value.value.value').value, 1);
179
180 obj.value.value.value = 2;
181 expect(new PathObserver(obj, 'value.value.value').value, 2);
182
183 obj.value.value = new ObservableBox(3);
184 expect(new PathObserver(obj, 'value.value.value').value, 3);
185
186 obj.value = new ObservableBox(4);
187 expect(() => new PathObserver(obj, 'value.value.value').value,
188 _throwsNSM('value'));
189 expect(new PathObserver(obj, 'value.value').value, 4);
190 });
191
192
193 test('get value at path ObservableMap', () {
194 var obj = toObservable({'a': {'b': {'c': 1}}});
195
196 expect(new PathObserver(obj, '').value, obj);
197 expect(new PathObserver(obj, 'a').value, obj['a']);
198 expect(new PathObserver(obj, 'a.b').value, obj['a']['b']);
199 expect(new PathObserver(obj, 'a.b.c').value, 1);
200
201 obj['a']['b']['c'] = 2;
202 expect(new PathObserver(obj, 'a.b.c').value, 2);
203
204 obj['a']['b'] = toObservable({'c': 3});
205 expect(new PathObserver(obj, 'a.b.c').value, 3);
206
207 obj['a'] = toObservable({'b': 4});
208 expect(() => new PathObserver(obj, 'a.b.c').value, _throwsNSM('c'));
209 expect(new PathObserver(obj, 'a.b').value, 4);
210 });
211
212 test('set value at path', () {
213 var obj = toObservable({});
214 new PropertyPath('foo').setValueFrom(obj, 3);
215 expect(obj['foo'], 3);
216
217 var bar = toObservable({ 'baz': 3 });
218 new PropertyPath('bar').setValueFrom(obj, bar);
219 expect(obj['bar'], bar);
220
221 expect(() => new PropertyPath('bar.baz.bat').setValueFrom(obj, 'not here'),
222 _throwsNSM('bat='));
223 expect(() => new PathObserver(obj, 'bar.baz.bat').value, _throwsNSM('bat'));
224 });
225
226 test('set value back to same', () {
227 var obj = toObservable({});
228 var path = new PathObserver(obj, 'foo');
229 var values = [];
230 path.open((x) {
231 expect(x, path.value, reason: 'callback should get current value');
232 values.add(x);
233 });
234
235 path.value = 3;
236 expect(obj['foo'], 3);
237 expect(path.value, 3);
238
239 new PropertyPath('foo').setValueFrom(obj, 2);
240 return new Future(() {
241 expect(path.value, 2);
242 expect(new PathObserver(obj, 'foo').value, 2);
243
244 new PropertyPath('foo').setValueFrom(obj, 3);
245 }).then(newMicrotask).then((_) {
246 expect(path.value, 3);
247
248 }).then(newMicrotask).then((_) {
249 expect(values, [2, 3]);
250 });
251 });
252
253 test('Observe and Unobserve - Paths', () {
254 var arr = toObservable({});
255
256 arr['foo'] = 'bar';
257 var fooValues = [];
258 var fooPath = new PathObserver(arr, 'foo');
259 fooPath.open(fooValues.add);
260 arr['foo'] = 'baz';
261 arr['bat'] = 'bag';
262 var batValues = [];
263 var batPath = new PathObserver(arr, 'bat');
264 batPath.open(batValues.add);
265
266 return new Future(() {
267 expect(fooValues, ['baz']);
268 expect(batValues, []);
269
270 arr['foo'] = 'bar';
271 fooPath.close();
272 arr['bat'] = 'boo';
273 batPath.close();
274 arr['bat'] = 'boot';
275
276 }).then(newMicrotask).then((_) {
277 expect(fooValues, ['baz']);
278 expect(batValues, []);
279 });
280 });
281
282 test('Path Value With Indices', () {
283 var model = toObservable([]);
284 var path = new PathObserver(model, '[0]');
285 path.open(expectAsync((x) {
286 expect(path.value, 123);
287 expect(x, 123);
288 }));
289 model.add(123);
290 });
291
292 group('ObservableList', () {
293 test('isNotEmpty', () {
294 var model = new ObservableList();
295 var path = new PathObserver(model, 'isNotEmpty');
296 expect(path.value, false);
297
298 path.open(expectAsync((_) {
299 expect(path.value, true);
300 }));
301 model.add(123);
302 });
303
304 test('isEmpty', () {
305 var model = new ObservableList();
306 var path = new PathObserver(model, 'isEmpty');
307 expect(path.value, true);
308
309 path.open(expectAsync((_) {
310 expect(path.value, false);
311 }));
312 model.add(123);
313 });
314 });
315
316 for (var createModel in [() => new TestModel(), () => new WatcherModel()]) {
317 test('Path Observation - ${createModel().runtimeType}', () {
318 var model = createModel()..a =
319 (createModel()..b = (createModel()..c = 'hello, world'));
320
321 var path = new PathObserver(model, 'a.b.c');
322 var lastValue = null;
323 var errorSeen = false;
324 runZoned(() {
325 path.open((x) { lastValue = x; });
326 }, onError: (e) {
327 expect(e, _isNoSuchMethodOf('c'));
328 errorSeen = true;
329 });
330
331 model.a.b.c = 'hello, mom';
332
333 expect(lastValue, null);
334 return new Future(() {
335 expect(lastValue, 'hello, mom');
336
337 model.a.b = createModel()..c = 'hello, dad';
338 }).then(newMicrotask).then((_) {
339 expect(lastValue, 'hello, dad');
340
341 model.a = createModel()..b =
342 (createModel()..c = 'hello, you');
343 }).then(newMicrotask).then((_) {
344 expect(lastValue, 'hello, you');
345
346 model.a.b = 1;
347 expect(errorSeen, isFalse);
348 }).then(newMicrotask).then((_) {
349 expect(errorSeen, isTrue);
350 expect(lastValue, 'hello, you');
351
352 // Stop observing
353 path.close();
354
355 model.a.b = createModel()..c = 'hello, back again -- but not observing';
356 }).then(newMicrotask).then((_) {
357 expect(lastValue, 'hello, you');
358
359 // Resume observing
360 new PathObserver(model, 'a.b.c').open((x) { lastValue = x; });
361
362 model.a.b.c = 'hello. Back for reals';
363 }).then(newMicrotask).then((_) {
364 expect(lastValue, 'hello. Back for reals');
365 });
366 });
367 }
368
369 test('observe map', () {
370 var model = toObservable({'a': 1});
371 var path = new PathObserver(model, 'a');
372
373 var values = [path.value];
374 path.open(values.add);
375 expect(values, [1]);
376
377 model['a'] = 2;
378 return new Future(() {
379 expect(values, [1, 2]);
380
381 path.close();
382 model['a'] = 3;
383 }).then(newMicrotask).then((_) {
384 expect(values, [1, 2]);
385 });
386 });
387
388 test('errors thrown from getter/setter', () {
389 var model = new ObjectWithErrors();
390 var observer = new PathObserver(model, 'foo');
391
392 expect(() => observer.value, _throwsNSM('bar'));
393 expect(model.getFooCalled, 1);
394
395 expect(() { observer.value = 123; }, _throwsNSM('bar='));
396 expect(model.setFooCalled, [123]);
397 });
398
399 test('object with noSuchMethod', () {
400 var model = new NoSuchMethodModel();
401 var observer = new PathObserver(model, 'foo');
402
403 expect(observer.value, 42);
404 observer.value = 'hi';
405 expect(model._foo, 'hi');
406 expect(observer.value, 'hi');
407
408 expect(model.log, [#foo, const Symbol('foo='), #foo]);
409
410 // These shouldn't throw
411 observer = new PathObserver(model, 'bar');
412 expect(observer.value, null, reason: 'path not found');
413 observer.value = 42;
414 expect(observer.value, null, reason: 'path not found');
415 });
416
417 test('object with indexer', () {
418 var model = new IndexerModel();
419 var observer = new PathObserver(model, 'foo');
420
421 expect(observer.value, 42);
422 expect(model.log, ['[] foo']);
423 model.log.clear();
424
425 observer.value = 'hi';
426 expect(model.log, ['[]= foo hi']);
427 expect(model._foo, 'hi');
428
429 expect(observer.value, 'hi');
430
431 // These shouldn't throw
432 model.log.clear();
433 observer = new PathObserver(model, 'bar');
434 expect(observer.value, null, reason: 'path not found');
435 expect(model.log, ['[] bar']);
436 model.log.clear();
437
438 observer.value = 42;
439 expect(model.log, ['[]= bar 42']);
440 model.log.clear();
441 });
442
443 test('regression for TemplateBinding#161', () {
444 var model = toObservable({'obj': toObservable({'bar': false})});
445 var ob1 = new PathObserver(model, 'obj.bar');
446 var called = false;
447 ob1.open(() { called = true; });
448
449 var obj2 = new PathObserver(model, 'obj');
450 obj2.open(() { model['obj']['bar'] = true; });
451
452 model['obj'] = toObservable({ 'obj': 'obj' });
453
454 return new Future(() {})
455 .then((_) => expect(called, true));
456 });
457 }
458
459 compoundObserverTests() {
460 var model;
461 var observer;
462 bool called;
463 var newValues;
464 var oldValues;
465 var observed;
466
467 setUp(() {
468 model = new TestModel(1, 2, 3);
469 called = false;
470 });
471
472 callback(a, b, c) {
473 called = true;
474 newValues = a;
475 oldValues = b;
476 observed = c;
477 }
478
479 reset() {
480 called = false;
481 newValues = null;
482 oldValues = null;
483 observed = null;
484 }
485
486 expectNoChanges() {
487 observer.deliver();
488 expect(called, isFalse);
489 expect(newValues, isNull);
490 expect(oldValues, isNull);
491 expect(observed, isNull);
492 }
493
494 expectCompoundPathChanges(expectedNewValues,
495 expectedOldValues, expectedObserved, {deliver: true}) {
496 if (deliver) observer.deliver();
497 expect(called, isTrue);
498
499 expect(newValues, expectedNewValues);
500 var oldValuesAsMap = {};
501 for (int i = 0; i < expectedOldValues.length; i++) {
502 if (expectedOldValues[i] != null) {
503 oldValuesAsMap[i] = expectedOldValues[i];
504 }
505 }
506 expect(oldValues, oldValuesAsMap);
507 expect(observed, expectedObserved);
508
509 reset();
510 }
511
512 tearDown(() {
513 observer.close();
514 reset();
515 });
516
517 _path(s) => new PropertyPath(s);
518
519 test('simple', () {
520 observer = new CompoundObserver();
521 observer.addPath(model, 'a');
522 observer.addPath(model, 'b');
523 observer.addPath(model, _path('c'));
524 observer.open(callback);
525 expectNoChanges();
526
527 var expectedObs = [model, _path('a'), model, _path('b'), model, _path('c')];
528 model.a = -10;
529 model.b = 20;
530 model.c = 30;
531 expectCompoundPathChanges([-10, 20, 30], [1, 2, 3], expectedObs);
532
533 model.a = 'a';
534 model.c = 'c';
535 expectCompoundPathChanges(['a', 20, 'c'], [-10, null, 30], expectedObs);
536
537 model.a = 2;
538 model.b = 3;
539 model.c = 4;
540 expectCompoundPathChanges([2, 3, 4], ['a', 20, 'c'], expectedObs);
541
542 model.a = 'z';
543 model.b = 'y';
544 model.c = 'x';
545 expect(observer.value, ['z', 'y', 'x']);
546 expectNoChanges();
547
548 expect(model.a, 'z');
549 expect(model.b, 'y');
550 expect(model.c, 'x');
551 expectNoChanges();
552 });
553
554 test('reportChangesOnOpen', () {
555 observer = new CompoundObserver(true);
556 observer.addPath(model, 'a');
557 observer.addPath(model, 'b');
558 observer.addPath(model, _path('c'));
559
560 model.a = -10;
561 model.b = 20;
562 observer.open(callback);
563 var expectedObs = [model, _path('a'), model, _path('b'), model, _path('c')];
564 expectCompoundPathChanges([-10, 20, 3], [1, 2, null], expectedObs,
565 deliver: false);
566 });
567
568 test('All Observers', () {
569 observer = new CompoundObserver();
570 var pathObserver1 = new PathObserver(model, 'a');
571 var pathObserver2 = new PathObserver(model, 'b');
572 var pathObserver3 = new PathObserver(model, _path('c'));
573
574 observer.addObserver(pathObserver1);
575 observer.addObserver(pathObserver2);
576 observer.addObserver(pathObserver3);
577 observer.open(callback);
578
579 var expectedObs = [observerSentinelForTesting, pathObserver1,
580 observerSentinelForTesting, pathObserver2,
581 observerSentinelForTesting, pathObserver3];
582 model.a = -10;
583 model.b = 20;
584 model.c = 30;
585 expectCompoundPathChanges([-10, 20, 30], [1, 2, 3], expectedObs);
586
587 model.a = 'a';
588 model.c = 'c';
589 expectCompoundPathChanges(['a', 20, 'c'], [-10, null, 30], expectedObs);
590 });
591
592 test('Degenerate Values', () {
593 observer = new CompoundObserver();
594 observer.addPath(model, '.'); // invalid path
595 observer.addPath('obj-value', ''); // empty path
596 // Dart note: we don't port these two tests because in Dart we produce
597 // exceptions for these invalid paths.
598 // observer.addPath(model, 'foo'); // unreachable
599 // observer.addPath(3, 'bar'); // non-object with non-empty path
600 var values = observer.open(callback);
601 expect(values.length, 2);
602 expect(values[0], null);
603 expect(values[1], 'obj-value');
604 observer.close();
605 });
606
607 test('Heterogeneous', () {
608 model.c = null;
609 var otherModel = new TestModel(null, null, 3);
610
611 twice(value) => value * 2;
612 half(value) => value ~/ 2;
613
614 var compound = new CompoundObserver();
615 compound.addPath(model, 'a');
616 compound.addObserver(new ObserverTransform(new PathObserver(model, 'b'),
617 twice, setValue: half));
618 compound.addObserver(new PathObserver(otherModel, 'c'));
619
620 combine(values) => values[0] + values[1] + values[2];
621 observer = new ObserverTransform(compound, combine);
622
623 var newValue;
624 transformCallback(v) {
625 newValue = v;
626 called = true;
627 }
628 expect(observer.open(transformCallback), 8);
629
630 model.a = 2;
631 model.b = 4;
632 observer.deliver();
633 expect(called, isTrue);
634 expect(newValue, 13);
635 called = false;
636
637 model.b = 10;
638 otherModel.c = 5;
639 observer.deliver();
640 expect(called, isTrue);
641 expect(newValue, 27);
642 called = false;
643
644 model.a = 20;
645 model.b = 1;
646 otherModel.c = 5;
647 observer.deliver();
648 expect(called, isFalse);
649 expect(newValue, 27);
650 });
651 }
652
653 /// A matcher that checks that a closure throws a NoSuchMethodError matching the
654 /// given [name].
655 _throwsNSM(String name) => throwsA(_isNoSuchMethodOf(name));
656
657 /// A matcher that checkes whether an exception is a NoSuchMethodError matching
658 /// the given [name].
659 _isNoSuchMethodOf(String name) => predicate((e) =>
660 e is NoSuchMethodError &&
661 // Dart2js and VM error messages are a bit different, but they both contain
662 // the name.
663 ('$e'.contains("'$name'") || // VM error
664 '$e'.contains('\'Symbol("$name")\''))); // dart2js error
665
666 class ObjectWithErrors {
667 int getFooCalled = 0;
668 List setFooCalled = [];
669 @reflectable get foo {
670 getFooCalled++;
671 (this as dynamic).bar;
672 }
673 @reflectable set foo(value) {
674 setFooCalled.add(value);
675 (this as dynamic).bar = value;
676 }
677 }
678
679 class NoSuchMethodModel {
680 var _foo = 42;
681 List log = [];
682
683 // TODO(ahe): Remove @reflectable from here (once either of
684 // http://dartbug.com/15408 or http://dartbug.com/15409 are fixed).
685 @reflectable noSuchMethod(Invocation invocation) {
686 final name = invocation.memberName;
687 log.add(name);
688 if (name == #foo && invocation.isGetter) return _foo;
689 if (name == const Symbol('foo=')) {
690 _foo = invocation.positionalArguments[0];
691 return null;
692 }
693 return super.noSuchMethod(invocation);
694 }
695 }
696
697 class IndexerModel implements Indexable<String, dynamic> {
698 var _foo = 42;
699 List log = [];
700
701 operator [](index) {
702 log.add('[] $index');
703 if (index == 'foo') return _foo;
704 }
705
706 operator []=(index, value) {
707 log.add('[]= $index $value');
708 if (index == 'foo') _foo = value;
709 }
710 }
711
712 @reflectable
713 class TestModel extends ChangeNotifier {
714 var _a, _b, _c;
715
716 TestModel([this._a, this._b, this._c]);
717
718 get a => _a;
719
720 void set a(newValue) {
721 _a = notifyPropertyChange(#a, _a, newValue);
722 }
723
724 get b => _b;
725
726 void set b(newValue) {
727 _b = notifyPropertyChange(#b, _b, newValue);
728 }
729
730 get c => _c;
731
732 void set c(newValue) {
733 _c = notifyPropertyChange(#c, _c, newValue);
734 }
735 }
736
737 class WatcherModel extends Observable {
738 // TODO(jmesserly): dart2js does not let these be on the same line:
739 // @observable var a, b, c;
740 @observable var a;
741 @observable var b;
742 @observable var c;
743
744 WatcherModel();
745 }
746
747 class Foo {
748 var value;
749 Foo(this.value);
750 String toString() => 'Foo$value';
751 }
OLDNEW
« no previous file with comments | « packages/observe/test/observe_test_utils.dart ('k') | packages/observe/test/transformer_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698