OLD | NEW |
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 'package:observe/observe.dart'; | 6 import 'package:observe/observe.dart'; |
6 import 'package:unittest/unittest.dart'; | 7 import 'package:unittest/unittest.dart'; |
7 import 'utils.dart'; | 8 import 'observe_test_utils.dart'; |
8 | 9 |
9 main() { | 10 main() { |
10 // TODO(jmesserly): need all standard Map API tests. | 11 // TODO(jmesserly): need all standard Map API tests. |
11 | 12 |
| 13 StreamSubscription sub; |
| 14 |
| 15 sharedTearDown() { |
| 16 if (sub != null) { |
| 17 sub.cancel(); |
| 18 sub = null; |
| 19 } |
| 20 } |
| 21 |
12 group('observe length', () { | 22 group('observe length', () { |
13 ObservableMap map; | 23 ObservableMap map; |
14 List<ChangeRecord> changes; | 24 List<ChangeRecord> changes; |
15 | 25 |
16 setUp(() { | 26 setUp(() { |
17 map = toObservable({'a': 1, 'b': 2, 'c': 3}); | 27 map = toObservable({'a': 1, 'b': 2, 'c': 3}); |
18 changes = null; | 28 changes = null; |
19 map.changes.listen((records) { | 29 sub = map.changes.listen((records) { |
20 changes = records.where((r) => r.changes(_LENGTH)).toList(); | 30 changes = records.where((r) => r.changes(_LENGTH)).toList(); |
21 }); | 31 }); |
22 }); | 32 }); |
23 | 33 |
24 test('add item changes length', () { | 34 tearDown(sharedTearDown); |
| 35 |
| 36 observeTest('add item changes length', () { |
25 map['d'] = 4; | 37 map['d'] = 4; |
26 expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4}); | 38 expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4}); |
27 deliverChangeRecords(); | 39 performMicrotaskCheckpoint(); |
28 expectChanges(changes, [_lengthChange]); | 40 expectChanges(changes, [_lengthChange]); |
29 }); | 41 }); |
30 | 42 |
31 test('putIfAbsent changes length', () { | 43 observeTest('putIfAbsent changes length', () { |
32 map.putIfAbsent('d', () => 4); | 44 map.putIfAbsent('d', () => 4); |
33 expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4}); | 45 expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4}); |
34 deliverChangeRecords(); | 46 performMicrotaskCheckpoint(); |
35 expectChanges(changes, [_lengthChange]); | 47 expectChanges(changes, [_lengthChange]); |
36 }); | 48 }); |
37 | 49 |
38 test('remove changes length', () { | 50 observeTest('remove changes length', () { |
39 map.remove('c'); | 51 map.remove('c'); |
40 map.remove('a'); | 52 map.remove('a'); |
41 expect(map, {'b': 2}); | 53 expect(map, {'b': 2}); |
42 deliverChangeRecords(); | 54 performMicrotaskCheckpoint(); |
43 expectChanges(changes, [_lengthChange, _lengthChange]); | 55 expectChanges(changes, [_lengthChange, _lengthChange]); |
44 }); | 56 }); |
45 | 57 |
46 test('remove non-existent item does not change length', () { | 58 observeTest('remove non-existent item does not change length', () { |
47 map.remove('d'); | 59 map.remove('d'); |
48 expect(map, {'a': 1, 'b': 2, 'c': 3}); | 60 expect(map, {'a': 1, 'b': 2, 'c': 3}); |
49 deliverChangeRecords(); | 61 performMicrotaskCheckpoint(); |
50 expectChanges(changes, null); | 62 expectChanges(changes, null); |
51 }); | 63 }); |
52 | 64 |
53 test('set existing item does not change length', () { | 65 observeTest('set existing item does not change length', () { |
54 map['c'] = 9000; | 66 map['c'] = 9000; |
55 expect(map, {'a': 1, 'b': 2, 'c': 9000}); | 67 expect(map, {'a': 1, 'b': 2, 'c': 9000}); |
56 deliverChangeRecords(); | 68 performMicrotaskCheckpoint(); |
57 expectChanges(changes, []); | 69 expectChanges(changes, []); |
58 }); | 70 }); |
59 | 71 |
60 test('clear changes length', () { | 72 observeTest('clear changes length', () { |
61 map.clear(); | 73 map.clear(); |
62 expect(map, {}); | 74 expect(map, {}); |
63 deliverChangeRecords(); | 75 performMicrotaskCheckpoint(); |
64 expectChanges(changes, [_lengthChange]); | 76 expectChanges(changes, [_lengthChange]); |
65 }); | 77 }); |
66 }); | 78 }); |
67 | 79 |
68 group('observe item', () { | 80 group('observe item', () { |
69 | 81 |
70 ObservableMap map; | 82 ObservableMap map; |
71 List<ChangeRecord> changes; | 83 List<ChangeRecord> changes; |
72 | 84 |
73 setUp(() { | 85 setUp(() { |
74 map = toObservable({'a': 1, 'b': 2, 'c': 3}); | 86 map = toObservable({'a': 1, 'b': 2, 'c': 3}); |
75 changes = null; | 87 changes = null; |
76 map.changes.listen((records) { | 88 sub = map.changes.listen((records) { |
77 changes = records.where((r) => r.changes('b')).toList(); | 89 changes = records.where((r) => r.changes('b')).toList(); |
78 }); | 90 }); |
79 }); | 91 }); |
80 | 92 |
81 test('putIfAbsent new item does not change existing item', () { | 93 tearDown(sharedTearDown); |
| 94 |
| 95 observeTest('putIfAbsent new item does not change existing item', () { |
82 map.putIfAbsent('d', () => 4); | 96 map.putIfAbsent('d', () => 4); |
83 expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4}); | 97 expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4}); |
84 deliverChangeRecords(); | 98 performMicrotaskCheckpoint(); |
85 expectChanges(changes, []); | 99 expectChanges(changes, []); |
86 }); | 100 }); |
87 | 101 |
88 test('set item to null', () { | 102 observeTest('set item to null', () { |
89 map['b'] = null; | 103 map['b'] = null; |
90 expect(map, {'a': 1, 'b': null, 'c': 3}); | 104 expect(map, {'a': 1, 'b': null, 'c': 3}); |
91 deliverChangeRecords(); | 105 performMicrotaskCheckpoint(); |
92 expectChanges(changes, [_change('b')]); | 106 expectChanges(changes, [_change('b')]); |
93 }); | 107 }); |
94 | 108 |
95 test('set item to value', () { | 109 observeTest('set item to value', () { |
96 map['b'] = 777; | 110 map['b'] = 777; |
97 expect(map, {'a': 1, 'b': 777, 'c': 3}); | 111 expect(map, {'a': 1, 'b': 777, 'c': 3}); |
98 deliverChangeRecords(); | 112 performMicrotaskCheckpoint(); |
99 expectChanges(changes, [_change('b')]); | 113 expectChanges(changes, [_change('b')]); |
100 }); | 114 }); |
101 | 115 |
102 test('putIfAbsent does not change if already there', () { | 116 observeTest('putIfAbsent does not change if already there', () { |
103 map.putIfAbsent('b', () => 1234); | 117 map.putIfAbsent('b', () => 1234); |
104 expect(map, {'a': 1, 'b': 2, 'c': 3}); | 118 expect(map, {'a': 1, 'b': 2, 'c': 3}); |
105 deliverChangeRecords(); | 119 performMicrotaskCheckpoint(); |
106 expectChanges(changes, null); | 120 expectChanges(changes, null); |
107 }); | 121 }); |
108 | 122 |
109 test('change a different item', () { | 123 observeTest('change a different item', () { |
110 map['c'] = 9000; | 124 map['c'] = 9000; |
111 expect(map, {'a': 1, 'b': 2, 'c': 9000}); | 125 expect(map, {'a': 1, 'b': 2, 'c': 9000}); |
112 deliverChangeRecords(); | 126 performMicrotaskCheckpoint(); |
113 expectChanges(changes, []); | 127 expectChanges(changes, []); |
114 }); | 128 }); |
115 | 129 |
116 test('change the item', () { | 130 observeTest('change the item', () { |
117 map['b'] = 9001; | 131 map['b'] = 9001; |
118 map['b'] = 42; | 132 map['b'] = 42; |
119 expect(map, {'a': 1, 'b': 42, 'c': 3}); | 133 expect(map, {'a': 1, 'b': 42, 'c': 3}); |
120 deliverChangeRecords(); | 134 performMicrotaskCheckpoint(); |
121 expectChanges(changes, [_change('b'), _change('b')]); | 135 expectChanges(changes, [_change('b'), _change('b')]); |
122 }); | 136 }); |
123 | 137 |
124 test('remove other items', () { | 138 observeTest('remove other items', () { |
125 map.remove('a'); | 139 map.remove('a'); |
126 expect(map, {'b': 2, 'c': 3}); | 140 expect(map, {'b': 2, 'c': 3}); |
127 deliverChangeRecords(); | 141 performMicrotaskCheckpoint(); |
128 expectChanges(changes, []); | 142 expectChanges(changes, []); |
129 }); | 143 }); |
130 | 144 |
131 test('remove the item', () { | 145 observeTest('remove the item', () { |
132 map.remove('b'); | 146 map.remove('b'); |
133 expect(map, {'a': 1, 'c': 3}); | 147 expect(map, {'a': 1, 'c': 3}); |
134 deliverChangeRecords(); | 148 performMicrotaskCheckpoint(); |
135 expectChanges(changes, [_change('b', isRemove: true)]); | 149 expectChanges(changes, [_change('b', isRemove: true)]); |
136 }); | 150 }); |
137 | 151 |
138 test('remove and add back', () { | 152 observeTest('remove and add back', () { |
139 map.remove('b'); | 153 map.remove('b'); |
140 map['b'] = 2; | 154 map['b'] = 2; |
141 expect(map, {'a': 1, 'b': 2, 'c': 3}); | 155 expect(map, {'a': 1, 'b': 2, 'c': 3}); |
142 deliverChangeRecords(); | 156 performMicrotaskCheckpoint(); |
143 expectChanges(changes, | 157 expectChanges(changes, |
144 [_change('b', isRemove: true), _change('b', isInsert: true)]); | 158 [_change('b', isRemove: true), _change('b', isInsert: true)]); |
145 }); | 159 }); |
146 }); | 160 }); |
147 | 161 |
148 test('toString', () { | 162 observeTest('toString', () { |
149 var map = toObservable({'a': 1, 'b': 2}); | 163 var map = toObservable({'a': 1, 'b': 2}); |
150 expect(map.toString(), '{a: 1, b: 2}'); | 164 expect(map.toString(), '{a: 1, b: 2}'); |
151 }); | 165 }); |
152 | 166 |
153 group('change records', () { | 167 group('change records', () { |
154 List<ChangeRecord> records; | 168 List<ChangeRecord> records; |
155 ObservableMap map; | 169 ObservableMap map; |
156 | 170 |
157 setUp(() { | 171 setUp(() { |
158 map = toObservable({'a': 1, 'b': 2}); | 172 map = toObservable({'a': 1, 'b': 2}); |
159 records = null; | 173 records = null; |
160 map.changes.first.then((r) { records = r; }); | 174 map.changes.first.then((r) { records = r; }); |
161 }); | 175 }); |
162 | 176 |
163 test('read operations', () { | 177 tearDown(sharedTearDown); |
| 178 |
| 179 observeTest('read operations', () { |
164 expect(map.length, 2); | 180 expect(map.length, 2); |
165 expect(map.isEmpty, false); | 181 expect(map.isEmpty, false); |
166 expect(map['a'], 1); | 182 expect(map['a'], 1); |
167 expect(map.containsKey(2), false); | 183 expect(map.containsKey(2), false); |
168 expect(map.containsValue(2), true); | 184 expect(map.containsValue(2), true); |
169 expect(map.containsKey('b'), true); | 185 expect(map.containsKey('b'), true); |
170 expect(map.keys.toList(), ['a', 'b']); | 186 expect(map.keys.toList(), ['a', 'b']); |
171 expect(map.values.toList(), [1, 2]); | 187 expect(map.values.toList(), [1, 2]); |
172 var copy = {}; | 188 var copy = {}; |
173 map.forEach((k, v) { copy[k] = v; }); | 189 map.forEach((k, v) { copy[k] = v; }); |
174 expect(copy, {'a': 1, 'b': 2}); | 190 expect(copy, {'a': 1, 'b': 2}); |
175 deliverChangeRecords(); | 191 performMicrotaskCheckpoint(); |
176 | 192 |
177 // no change from read-only operators | 193 // no change from read-only operators |
178 expect(records, null); | 194 expect(records, null); |
| 195 |
| 196 // Make a change so the subscription gets unregistered. |
| 197 map.clear(); |
179 }); | 198 }); |
180 | 199 |
181 test('putIfAbsent', () { | 200 observeTest('putIfAbsent', () { |
182 map.putIfAbsent('a', () => 42); | 201 map.putIfAbsent('a', () => 42); |
183 expect(map, {'a': 1, 'b': 2}); | 202 expect(map, {'a': 1, 'b': 2}); |
184 | 203 |
185 map.putIfAbsent('c', () => 3); | 204 map.putIfAbsent('c', () => 3); |
186 expect(map, {'a': 1, 'b': 2, 'c': 3}); | 205 expect(map, {'a': 1, 'b': 2, 'c': 3}); |
187 | 206 |
188 deliverChangeRecords(); | 207 performMicrotaskCheckpoint(); |
189 expectChanges(records, [ | 208 expectChanges(records, [ |
190 _lengthChange, | 209 _lengthChange, |
191 _change('c', isInsert: true), | 210 _change('c', isInsert: true), |
192 ]); | 211 ]); |
193 }); | 212 }); |
194 | 213 |
195 test('[]=', () { | 214 observeTest('[]=', () { |
196 map['a'] = 42; | 215 map['a'] = 42; |
197 expect(map, {'a': 42, 'b': 2}); | 216 expect(map, {'a': 42, 'b': 2}); |
198 | 217 |
199 map['c'] = 3; | 218 map['c'] = 3; |
200 expect(map, {'a': 42, 'b': 2, 'c': 3}); | 219 expect(map, {'a': 42, 'b': 2, 'c': 3}); |
201 | 220 |
202 deliverChangeRecords(); | 221 performMicrotaskCheckpoint(); |
203 expectChanges(records, [ | 222 expectChanges(records, [ |
204 _change('a'), | 223 _change('a'), |
205 _lengthChange, | 224 _lengthChange, |
206 _change('c', isInsert: true) | 225 _change('c', isInsert: true) |
207 ]); | 226 ]); |
208 }); | 227 }); |
209 | 228 |
210 test('remove', () { | 229 observeTest('remove', () { |
211 map.remove('b'); | 230 map.remove('b'); |
212 expect(map, {'a': 1}); | 231 expect(map, {'a': 1}); |
213 | 232 |
214 deliverChangeRecords(); | 233 performMicrotaskCheckpoint(); |
215 expectChanges(records, [ | 234 expectChanges(records, [ |
216 _change('b', isRemove: true), | 235 _change('b', isRemove: true), |
217 _lengthChange, | 236 _lengthChange, |
218 ]); | 237 ]); |
219 }); | 238 }); |
220 | 239 |
221 test('clear', () { | 240 observeTest('clear', () { |
222 map.clear(); | 241 map.clear(); |
223 expect(map, {}); | 242 expect(map, {}); |
224 | 243 |
225 deliverChangeRecords(); | 244 performMicrotaskCheckpoint(); |
226 expectChanges(records, [ | 245 expectChanges(records, [ |
227 _change('a', isRemove: true), | 246 _change('a', isRemove: true), |
228 _change('b', isRemove: true), | 247 _change('b', isRemove: true), |
229 _lengthChange, | 248 _lengthChange, |
230 ]); | 249 ]); |
231 }); | 250 }); |
232 }); | 251 }); |
233 } | 252 } |
234 | 253 |
235 | 254 |
236 const _LENGTH = const Symbol('length'); | 255 const _LENGTH = const Symbol('length'); |
237 | 256 |
238 final _lengthChange = new PropertyChangeRecord(_LENGTH); | 257 final _lengthChange = new PropertyChangeRecord(_LENGTH); |
239 | 258 |
240 _change(key, {isInsert: false, isRemove: false}) => | 259 _change(key, {isInsert: false, isRemove: false}) => |
241 new MapChangeRecord(key, isInsert: isInsert, isRemove: isRemove); | 260 new MapChangeRecord(key, isInsert: isInsert, isRemove: isRemove); |
OLD | NEW |