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

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

Issue 213713002: Reapply change that makes path-observer more agressive with property lookups. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 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
« no previous file with comments | « pkg/observe/lib/src/path_observer.dart ('k') | pkg/polymer_expressions/lib/eval.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 'observe_test_utils.dart'; 8 import 'observe_test_utils.dart';
9 9
10 // This file contains code ported from: 10 // This file contains code ported from:
11 // https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js 11 // https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js
12 12 // 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
14 // the tests below.
13 main() => dirtyCheckZone().run(() { 15 main() => dirtyCheckZone().run(() {
14 group('PathObserver', observePathTests); 16 group('PathObserver', observePathTests);
15 17
16 group('PropertyPath', () { 18 group('PropertyPath', () {
17 test('toString length', () { 19 test('toString length', () {
18 expectPath(p, str, len) { 20 expectPath(p, str, len) {
19 var path = new PropertyPath(p); 21 var path = new PropertyPath(p);
20 expect(path.toString(), str); 22 expect(path.toString(), str);
21 expect(path.length, len, reason: 'expected path length $len for $path'); 23 expect(path.length, len, reason: 'expected path length $len for $path');
22 } 24 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 reason: 'test depends on 2 and 3 having different hashcodes'); 63 reason: 'test depends on 2 and 3 having different hashcodes');
62 64
63 var a = new PropertyPath([2]); 65 var a = new PropertyPath([2]);
64 var b = new PropertyPath([3]); 66 var b = new PropertyPath([3]);
65 expect(a, isNot(b), reason: 'different paths'); 67 expect(a, isNot(b), reason: 'different paths');
66 expect(a.hashCode, isNot(b.hashCode), reason: 'different hashCodes'); 68 expect(a.hashCode, isNot(b.hashCode), reason: 'different hashCodes');
67 }); 69 });
68 }); 70 });
69 }); 71 });
70 72
71
72 observePathTests() { 73 observePathTests() {
73 test('Degenerate Values', () { 74 test('Degenerate Values', () {
74 expect(new PathObserver(null, '').value, null); 75 expect(new PathObserver(null, '').value, null);
75 expect(new PathObserver(123, '').value, 123); 76 expect(new PathObserver(123, '').value, 123);
76 expect(new PathObserver(123, 'foo.bar.baz').value, null); 77 expect(() => new PathObserver(123, 'foo.bar.baz').value, _throwsNSM('foo'));
77 78
78 // shouldn't throw: 79 // shouldn't throw:
79 new PathObserver(123, '')..open((_) {})..close(); 80 new PathObserver(123, '')..open((_) {})..close();
80 new PropertyPath('').setValueFrom(null, null); 81 new PropertyPath('').setValueFrom(null, null);
81 new PropertyPath('').setValueFrom(123, 42); 82 new PropertyPath('').setValueFrom(123, 42);
82 new PropertyPath('foo.bar.baz').setValueFrom(123, 42); 83 expect(() => new PropertyPath('foo.bar.baz').setValueFrom(123, 42),
83 84 _throwsNSM('foo'));
84 var foo = {}; 85 var foo = {};
85 expect(new PathObserver(foo, '').value, foo); 86 expect(new PathObserver(foo, '').value, foo);
86 87
87 foo = new Object(); 88 foo = new Object();
88 expect(new PathObserver(foo, '').value, foo); 89 expect(new PathObserver(foo, '').value, foo);
89 90
90 expect(new PathObserver(foo, 'a/3!').value, null); 91 expect(new PathObserver(foo, 'a/3!').value, null);
91 }); 92 });
92 93
93 test('get value at path ObservableBox', () { 94 test('get value at path ObservableBox', () {
94 var obj = new ObservableBox(new ObservableBox(new ObservableBox(1))); 95 var obj = new ObservableBox(new ObservableBox(new ObservableBox(1)));
95 96
96 expect(new PathObserver(obj, '').value, obj); 97 expect(new PathObserver(obj, '').value, obj);
97 expect(new PathObserver(obj, 'value').value, obj.value); 98 expect(new PathObserver(obj, 'value').value, obj.value);
98 expect(new PathObserver(obj, 'value.value').value, obj.value.value); 99 expect(new PathObserver(obj, 'value.value').value, obj.value.value);
99 expect(new PathObserver(obj, 'value.value.value').value, 1); 100 expect(new PathObserver(obj, 'value.value.value').value, 1);
100 101
101 obj.value.value.value = 2; 102 obj.value.value.value = 2;
102 expect(new PathObserver(obj, 'value.value.value').value, 2); 103 expect(new PathObserver(obj, 'value.value.value').value, 2);
103 104
104 obj.value.value = new ObservableBox(3); 105 obj.value.value = new ObservableBox(3);
105 expect(new PathObserver(obj, 'value.value.value').value, 3); 106 expect(new PathObserver(obj, 'value.value.value').value, 3);
106 107
107 obj.value = new ObservableBox(4); 108 obj.value = new ObservableBox(4);
108 expect(new PathObserver(obj, 'value.value.value').value, null); 109 expect(() => new PathObserver(obj, 'value.value.value').value,
110 _throwsNSM('value'));
109 expect(new PathObserver(obj, 'value.value').value, 4); 111 expect(new PathObserver(obj, 'value.value').value, 4);
110 }); 112 });
111 113
112 114
113 test('get value at path ObservableMap', () { 115 test('get value at path ObservableMap', () {
114 var obj = toObservable({'a': {'b': {'c': 1}}}); 116 var obj = toObservable({'a': {'b': {'c': 1}}});
115 117
116 expect(new PathObserver(obj, '').value, obj); 118 expect(new PathObserver(obj, '').value, obj);
117 expect(new PathObserver(obj, 'a').value, obj['a']); 119 expect(new PathObserver(obj, 'a').value, obj['a']);
118 expect(new PathObserver(obj, 'a.b').value, obj['a']['b']); 120 expect(new PathObserver(obj, 'a.b').value, obj['a']['b']);
119 expect(new PathObserver(obj, 'a.b.c').value, 1); 121 expect(new PathObserver(obj, 'a.b.c').value, 1);
120 122
121 obj['a']['b']['c'] = 2; 123 obj['a']['b']['c'] = 2;
122 expect(new PathObserver(obj, 'a.b.c').value, 2); 124 expect(new PathObserver(obj, 'a.b.c').value, 2);
123 125
124 obj['a']['b'] = toObservable({'c': 3}); 126 obj['a']['b'] = toObservable({'c': 3});
125 expect(new PathObserver(obj, 'a.b.c').value, 3); 127 expect(new PathObserver(obj, 'a.b.c').value, 3);
126 128
127 obj['a'] = toObservable({'b': 4}); 129 obj['a'] = toObservable({'b': 4});
128 expect(new PathObserver(obj, 'a.b.c').value, null); 130 expect(() => new PathObserver(obj, 'a.b.c').value, _throwsNSM('c'));
129 expect(new PathObserver(obj, 'a.b').value, 4); 131 expect(new PathObserver(obj, 'a.b').value, 4);
130 }); 132 });
131 133
132 test('set value at path', () { 134 test('set value at path', () {
133 var obj = toObservable({}); 135 var obj = toObservable({});
134 new PropertyPath('foo').setValueFrom(obj, 3); 136 new PropertyPath('foo').setValueFrom(obj, 3);
135 expect(obj['foo'], 3); 137 expect(obj['foo'], 3);
136 138
137 var bar = toObservable({ 'baz': 3 }); 139 var bar = toObservable({ 'baz': 3 });
138 new PropertyPath('bar').setValueFrom(obj, bar); 140 new PropertyPath('bar').setValueFrom(obj, bar);
139 expect(obj['bar'], bar); 141 expect(obj['bar'], bar);
140 142
141 new PropertyPath('bar.baz.bat').setValueFrom(obj, 'not here'); 143 expect(() => new PropertyPath('bar.baz.bat').setValueFrom(obj, 'not here'),
142 expect(new PathObserver(obj, 'bar.baz.bat').value, null); 144 _throwsNSM('bat='));
145 expect(() => new PathObserver(obj, 'bar.baz.bat').value, _throwsNSM('bat'));
143 }); 146 });
144 147
145 test('set value back to same', () { 148 test('set value back to same', () {
146 var obj = toObservable({}); 149 var obj = toObservable({});
147 var path = new PathObserver(obj, 'foo'); 150 var path = new PathObserver(obj, 'foo');
148 var values = []; 151 var values = [];
149 path.open((x) { 152 path.open((x) {
150 expect(x, path.value, reason: 'callback should get current value'); 153 expect(x, path.value, reason: 'callback should get current value');
151 values.add(x); 154 values.add(x);
152 }); 155 });
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 }); 235 });
233 }); 236 });
234 237
235 for (var createModel in [() => new TestModel(), () => new WatcherModel()]) { 238 for (var createModel in [() => new TestModel(), () => new WatcherModel()]) {
236 test('Path Observation - ${createModel().runtimeType}', () { 239 test('Path Observation - ${createModel().runtimeType}', () {
237 var model = createModel()..a = 240 var model = createModel()..a =
238 (createModel()..b = (createModel()..c = 'hello, world')); 241 (createModel()..b = (createModel()..c = 'hello, world'));
239 242
240 var path = new PathObserver(model, 'a.b.c'); 243 var path = new PathObserver(model, 'a.b.c');
241 var lastValue = null; 244 var lastValue = null;
242 path.open((x) { lastValue = x; }); 245 var errorSeen = false;
246 runZoned(() {
247 path.open((x) { lastValue = x; });
248 }, onError: (e) {
249 expect(e, _isNoSuchMethodOf('c'));
250 errorSeen = true;
251 });
243 252
244 model.a.b.c = 'hello, mom'; 253 model.a.b.c = 'hello, mom';
245 254
246 expect(lastValue, null); 255 expect(lastValue, null);
247 return new Future(() { 256 return new Future(() {
248 expect(lastValue, 'hello, mom'); 257 expect(lastValue, 'hello, mom');
249 258
250 model.a.b = createModel()..c = 'hello, dad'; 259 model.a.b = createModel()..c = 'hello, dad';
251 }).then(newMicrotask).then((_) { 260 }).then(newMicrotask).then((_) {
252 expect(lastValue, 'hello, dad'); 261 expect(lastValue, 'hello, dad');
253 262
254 model.a = createModel()..b = 263 model.a = createModel()..b =
255 (createModel()..c = 'hello, you'); 264 (createModel()..c = 'hello, you');
256 }).then(newMicrotask).then((_) { 265 }).then(newMicrotask).then((_) {
257 expect(lastValue, 'hello, you'); 266 expect(lastValue, 'hello, you');
258 267
259 model.a.b = 1; 268 model.a.b = 1;
269 expect(errorSeen, isFalse);
260 }).then(newMicrotask).then((_) { 270 }).then(newMicrotask).then((_) {
261 expect(lastValue, null); 271 expect(errorSeen, isTrue);
272 expect(lastValue, 'hello, you');
262 273
263 // Stop observing 274 // Stop observing
264 path.close(); 275 path.close();
265 276
266 model.a.b = createModel()..c = 'hello, back again -- but not observing'; 277 model.a.b = createModel()..c = 'hello, back again -- but not observing';
267 }).then(newMicrotask).then((_) { 278 }).then(newMicrotask).then((_) {
268 expect(lastValue, null); 279 expect(lastValue, 'hello, you');
269 280
270 // Resume observing 281 // Resume observing
271 new PathObserver(model, 'a.b.c').open((x) { lastValue = x; }); 282 new PathObserver(model, 'a.b.c').open((x) { lastValue = x; });
272 283
273 model.a.b.c = 'hello. Back for reals'; 284 model.a.b.c = 'hello. Back for reals';
274 }).then(newMicrotask).then((_) { 285 }).then(newMicrotask).then((_) {
275 expect(lastValue, 'hello. Back for reals'); 286 expect(lastValue, 'hello. Back for reals');
276 }); 287 });
277 }); 288 });
278 } 289 }
(...skipping 14 matching lines...) Expand all
293 model['a'] = 3; 304 model['a'] = 3;
294 }).then(newMicrotask).then((_) { 305 }).then(newMicrotask).then((_) {
295 expect(values, [1, 2]); 306 expect(values, [1, 2]);
296 }); 307 });
297 }); 308 });
298 309
299 test('errors thrown from getter/setter', () { 310 test('errors thrown from getter/setter', () {
300 var model = new ObjectWithErrors(); 311 var model = new ObjectWithErrors();
301 var observer = new PathObserver(model, 'foo'); 312 var observer = new PathObserver(model, 'foo');
302 313
303 expect(() => observer.value, throws); 314 expect(() => observer.value, _throwsNSM('bar'));
304 expect(model.getFooCalled, 1); 315 expect(model.getFooCalled, 1);
305 316
306 expect(() { observer.value = 123; }, throws); 317 expect(() { observer.value = 123; }, _throwsNSM('bar='));
307 expect(model.setFooCalled, [123]); 318 expect(model.setFooCalled, [123]);
308 }); 319 });
309 320
310 test('object with noSuchMethod', () { 321 test('object with noSuchMethod', () {
311 var model = new NoSuchMethodModel(); 322 var model = new NoSuchMethodModel();
312 var observer = new PathObserver(model, 'foo'); 323 var observer = new PathObserver(model, 'foo');
313 324
314 expect(observer.value, 42); 325 expect(observer.value, 42);
315 observer.value = 'hi'; 326 observer.value = 'hi';
316 expect(model._foo, 'hi'); 327 expect(model._foo, 'hi');
(...skipping 28 matching lines...) Expand all
345 expect(observer.value, null, reason: 'path not found'); 356 expect(observer.value, null, reason: 'path not found');
346 expect(model.log, ['[] bar']); 357 expect(model.log, ['[] bar']);
347 model.log.clear(); 358 model.log.clear();
348 359
349 observer.value = 42; 360 observer.value = 42;
350 expect(model.log, ['[]= bar 42']); 361 expect(model.log, ['[]= bar 42']);
351 model.log.clear(); 362 model.log.clear();
352 }); 363 });
353 } 364 }
354 365
366 /// A matcher that checks that a closure throws a NoSuchMethodError matching the
367 /// given [name].
368 _throwsNSM(String name) => throwsA(_isNoSuchMethodOf(name));
369
370 /// A matcher that checkes whether an exception is a NoSuchMethodError matching
371 /// the given [name].
372 _isNoSuchMethodOf(String name) => predicate((e) =>
373 e is NoSuchMethodError &&
374 // Dart2js and VM error messages are a bit different, but they both contain
375 // the name.
376 ('$e'.contains("'$name'") || // VM error
377 '$e'.contains('\'Symbol("$name")\''))); // dart2js error
378
355 class ObjectWithErrors { 379 class ObjectWithErrors {
356 int getFooCalled = 0; 380 int getFooCalled = 0;
357 List setFooCalled = []; 381 List setFooCalled = [];
358 @reflectable get foo { 382 @reflectable get foo {
359 getFooCalled++; 383 getFooCalled++;
360 (this as dynamic).bar; 384 (this as dynamic).bar;
361 } 385 }
362 @reflectable set foo(value) { 386 @reflectable set foo(value) {
363 setFooCalled.add(value); 387 setFooCalled.add(value);
364 (this as dynamic).bar = value; 388 (this as dynamic).bar = value;
(...skipping 11 matching lines...) Expand all
376 log.add(name); 400 log.add(name);
377 if (name == #foo && invocation.isGetter) return _foo; 401 if (name == #foo && invocation.isGetter) return _foo;
378 if (name == const Symbol('foo=')) { 402 if (name == const Symbol('foo=')) {
379 _foo = invocation.positionalArguments[0]; 403 _foo = invocation.positionalArguments[0];
380 return null; 404 return null;
381 } 405 }
382 return super.noSuchMethod(invocation); 406 return super.noSuchMethod(invocation);
383 } 407 }
384 } 408 }
385 409
386 class IndexerModel { 410 class IndexerModel implements Indexable<String, dynamic> {
387 var _foo = 42; 411 var _foo = 42;
388 List log = []; 412 List log = [];
389 413
390 operator [](index) { 414 operator [](index) {
391 log.add('[] $index'); 415 log.add('[] $index');
392 if (index == 'foo') return _foo; 416 if (index == 'foo') return _foo;
393 } 417 }
394 418
395 operator []=(index, value) { 419 operator []=(index, value) {
396 log.add('[]= $index $value'); 420 log.add('[]= $index $value');
(...skipping 28 matching lines...) Expand all
425 449
426 class WatcherModel extends Observable { 450 class WatcherModel extends Observable {
427 // TODO(jmesserly): dart2js does not let these be on the same line: 451 // TODO(jmesserly): dart2js does not let these be on the same line:
428 // @observable var a, b, c; 452 // @observable var a, b, c;
429 @observable var a; 453 @observable var a;
430 @observable var b; 454 @observable var b;
431 @observable var c; 455 @observable var c;
432 456
433 WatcherModel(); 457 WatcherModel();
434 } 458 }
OLDNEW
« no previous file with comments | « pkg/observe/lib/src/path_observer.dart ('k') | pkg/polymer_expressions/lib/eval.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698