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

Side by Side Diff: test/observe_test.dart

Issue 12096106: work in progress: observable implementation using detailed change records (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: Created 7 years, 10 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
« no previous file with comments | « pubspec.yaml ('k') | test/run_all.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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 /** Tests for some of the utility helper functions used by the compiler. */
6 library observe_test;
7
8 import 'package:unittest/compact_vm_config.dart';
9 import 'package:unittest/unittest.dart';
10 import 'package:web_ui/observe.dart';
11 import 'package:web_ui/src/utils.dart' show setImmediate;
12
13 main() {
14 useCompactVMConfiguration();
15
16 group('ObservableReference', () {
17 test('no observers', () {
18 var t = new ObservableReference<int>(123);
19 expect(t.value, 123);
20 t.value = 42;
21 expect(t.value, 42);
22 expect(t.hasObservers, false);
23 });
24
25 test('observe', () {
26 var t = new ObservableReference<int>(123);
27 int called = 0;
28 observe(() => t.value, expectAsync1((ExpressionChange n) {
29 expect(n.oldValue, 123);
30 expect(n.newValue, 42);
31 }));
32 t.value = 41;
33 t.value = 42;
34 expect(called, 0, reason: 'changes delived async');
35 });
36
37 test('observe multiple changes', () {
38 var t = new ObservableReference<int>(123);
39 observe(() => t.value, expectAsync1((ExpressionChange n) {
40 if (n.oldValue == 123) {
41 expect(n.newValue, 42);
42 // Cause another change
43 t.value = 777;
44 } else {
45 expect(n.oldValue, 42);
46 expect(n.newValue, 777);
47 }
48 }, count: 2));
49 t.value = 42;
50 });
51
52 test('multiple observers', () {
53 var t = new ObservableReference<int>(123);
54 observe(() => t.value, expectAsync1((ExpressionChange n) {
55 expect(n.oldValue, 123);
56 expect(n.newValue, 42);
57 }));
58 observe(() => t.value + 1, expectAsync1((ExpressionChange n) {
59 expect(n.oldValue, 124);
60 expect(n.newValue, 43);
61 }));
62 t.value = 41;
63 t.value = 42;
64 });
65
66 test('deliverChangesSync', () {
67 var t = new ObservableReference<int>(123);
68 var notifications = [];
69 observe(() => t.value, notifications.add);
70 t.value = 41;
71 t.value = 42;
72 expect(notifications, [], reason: 'changes delived async');
73
74 deliverChangesSync();
75 expect(notifications, [_change(123, 42)]);
76 t.value = 777;
77 expect(notifications.length, 1, reason: 'changes delived async');
78
79 deliverChangesSync();
80 expect(notifications, [_change(123, 42), _change(42, 777)]);
81
82 // Has no effect if there are no changes
83 deliverChangesSync();
84 expect(notifications, [_change(123, 42), _change(42, 777)]);
85 });
86
87 test('unobserve', () {
88 var t = new ObservableReference<int>(123);
89 ChangeUnobserver unobserve;
90 unobserve = observe(() => t.value, expectAsync1((n) {
91 expect(n.oldValue, 123);
92 expect(n.newValue, 42);
93 unobserve();
94 t.value = 777;
95 }));
96 t.value = 42;
97 });
98
99 test('observers fired in order', () {
100 var t = new ObservableReference<int>(123);
101 int expectOldValue = 123;
102 int expectNewValue = 42;
103 observe(() => t.value, expectAsync1((n) {
104 expect(n.oldValue, expectOldValue);
105 expect(n.newValue, expectNewValue);
106
107 // The second observer will see this change already, and only be called
108 // once. However we'll be called a second time.
109 t.value = 777;
110 expectNewValue = 777;
111 expectOldValue = 42;
112 }, count: 2));
113 int count = 0;
114 observe(() => t.value + 1000, expectAsync1((n) {
115 expect(n.oldValue, 1123);
116 expect(n.newValue, 1777);
117 }));
118
119 // Make the initial change
120 t.value = 42;
121 });
122
123 test('unobserve one of two observers', () {
124 var t = new ObservableReference<int>(123);
125 ChangeUnobserver unobserve;
126 unobserve = observe(() => t.value, expectAsync1((n) {
127 expect(n.oldValue, 123);
128 expect(n.newValue, 42);
129
130 // This will not affect the other observer, so it still gets the event.
131 unobserve();
132 setImmediate(() => t.value = 777);
133 }));
134 int count = 0;
135 observe(() => t.value + 1000, expectAsync1((n) {
136 if (++count == 1) {
137 expect(n.oldValue, 1123);
138 expect(n.newValue, 1042);
139 } else {
140 expect(n.oldValue, 1042);
141 expect(n.newValue, 1777);
142 }
143 }, count: 2));
144
145 // Make the initial change
146 t.value = 42;
147 });
148
149 test('notifyRead in getter', () {
150 var t = new ObservableReference<int>(123);
151
152 observe(() {
153 expect(observeReads, true);
154 expect(t.hasObservers, false);
155 return t.value;
156 }, (n) {});
157
158 expect(observeReads, false);
159 expect(t.hasObservers, true);
160 });
161
162 test('notifyWrite in setter', () {
163 var t = new ObservableReference<int>(123);
164 observe(() => t.value, (n) {});
165
166 t.value = 42;
167 expect(observeReads, false);
168 expect(t.hasObservers, true);
169
170 // This will re-observe the expression.
171 deliverChangesSync();
172
173 expect(observeReads, false);
174 expect(t.hasObservers, true);
175 });
176
177 test('observe conditional async', () {
178 var t = new ObservableReference<bool>(false);
179 var a = new ObservableReference<int>(123);
180 var b = new ObservableReference<String>('hi');
181
182 int count = 0;
183 var oldValue = 'hi';
184 observe(() => t.value ? a.value : b.value, expectAsync1((n) {
185 expect(n.oldValue, oldValue);
186 oldValue = t.value ? a.value : b.value;
187 expect(n.newValue, oldValue);
188
189 switch (++count) {
190 case 1:
191 // We are observing "a", change it
192 a.value = 42;
193 break;
194 case 2:
195 // Switch to observing "b"
196 t.value = false;
197 break;
198 case 3:
199 // Change "a", this should have no effect and will not fire a 4th
200 // change event.
201 a.value = 777;
202 break;
203 default:
204 // Should not be able to reach this because of the "count" argument
205 // to expectAsync1
206 throw new StateError('unreachable');
207 }
208 }, count: 3));
209
210 expect(t.hasObservers, true);
211 expect(a.hasObservers, false);
212 expect(b.hasObservers, true);
213
214 // Start off by changing "t" to true.
215 t.value = true;
216 });
217
218 test('change limit', () {
219 var x = new ObservableReference(false);
220 var y = new ObservableReference(false);
221
222 int xCount = 0, yCount = 0;
223 int limit = 1000;
224 observe(() => x.value, (n) {
225 if (++xCount < limit) y.value = x.value;
226 });
227 observe(() => y.value, (n) {
228 if (++yCount < limit) x.value = !y.value;
229 });
230
231 // Kick off the cascading changes
232 x.value = true;
233
234 deliverChangesSync();
235
236 expect(xCount, limit);
237 expect(yCount, limit - 1);
238 });
239
240 test('observe conditional sync', () {
241 var t = new ObservableReference<bool>(false);
242 var a = new ObservableReference<int>(123);
243 var b = new ObservableReference<String>('hi');
244
245 var notifications = [];
246 observe(() => t.value ? a.value : b.value, notifications.add);
247
248 // Start off by changing "t" to true, so we evaluate "a".
249 t.value = true;
250 deliverChangesSync();
251
252 // This changes "a" which we should be observing.
253 a.value = 42;
254 deliverChangesSync();
255
256 // This has no effect because we aren't using "b" yet.
257 b.value = 'universe';
258 deliverChangesSync();
259
260 // Switch to use "b".
261 t.value = false;
262 deliverChangesSync();
263
264 // This has no effect because we aren't using "a" anymore.
265 a.value = 777;
266 deliverChangesSync();
267
268 expect(notifications, [
269 _change('hi', 123),
270 _change(123, 42),
271 _change(42, 'universe')]);
272 });
273 });
274
275
276 group('ObservableList', () {
277 // TODO(jmesserly): need all standard List tests.
278
279 test('observe length', () {
280 var list = new ObservableList();
281 var notification = null;
282 observe(() => list.length, (n) { notification = n; });
283
284 list.addAll([1, 2, 3]);
285 expect(list, [1, 2, 3]);
286 deliverChangesSync();
287 expect(notification, _change(0, 3), reason: 'addAll changes length');
288
289 list.add(4);
290 expect(list, [1, 2, 3, 4]);
291 deliverChangesSync();
292 expect(notification, _change(3, 4), reason: 'add changes length');
293
294 list.removeRange(1, 2);
295 expect(list, [1, 4]);
296 deliverChangesSync();
297 expect(notification, _change(4, 2), reason: 'removeRange changes length');
298
299 list.length = 5;
300 expect(list, [1, 4, null, null, null]);
301 deliverChangesSync();
302 expect(notification, _change(2, 5), reason: 'length= changes length');
303 notification = null;
304
305 list[2] = 9000;
306 expect(list, [1, 4, 9000, null, null]);
307 deliverChangesSync();
308 expect(notification, null, reason: '[]= does not change length');
309
310 list.clear();
311 expect(list, []);
312 deliverChangesSync();
313 expect(notification, _change(5, 0), reason: 'clear changes length');
314 });
315
316 test('observe index', () {
317 var list = new ObservableList.from([1, 2, 3]);
318 var notification = null;
319 observe(() => list[1], (n) { notification = n; });
320
321 list.add(4);
322 expect(list, [1, 2, 3, 4]);
323 deliverChangesSync();
324 expect(notification, null,
325 reason: 'add does not change existing items');
326
327 list[1] = 777;
328 expect(list, [1, 777, 3, 4]);
329 deliverChangesSync();
330 expect(notification, _change(2, 777));
331
332 notification = null;
333 list[2] = 9000;
334 expect(list, [1, 777, 9000, 4]);
335 deliverChangesSync();
336 expect(notification, null,
337 reason: 'setting a different index should not fire change');
338
339 list[1] = 44;
340 list[1] = 43;
341 list[1] = 42;
342 expect(list, [1, 42, 9000, 4]);
343 deliverChangesSync();
344 expect(notification, _change(777, 42));
345
346 notification = null;
347 list.length = 2;
348 expect(list, [1, 42]);
349 deliverChangesSync();
350 expect(notification, null,
351 reason: 'did not truncate the observed item');
352
353 list.length = 1; // truncate
354 list.add(2);
355 expect(list, [1, 2]);
356 deliverChangesSync();
357 expect(notification, _change(42, 2),
358 reason: 'item truncated and added back');
359 });
360
361 test('toString', () {
362 var list = new ObservableList.from([1, 2, 3]);
363 var notification = null;
364 observe(() => list.toString(), (n) { notification = n; });
365 list[2] = 4;
366 deliverChangesSync();
367 expect(notification, _change('[1, 2, 3]', '[1, 2, 4]'));
368 });
369 });
370
371
372 group('ObservableSet', () {
373 // TODO(jmesserly): need all standard Set tests.
374
375 test('observe length', () {
376 var set = new ObservableSet();
377 var notification = null;
378 observe(() => set.length, (n) { notification = n; });
379
380 set.addAll([1, 2, 3]);
381 expect(set, [1, 2, 3]);
382 deliverChangesSync();
383 expect(notification, _change(0, 3), reason: 'addAll changes length');
384
385 set.add(4);
386 expect(set, [1, 2, 3, 4]);
387 deliverChangesSync();
388 expect(notification, _change(3, 4), reason: 'add changes length');
389
390 set.removeAll([2, 3]);
391 expect(set, [1, 4]);
392 deliverChangesSync();
393 expect(notification, _change(4, 2), reason: 'removeAll changes length');
394
395 set.remove(1);
396 expect(set, [4]);
397 deliverChangesSync();
398 expect(notification, _change(2, 1), reason: 'remove changes length');
399
400 notification = null;
401 set.add(4);
402 expect(set, [4]);
403 deliverChangesSync();
404 expect(notification, null, reason: 'item already exists');
405
406 set.clear();
407 expect(set, []);
408 deliverChangesSync();
409 expect(notification, _change(1, 0), reason: 'clear changes length');
410 });
411
412 test('observe item', () {
413 var set = new ObservableSet.from([1, 2, 3]);
414 var notification = null;
415 observe(() => set.contains(2), (n) { notification = n; });
416
417 set.add(4);
418 expect(set, [1, 2, 3, 4]);
419 deliverChangesSync();
420 expect(notification, null, reason: 'add does not change existing items');
421
422 set.remove(3);
423 expect(set, [1, 2, 4]);
424 expect(notification, null,
425 reason: 'removing an item does not change other items');
426
427 set.remove(2);
428 expect(set, [1, 4]);
429 deliverChangesSync();
430 expect(notification, _change(true, false));
431
432 notification = null;
433 set.removeAll([2, 3]);
434 expect(set, [1, 4]);
435 deliverChangesSync();
436 expect(notification, null, reason: 'item already removed');
437
438 set.add(2);
439 expect(set, [1, 2, 4]);
440 deliverChangesSync();
441 expect(notification, _change(false, true), reason: 'item added again');
442 });
443
444 test('toString', () {
445 var original = new Set.from([1, 2, 3]);
446 var set = new ObservableSet.from(original);
447 var notification = null;
448 observe(() => set.toString(), (n) { notification = n; });
449 set.add(4);
450 deliverChangesSync();
451 var updated = new Set.from([1, 2, 3, 4]);
452
453 // Note: using Set.toString as the exectation, so the order is the same
454 // as with ObservableSet, regardless of how hashCode is implemented.
455 expect(notification, _change('$original', '$updated'));
456 });
457 });
458
459
460 group('ObservableMap', () {
461 // TODO(jmesserly): need all standard Map tests.
462
463 test('observe length', () {
464 var map = new ObservableMap();
465 var notification = null;
466 observe(() => map.length, (n) { notification = n; });
467
468 map['a'] = 1;
469 map.putIfAbsent('b', () => 2);
470 map['c'] = 3;
471 expect(map, {'a': 1, 'b': 2, 'c': 3});
472 deliverChangesSync();
473 expect(notification, _change(0, 3), reason: 'adding changes length');
474
475 map['d'] = 4;
476 expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4});
477 deliverChangesSync();
478 expect(notification, _change(3, 4), reason: 'add changes length');
479
480 map.remove('b');
481 map.remove('c');
482 expect(map, {'a': 1, 'd': 4});
483 deliverChangesSync();
484 expect(notification, _change(4, 2), reason: 'removeRange changes length');
485
486 notification = null;
487 map['d'] = 9000;
488 expect(map, {'a': 1, 'd': 9000});
489 deliverChangesSync();
490 expect(notification, null, reason: 'update item does not change length');
491
492 map.clear();
493 expect(map, {});
494 deliverChangesSync();
495 expect(notification, _change(2, 0), reason: 'clear changes length');
496 });
497
498 test('observe index', () {
499 var map = new ObservableMap.from({'a': 1, 'b': 2, 'c': 3});
500 var notification = null;
501 observe(() => map['b'], (n) { notification = n; });
502
503 map.putIfAbsent('d', () => 4);
504 expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4});
505 deliverChangesSync();
506 expect(notification, null, reason: 'add does not change existing items');
507
508 map['b'] = 777;
509 expect(map, {'a': 1, 'b': 777, 'c': 3, 'd': 4});
510 deliverChangesSync();
511 expect(notification, _change(2, 777));
512
513 notification = null;
514 map.putIfAbsent('b', () => 1234);
515 expect(map, {'a': 1, 'b': 777, 'c': 3, 'd': 4});
516 deliverChangesSync();
517 expect(notification, null, reason: 'item already there');
518
519 map['c'] = 9000;
520 expect(map, {'a': 1, 'b': 777, 'c': 9000, 'd': 4});
521 deliverChangesSync();
522 expect(notification, null, reason: 'setting a different item');
523
524 map['b'] = 44;
525 map['b'] = 43;
526 map['b'] = 42;
527 expect(map, {'a': 1, 'b': 42, 'c': 9000, 'd': 4});
528 deliverChangesSync();
529 expect(notification, _change(777, 42));
530
531 notification = null;
532 map.remove('a');
533 map.remove('d');
534 expect(map, {'b': 42, 'c': 9000});
535 deliverChangesSync();
536 expect(notification, null, reason: 'did not remove the observed item');
537
538 map.remove('b');
539 map['b'] = 2;
540 expect(map, {'b': 2, 'c': 9000});
541 deliverChangesSync();
542 expect(notification, _change(42, 2), reason: 'removed and added back');
543 });
544
545 test('toString', () {
546 var hashMap = new Map.from({'a': 1, 'b': 2});
547 var expect1 = hashMap.toString();
548 var map = new ObservableMap.from(hashMap);
549
550 var notification = null;
551 observe(() => map.toString(), (n) { notification = n; });
552 map.remove('b');
553 map['c'] = 3;
554
555 hashMap.remove('b');
556 hashMap['c'] = 3;
557 var expect2 = hashMap.toString();
558
559 deliverChangesSync();
560
561 expect(notification, _change(expect1, expect2));
562 });
563 });
564
565 }
566
567 _change(oldValue, newValue) => new ExpressionChange(oldValue, newValue);
OLDNEW
« no previous file with comments | « pubspec.yaml ('k') | test/run_all.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698