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

Side by Side Diff: third_party/pkg/angular/test/change_detection/watch_group_spec.dart

Issue 180843004: Revert revision 33053 (Closed) Base URL: http://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
OLDNEW
(Empty)
1 library watch_group_spec;
2
3 import '../_specs.dart';
4 import 'package:angular/change_detection/watch_group.dart';
5 import 'package:angular/change_detection/dirty_checking_change_detector.dart';
6 import 'dirty_checking_change_detector_spec.dart' hide main;
7
8 main() => describe('WatchGroup', () {
9 var context;
10 var watchGrp;
11 DirtyCheckingChangeDetector changeDetector;
12 Logger logger;
13
14 AST parse(String expression) {
15 var currentAST = new ContextReferenceAST();
16 expression.split('.').forEach((name) {
17 currentAST = new FieldReadAST(currentAST, name);
18 });
19 return currentAST;
20 }
21
22 expectOrder(list) {
23 logger.clear();
24 watchGrp.detectChanges(); // Clear the initial queue
25 logger.clear();
26 watchGrp.detectChanges();
27 expect(logger).toEqual(list);
28 }
29
30 beforeEach(inject((Logger _logger) {
31 context = {};
32 changeDetector = new DirtyCheckingChangeDetector(new GetterCache({}));
33 watchGrp = new RootWatchGroup(changeDetector, context);
34 logger = _logger;
35 }));
36
37 describe('watch lifecycle', () {
38 it('should prevent reaction fn on removed', () {
39 context['a'] = 'hello';
40 var watch ;
41 watchGrp.watch(parse('a'), (v, p) {
42 logger('removed');
43 watch.remove();
44 });
45 watch = watchGrp.watch(parse('a'), (v, p) => logger(v));
46 watchGrp.detectChanges();
47 expect(logger).toEqual(['removed']);
48 });
49 });
50
51 describe('property chaining', () {
52 it('should read property', () {
53 context['a'] = 'hello';
54
55 // should fire on initial adding
56 expect(watchGrp.fieldCost).toEqual(0);
57 var watch = watchGrp.watch(parse('a'), (v, p) => logger(v));
58 expect(watch.expression).toEqual('a');
59 expect(watchGrp.fieldCost).toEqual(1);
60 watchGrp.detectChanges();
61 expect(logger).toEqual(['hello']);
62
63 // make sore no new changes are logged on extra detectChanges
64 watchGrp.detectChanges();
65 expect(logger).toEqual(['hello']);
66
67 // Should detect value change
68 context['a'] = 'bye';
69 watchGrp.detectChanges();
70 expect(logger).toEqual(['hello', 'bye']);
71
72 // should cleanup after itself
73 watch.remove();
74 expect(watchGrp.fieldCost).toEqual(0);
75 context['a'] = 'cant see me';
76 watchGrp.detectChanges();
77 expect(logger).toEqual(['hello', 'bye']);
78 });
79
80 it('should read property chain', () {
81 context['a'] = {'b': 'hello'};
82
83 // should fire on initial adding
84 expect(watchGrp.fieldCost).toEqual(0);
85 expect(changeDetector.count).toEqual(0);
86 var watch = watchGrp.watch(parse('a.b'), (v, p) => logger(v));
87 expect(watch.expression).toEqual('a.b');
88 expect(watchGrp.fieldCost).toEqual(2);
89 expect(changeDetector.count).toEqual(2);
90 watchGrp.detectChanges();
91 expect(logger).toEqual(['hello']);
92
93 // make sore no new changes are logged on extra detectChanges
94 watchGrp.detectChanges();
95 expect(logger).toEqual(['hello']);
96
97 // make sure no changes or logged when intermediary object changes
98 context['a'] = {'b': 'hello'};
99 watchGrp.detectChanges();
100 expect(logger).toEqual(['hello']);
101
102 // Should detect value change
103 context['a'] = {'b': 'hello2'};
104 watchGrp.detectChanges();
105 expect(logger).toEqual(['hello', 'hello2']);
106
107 // Should detect value change
108 context['a']['b'] = 'bye';
109 watchGrp.detectChanges();
110 expect(logger).toEqual(['hello', 'hello2', 'bye']);
111
112 // should cleanup after itself
113 watch.remove();
114 expect(watchGrp.fieldCost).toEqual(0);
115 context['a']['b'] = 'cant see me';
116 watchGrp.detectChanges();
117 expect(logger).toEqual(['hello', 'hello2', 'bye']);
118 });
119
120 it('should reuse handlers', () {
121 var user1 = {'first': 'misko', 'last': 'hevery'};
122 var user2 = {'first': 'misko', 'last': 'Hevery'};
123
124 context['user'] = user1;
125
126 // should fire on initial adding
127 expect(watchGrp.fieldCost).toEqual(0);
128 var watch = watchGrp.watch(parse('user'), (v, p) => logger(v));
129 var watchFirst = watchGrp.watch(parse('user.first'), (v, p) => logger(v));
130 var watchLast = watchGrp.watch(parse('user.last'), (v, p) => logger(v));
131 expect(watchGrp.fieldCost).toEqual(3);
132
133 watchGrp.detectChanges();
134 expect(logger).toEqual([user1, 'misko', 'hevery']);
135 logger.clear();
136
137 context['user'] = user2;
138 watchGrp.detectChanges();
139 expect(logger).toEqual([user2, 'Hevery']);
140
141
142 watch.remove();
143 expect(watchGrp.fieldCost).toEqual(3);
144
145 watchFirst.remove();
146 expect(watchGrp.fieldCost).toEqual(2);
147
148 watchLast.remove();
149 expect(watchGrp.fieldCost).toEqual(0);
150
151 expect(() => watch.remove()).toThrow('Already deleted!');
152 });
153
154 it('should eval pure FunctionApply', () {
155 context['a'] = {'val': 1};
156
157 FunctionApply fn = new LoggingFunctionApply(logger);
158 var watch = watchGrp.watch(
159 new PureFunctionAST('add', fn, [parse('a.val')]),
160 (v, p) => logger(v)
161 );
162
163 // a; a.val; b; b.val;
164 expect(watchGrp.fieldCost).toEqual(2);
165 // add
166 expect(watchGrp.evalCost).toEqual(1);
167
168 watchGrp.detectChanges();
169 expect(logger).toEqual([[1], null]);
170 logger.clear();
171
172 context['a'] = {'val': 2};
173 watchGrp.detectChanges();
174 expect(logger).toEqual([[2]]);
175 });
176
177
178 it('should eval pure function', () {
179 context['a'] = {'val': 1};
180 context['b'] = {'val': 2};
181
182 var watch = watchGrp.watch(
183 new PureFunctionAST('add',
184 (a, b) { logger('+'); return a+b; },
185 [parse('a.val'), parse('b.val')]
186 ),
187 (v, p) => logger(v)
188 );
189
190 // a; a.val; b; b.val;
191 expect(watchGrp.fieldCost).toEqual(4);
192 // add
193 expect(watchGrp.evalCost).toEqual(1);
194
195 watchGrp.detectChanges();
196 expect(logger).toEqual(['+', 3]);
197
198 // extra checks should not trigger functions
199 watchGrp.detectChanges();
200 watchGrp.detectChanges();
201 expect(logger).toEqual(['+', 3]);
202
203 // multiple arg changes should only trigger function once.
204 context['a']['val'] = 3;
205 context['b']['val'] = 4;
206
207 watchGrp.detectChanges();
208 expect(logger).toEqual(['+', 3, '+', 7]);
209
210 watch.remove();
211 expect(watchGrp.fieldCost).toEqual(0);
212 expect(watchGrp.evalCost).toEqual(0);
213
214 context['a']['val'] = 0;
215 context['b']['val'] = 0;
216
217 watchGrp.detectChanges();
218 expect(logger).toEqual(['+', 3, '+', 7]);
219 });
220
221
222 it('should eval chained pure function', () {
223 context['a'] = {'val': 1};
224 context['b'] = {'val': 2};
225 context['c'] = {'val': 3};
226
227 var a_plus_b = new PureFunctionAST('add1',
228 (a, b) { logger('$a+$b'); return a + b; },
229 [parse('a.val'), parse('b.val')]);
230
231 var a_plus_b_plus_c = new PureFunctionAST('add2',
232 (b, c) { logger('$b+$c'); return b + c; },
233 [a_plus_b, parse('c.val')]);
234
235 var watch = watchGrp.watch(a_plus_b_plus_c, (v, p) => logger(v));
236
237 // a; a.val; b; b.val; c; c.val;
238 expect(watchGrp.fieldCost).toEqual(6);
239 // add
240 expect(watchGrp.evalCost).toEqual(2);
241
242 watchGrp.detectChanges();
243 expect(logger).toEqual(['1+2', '3+3', 6]);
244 logger.clear();
245
246 // extra checks should not trigger functions
247 watchGrp.detectChanges();
248 watchGrp.detectChanges();
249 expect(logger).toEqual([]);
250 logger.clear();
251
252 // multiple arg changes should only trigger function once.
253 context['a']['val'] = 3;
254 context['b']['val'] = 4;
255 context['c']['val'] = 5;
256 watchGrp.detectChanges();
257 expect(logger).toEqual(['3+4', '7+5', 12]);
258 logger.clear();
259
260 context['a']['val'] = 9;
261 watchGrp.detectChanges();
262 expect(logger).toEqual(['9+4', '13+5', 18]);
263 logger.clear();
264
265 context['c']['val'] = 9;
266 watchGrp.detectChanges();
267 expect(logger).toEqual(['13+9', 22]);
268 logger.clear();
269
270
271 watch.remove();
272 expect(watchGrp.fieldCost).toEqual(0);
273 expect(watchGrp.evalCost).toEqual(0);
274
275 context['a']['val'] = 0;
276 context['b']['val'] = 0;
277
278 watchGrp.detectChanges();
279 expect(logger).toEqual([]);
280 });
281
282
283 it('should eval closure', () {
284 var obj;
285 obj = {
286 'methodA': (arg1) {
287 logger('methodA($arg1) => ${obj['valA']}');
288 return obj['valA'];
289 },
290 'valA': 'A'
291 };
292 context['obj'] = obj;
293 context['arg0'] = 1;
294
295 var watch = watchGrp.watch(
296 new MethodAST(parse('obj'), 'methodA', [parse('arg0')]),
297 (v, p) => logger(v)
298 );
299
300 // obj, arg0;
301 expect(watchGrp.fieldCost).toEqual(2);
302 // methodA()
303 expect(watchGrp.evalCost).toEqual(1);
304
305 watchGrp.detectChanges();
306 expect(logger).toEqual(['methodA(1) => A', 'A']);
307 logger.clear();
308
309 watchGrp.detectChanges();
310 watchGrp.detectChanges();
311 expect(logger).toEqual(['methodA(1) => A', 'methodA(1) => A']);
312 logger.clear();
313
314 obj['valA'] = 'B';
315 context['arg0'] = 2;
316
317 watchGrp.detectChanges();
318 expect(logger).toEqual(['methodA(2) => B', 'B']);
319 logger.clear();
320
321 watch.remove();
322 expect(watchGrp.fieldCost).toEqual(0);
323 expect(watchGrp.evalCost).toEqual(0);
324
325 obj['valA'] = 'C';
326 context['arg0'] = 3;
327
328 watchGrp.detectChanges();
329 expect(logger).toEqual([]);
330 });
331
332
333 it('should eval method', () {
334 var obj = new MyClass(logger);
335 obj.valA = 'A';
336 context['obj'] = obj;
337 context['arg0'] = 1;
338
339 var watch = watchGrp.watch(
340 new MethodAST(parse('obj'), 'methodA', [parse('arg0')]),
341 (v, p) => logger(v)
342 );
343
344 // obj, arg0;
345 expect(watchGrp.fieldCost).toEqual(2);
346 // methodA()
347 expect(watchGrp.evalCost).toEqual(1);
348
349 watchGrp.detectChanges();
350 expect(logger).toEqual(['methodA(1) => A', 'A']);
351 logger.clear();
352
353 watchGrp.detectChanges();
354 watchGrp.detectChanges();
355 expect(logger).toEqual(['methodA(1) => A', 'methodA(1) => A']);
356 logger.clear();
357
358 obj.valA = 'B';
359 context['arg0'] = 2;
360
361 watchGrp.detectChanges();
362 expect(logger).toEqual(['methodA(2) => B', 'B']);
363 logger.clear();
364
365 watch.remove();
366 expect(watchGrp.fieldCost).toEqual(0);
367 expect(watchGrp.evalCost).toEqual(0);
368
369 obj.valA = 'C';
370 context['arg0'] = 3;
371
372 watchGrp.detectChanges();
373 expect(logger).toEqual([]);
374 });
375
376 it('should eval method chain', () {
377 var obj1 = new MyClass(logger);
378 var obj2 = new MyClass(logger);
379 obj1.valA = obj2;
380 obj2.valA = 'A';
381 context['obj'] = obj1;
382 context['arg0'] = 0;
383 context['arg1'] = 1;
384
385 // obj.methodA(arg0)
386 var ast = new MethodAST(parse('obj'), 'methodA', [parse('arg0')]);
387 ast = new MethodAST(ast, 'methodA', [parse('arg1')]);
388 var watch = watchGrp.watch(ast, (v, p) => logger(v));
389
390 // obj, arg0, arg1;
391 expect(watchGrp.fieldCost).toEqual(3);
392 // methodA(), mothodA()
393 expect(watchGrp.evalCost).toEqual(2);
394
395 watchGrp.detectChanges();
396 expect(logger).toEqual(['methodA(0) => MyClass', 'methodA(1) => A', 'A']);
397 logger.clear();
398
399 watchGrp.detectChanges();
400 watchGrp.detectChanges();
401 expect(logger).toEqual(['methodA(0) => MyClass', 'methodA(1) => A',
402 'methodA(0) => MyClass', 'methodA(1) => A']);
403 logger.clear();
404
405 obj2.valA = 'B';
406 context['arg0'] = 10;
407 context['arg1'] = 11;
408
409 watchGrp.detectChanges();
410 expect(logger).toEqual(['methodA(10) => MyClass', 'methodA(11) => B', 'B'] );
411 logger.clear();
412
413 watch.remove();
414 expect(watchGrp.fieldCost).toEqual(0);
415 expect(watchGrp.evalCost).toEqual(0);
416
417 obj2.valA = 'C';
418 context['arg0'] = 20;
419 context['arg1'] = 21;
420
421 watchGrp.detectChanges();
422 expect(logger).toEqual([]);
423 });
424
425 it('should read connstant', () {
426 // should fire on initial adding
427 expect(watchGrp.fieldCost).toEqual(0);
428 var watch = watchGrp.watch(new ConstantAST(123), (v, p) => logger(v));
429 expect(watch.expression).toEqual('123');
430 expect(watchGrp.fieldCost).toEqual(0);
431 watchGrp.detectChanges();
432 expect(logger).toEqual([123]);
433
434 // make sore no new changes are logged on extra detectChanges
435 watchGrp.detectChanges();
436 expect(logger).toEqual([123]);
437 });
438
439 it('should wrap iterable in ObservableList', () {
440 context['list'] = [];
441 var watch = watchGrp.watch(new CollectionAST(parse('list')), (v, p) => log ger(v));
442
443 expect(watchGrp.fieldCost).toEqual(1);
444 expect(watchGrp.collectionCost).toEqual(1);
445 expect(watchGrp.evalCost).toEqual(0);
446
447 watchGrp.detectChanges();
448 expect(logger.length).toEqual(1);
449 expect(logger[0], toEqualCollectionRecord(
450 collection: [],
451 additions: [],
452 moves: [],
453 removals: []));
454 logger.clear();
455
456 context['list'] = [1];
457 watchGrp.detectChanges();
458 expect(logger.length).toEqual(1);
459 expect(logger[0], toEqualCollectionRecord(
460 collection: ['1[null -> 0]'],
461 additions: ['1[null -> 0]'],
462 moves: [],
463 removals: []));
464 logger.clear();
465
466 watch.remove();
467 expect(watchGrp.fieldCost).toEqual(0);
468 expect(watchGrp.collectionCost).toEqual(0);
469 expect(watchGrp.evalCost).toEqual(0);
470 });
471
472 it('should watch literal arrays made of expressions', () {
473 context['a'] = 1;
474 var ast = new CollectionAST(
475 new PureFunctionAST('[a]', new ArrayFn(), [parse('a')])
476 );
477 var watch = watchGrp.watch(ast, (v, p) => logger(v));
478 watchGrp.detectChanges();
479 expect(logger[0], toEqualCollectionRecord(
480 collection: ['1[null -> 0]'],
481 additions: ['1[null -> 0]'],
482 moves: [],
483 removals: []));
484 logger.clear();
485
486 context['a'] = 2;
487 watchGrp.detectChanges();
488 expect(logger[0], toEqualCollectionRecord(
489 collection: ['2[null -> 0]'],
490 additions: ['2[null -> 0]'],
491 moves: [],
492 removals: ['1[0 -> null]']));
493 logger.clear();
494 });
495
496 it('should watch pure function whose result goes to pure function', () {
497 context['a'] = 1;
498 var ast = new PureFunctionAST(
499 '-',
500 (v) => -v,
501 [new PureFunctionAST('++', (v) => v + 1, [parse('a')])]
502 );
503 var watch = watchGrp.watch(ast, (v, p) => logger(v));
504
505 expect(watchGrp.detectChanges()).not.toBe(null);
506 expect(logger).toEqual([-2]);
507 logger.clear();
508
509 context['a'] = 2;
510 expect(watchGrp.detectChanges()).not.toBe(null);
511 expect(logger).toEqual([-3]);
512 });
513 });
514
515 describe('child group', () {
516 it('should remove all field watches in group and group\'s children', () {
517 watchGrp.watch(parse('a'), (v, p) => logger('0a'));
518 var child1a = watchGrp.newGroup(new PrototypeMap(context));
519 var child1b = watchGrp.newGroup(new PrototypeMap(context));
520 var child2 = child1a.newGroup(new PrototypeMap(context));
521 child1a.watch(parse('a'), (v, p) => logger('1a'));
522 child1b.watch(parse('a'), (v, p) => logger('1b'));
523 watchGrp.watch(parse('a'), (v, p) => logger('0A'));
524 child1a.watch(parse('a'), (v, p) => logger('1A'));
525 child2.watch(parse('a'), (v, p) => logger('2A'));
526
527 // flush initial reaction functions
528 expect(watchGrp.detectChanges()).toEqual(6);
529 // expect(logger).toEqual(['0a', '0A', '1a', '1A', '2A', '1b']);
530 expect(logger).toEqual(['0a', '1a', '1b', '0A', '1A', '2A']); // we go by registration order
531 expect(watchGrp.fieldCost).toEqual(1);
532 expect(watchGrp.totalFieldCost).toEqual(4);
533 logger.clear();
534
535 context['a'] = 1;
536 expect(watchGrp.detectChanges()).toEqual(6);
537 expect(logger).toEqual(['0a', '0A', '1a', '1A', '2A', '1b']); // we go by group order
538 logger.clear();
539
540 context['a'] = 2;
541 child1a.remove(); // should also remove child2
542 expect(watchGrp.detectChanges()).toEqual(3);
543 expect(logger).toEqual(['0a', '0A', '1b']);
544 expect(watchGrp.fieldCost).toEqual(1);
545 expect(watchGrp.totalFieldCost).toEqual(2);
546 });
547
548 it('should remove all method watches in group and group\'s children', () {
549 context['my'] = new MyClass(logger);
550 AST countMethod = new MethodAST(parse('my'), 'count', []);
551 watchGrp.watch(countMethod, (v, p) => logger('0a'));
552 expectOrder(['0a']);
553
554 var child1a = watchGrp.newGroup(new PrototypeMap(context));
555 var child1b = watchGrp.newGroup(new PrototypeMap(context));
556 var child2 = child1a.newGroup(new PrototypeMap(context));
557 child1a.watch(countMethod, (v, p) => logger('1a'));
558 expectOrder(['0a', '1a']);
559 child1b.watch(countMethod, (v, p) => logger('1b'));
560 expectOrder(['0a', '1a', '1b']);
561 watchGrp.watch(countMethod, (v, p) => logger('0A'));
562 expectOrder(['0a', '0A', '1a', '1b']);
563 child1a.watch(countMethod, (v, p) => logger('1A'));
564 expectOrder(['0a', '0A', '1a', '1A', '1b']);
565 child2.watch(countMethod, (v, p) => logger('2A'));
566 expectOrder(['0a', '0A', '1a', '1A', '2A', '1b']);
567
568 // flush initial reaction functions
569 expect(watchGrp.detectChanges()).toEqual(6);
570 expectOrder(['0a', '0A', '1a', '1A', '2A', '1b']);
571
572 child1a.remove(); // should also remove child2
573 expect(watchGrp.detectChanges()).toEqual(3);
574 expectOrder(['0a', '0A', '1b']);
575 });
576
577 it('should add watches within its own group', () {
578 context['my'] = new MyClass(logger);
579 AST countMethod = new MethodAST(parse('my'), 'count', []);
580 var ra = watchGrp.watch(countMethod, (v, p) => logger('a'));
581 var child = watchGrp.newGroup(new PrototypeMap(context));
582 var cb = child.watch(countMethod, (v, p) => logger('b'));
583
584 expectOrder(['a', 'b']);
585 expectOrder(['a', 'b']);
586
587 ra.remove();
588 expectOrder(['b']);
589
590 cb.remove();
591 expectOrder([]);
592
593 // TODO: add them back in wrong order, assert events in right order
594 cb = child.watch(countMethod, (v, p) => logger('b'));
595 ra = watchGrp.watch(countMethod, (v, p) => logger('a'));;
596 expectOrder(['a', 'b']);
597 });
598
599
600 it('should not call reaction function on removed group', () {
601 var log = [];
602 context['name'] = 'misko';
603 var child = watchGrp.newGroup(context);
604 watchGrp.watch(parse('name'), (v, _) {
605 log.add('root $v');
606 if (v == 'destroy') {
607 child.remove();
608 }
609 });
610 child.watch(parse('name'), (v, _) => log.add('child $v'));
611 watchGrp.detectChanges();
612 expect(log).toEqual(['root misko', 'child misko']);
613 log.clear();
614
615 context['name'] = 'destroy';
616 watchGrp.detectChanges();
617 expect(log).toEqual(['root destroy']);
618 });
619
620
621
622 it('should watch children', () {
623 var childContext = new PrototypeMap(context);
624 context['a'] = 'OK';
625 context['b'] = 'BAD';
626 childContext['b'] = 'OK';
627 watchGrp.watch(parse('a'), (v, p) => logger(v));
628 watchGrp.newGroup(childContext).watch(parse('b'), (v, p) => logger(v));
629
630 watchGrp.detectChanges();
631 expect(logger).toEqual(['OK', 'OK']);
632 logger.clear();
633
634 context['a'] = 'A';
635 childContext['b'] = 'B';
636
637 watchGrp.detectChanges();
638 expect(logger).toEqual(['A', 'B']);
639 logger.clear();
640 });
641 });
642
643 });
644
645 class MyClass {
646 final Logger logger;
647 var valA;
648 int _count = 0;
649
650 MyClass(this.logger);
651
652 methodA(arg1) {
653 logger('methodA($arg1) => $valA');
654 return valA;
655 }
656
657 count() => _count++;
658
659 String toString() => 'MyClass';
660 }
661
662 class LoggingFunctionApply extends FunctionApply {
663 Logger logger;
664 LoggingFunctionApply(this.logger);
665 apply(List args) => logger(args);
666 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698