| 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 |