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

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

Issue 19771010: implement dirty checking for @observable objects (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 5 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
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 // This file contains code ported from: 10 // This file contains code ported from:
10 // https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js 11 // https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js
11 12
12 main() { 13 main() {
13 // TODO(jmesserly): rename this? Is summarizeListChanges coming back? 14 // TODO(jmesserly): rename this? Is summarizeListChanges coming back?
14 group('summarizeListChanges', listChangeTests); 15 group('summarizeListChanges', listChangeTests);
15 } 16 }
16 17
17 // TODO(jmesserly): port or write array fuzzer tests 18 // TODO(jmesserly): port or write array fuzzer tests
18 listChangeTests() { 19 listChangeTests() {
20 StreamSubscription sub;
19 21
20 test('sequential adds', () { 22 tearDown(() { sub.cancel(); });
23
24 observeTest('sequential adds', () {
21 var model = toObservable([]); 25 var model = toObservable([]);
22 model.add(0); 26 model.add(0);
23 27
24 var summary; 28 var summary;
25 var sub = model.changes.listen((r) { summary = _filter(r); }); 29 sub = model.changes.listen((r) { summary = _filter(r); });
26 30
27 model.add(1); 31 model.add(1);
28 model.add(2); 32 model.add(2);
29 33
30 expect(summary, null); 34 expect(summary, null);
31 deliverChangeRecords(); 35 performMicrotaskCheckpoint();
32 36
33 expectChanges(summary, [_delta(1, 0, 2)]); 37 expectChanges(summary, [_delta(1, 0, 2)]);
34 }); 38 });
35 39
36 test('List Splice Truncate And Expand With Length', () { 40 observeTest('List Splice Truncate And Expand With Length', () {
37 var model = toObservable(['a', 'b', 'c', 'd', 'e']); 41 var model = toObservable(['a', 'b', 'c', 'd', 'e']);
38 42
39 var summary; 43 var summary;
40 var sub = model.changes.listen((r) { summary = _filter(r); }); 44 sub = model.changes.listen((r) { summary = _filter(r); });
41 45
42 model.length = 2; 46 model.length = 2;
43 47
44 deliverChangeRecords(); 48 performMicrotaskCheckpoint();
45 expectChanges(summary, [_delta(2, 3, 0)]); 49 expectChanges(summary, [_delta(2, 3, 0)]);
46 summary = null; 50 summary = null;
47 51
48 model.length = 5; 52 model.length = 5;
49 53
50 deliverChangeRecords(); 54 performMicrotaskCheckpoint();
51 expectChanges(summary, [_delta(2, 0, 3)]); 55 expectChanges(summary, [_delta(2, 0, 3)]);
52 }); 56 });
53 57
54 group('List deltas can be applied', () { 58 group('List deltas can be applied', () {
55 59
56 var summary = null; 60 var summary = null;
57 61
58 observeArray(model) { 62 observeArray(model) {
59 model.changes.listen((records) { summary = _filter(records); }); 63 sub = model.changes.listen((records) { summary = _filter(records); });
60 } 64 }
61 65
62 applyAndCheckDeltas(model, copy) { 66 applyAndCheckDeltas(model, copy) {
63 summary = null; 67 summary = null;
64 deliverChangeRecords(); 68 performMicrotaskCheckpoint();
65 69
66 // apply deltas to the copy 70 // apply deltas to the copy
67 for (var delta in summary) { 71 for (var delta in summary) {
68 copy.removeRange(delta.index, delta.index + delta.removedCount); 72 copy.removeRange(delta.index, delta.index + delta.removedCount);
69 for (int i = delta.addedCount - 1; i >= 0; i--) { 73 for (int i = delta.addedCount - 1; i >= 0; i--) {
70 copy.insert(delta.index, model[delta.index + i]); 74 copy.insert(delta.index, model[delta.index + i]);
71 } 75 }
72 } 76 }
73 77
74 // Note: compare strings for easier debugging. 78 // Note: compare strings for easier debugging.
75 expect('$copy', '$model', reason: '!!! summary $summary'); 79 expect('$copy', '$model', reason: '!!! summary $summary');
76 } 80 }
77 81
78 test('Contained', () { 82 observeTest('Contained', () {
79 var model = toObservable(['a', 'b']); 83 var model = toObservable(['a', 'b']);
80 var copy = model.toList(); 84 var copy = model.toList();
81 observeArray(model); 85 observeArray(model);
82 86
83 model.removeAt(1); 87 model.removeAt(1);
84 model.insertAll(0, ['c', 'd', 'e']); 88 model.insertAll(0, ['c', 'd', 'e']);
85 model.removeRange(1, 3); 89 model.removeRange(1, 3);
86 model.insert(1, 'f'); 90 model.insert(1, 'f');
87 91
88 applyAndCheckDeltas(model, copy); 92 applyAndCheckDeltas(model, copy);
89 }); 93 });
90 94
91 test('Delete Empty', () { 95 observeTest('Delete Empty', () {
92 var model = toObservable([1]); 96 var model = toObservable([1]);
93 var copy = model.toList(); 97 var copy = model.toList();
94 observeArray(model); 98 observeArray(model);
95 99
96 model.removeAt(0); 100 model.removeAt(0);
97 model.insertAll(0, ['a', 'b', 'c']); 101 model.insertAll(0, ['a', 'b', 'c']);
98 102
99 applyAndCheckDeltas(model, copy); 103 applyAndCheckDeltas(model, copy);
100 }); 104 });
101 105
102 test('Right Non Overlap', () { 106 observeTest('Right Non Overlap', () {
103 var model = toObservable(['a', 'b', 'c', 'd']); 107 var model = toObservable(['a', 'b', 'c', 'd']);
104 var copy = model.toList(); 108 var copy = model.toList();
105 observeArray(model); 109 observeArray(model);
106 110
107 model.removeRange(0, 1); 111 model.removeRange(0, 1);
108 model.insert(0, 'e'); 112 model.insert(0, 'e');
109 model.removeRange(2, 3); 113 model.removeRange(2, 3);
110 model.insertAll(2, ['f', 'g']); 114 model.insertAll(2, ['f', 'g']);
111 115
112 applyAndCheckDeltas(model, copy); 116 applyAndCheckDeltas(model, copy);
113 }); 117 });
114 118
115 test('Left Non Overlap', () { 119 observeTest('Left Non Overlap', () {
116 var model = toObservable(['a', 'b', 'c', 'd']); 120 var model = toObservable(['a', 'b', 'c', 'd']);
117 var copy = model.toList(); 121 var copy = model.toList();
118 observeArray(model); 122 observeArray(model);
119 123
120 model.removeRange(3, 4); 124 model.removeRange(3, 4);
121 model.insertAll(3, ['f', 'g']); 125 model.insertAll(3, ['f', 'g']);
122 model.removeRange(0, 1); 126 model.removeRange(0, 1);
123 model.insert(0, 'e'); 127 model.insert(0, 'e');
124 128
125 applyAndCheckDeltas(model, copy); 129 applyAndCheckDeltas(model, copy);
126 }); 130 });
127 131
128 test('Right Adjacent', () { 132 observeTest('Right Adjacent', () {
129 var model = toObservable(['a', 'b', 'c', 'd']); 133 var model = toObservable(['a', 'b', 'c', 'd']);
130 var copy = model.toList(); 134 var copy = model.toList();
131 observeArray(model); 135 observeArray(model);
132 136
133 model.removeRange(1, 2); 137 model.removeRange(1, 2);
134 model.insert(3, 'e'); 138 model.insert(3, 'e');
135 model.removeRange(2, 3); 139 model.removeRange(2, 3);
136 model.insertAll(0, ['f', 'g']); 140 model.insertAll(0, ['f', 'g']);
137 141
138 applyAndCheckDeltas(model, copy); 142 applyAndCheckDeltas(model, copy);
139 }); 143 });
140 144
141 test('Left Adjacent', () { 145 observeTest('Left Adjacent', () {
142 var model = toObservable(['a', 'b', 'c', 'd']); 146 var model = toObservable(['a', 'b', 'c', 'd']);
143 var copy = model.toList(); 147 var copy = model.toList();
144 observeArray(model); 148 observeArray(model);
145 149
146 model.removeRange(2, 4); 150 model.removeRange(2, 4);
147 model.insert(2, 'e'); 151 model.insert(2, 'e');
148 152
149 model.removeAt(1); 153 model.removeAt(1);
150 model.insertAll(1, ['f', 'g']); 154 model.insertAll(1, ['f', 'g']);
151 155
152 applyAndCheckDeltas(model, copy); 156 applyAndCheckDeltas(model, copy);
153 }); 157 });
154 158
155 test('Right Overlap', () { 159 observeTest('Right Overlap', () {
156 var model = toObservable(['a', 'b', 'c', 'd']); 160 var model = toObservable(['a', 'b', 'c', 'd']);
157 var copy = model.toList(); 161 var copy = model.toList();
158 observeArray(model); 162 observeArray(model);
159 163
160 model.removeAt(1); 164 model.removeAt(1);
161 model.insert(1, 'e'); 165 model.insert(1, 'e');
162 model.removeAt(1); 166 model.removeAt(1);
163 model.insertAll(1, ['f', 'g']); 167 model.insertAll(1, ['f', 'g']);
164 168
165 applyAndCheckDeltas(model, copy); 169 applyAndCheckDeltas(model, copy);
166 }); 170 });
167 171
168 test('Left Overlap', () { 172 observeTest('Left Overlap', () {
169 var model = toObservable(['a', 'b', 'c', 'd']); 173 var model = toObservable(['a', 'b', 'c', 'd']);
170 var copy = model.toList(); 174 var copy = model.toList();
171 observeArray(model); 175 observeArray(model);
172 176
173 model.removeAt(2); 177 model.removeAt(2);
174 model.insertAll(2, ['e', 'f', 'g']); 178 model.insertAll(2, ['e', 'f', 'g']);
175 // a b [e f g] d 179 // a b [e f g] d
176 model.removeRange(1, 3); 180 model.removeRange(1, 3);
177 model.insertAll(1, ['h', 'i', 'j']); 181 model.insertAll(1, ['h', 'i', 'j']);
178 // a [h i j] f g d 182 // a [h i j] f g d
179 183
180 applyAndCheckDeltas(model, copy); 184 applyAndCheckDeltas(model, copy);
181 }); 185 });
182 186
183 test('Prefix And Suffix One In', () { 187 observeTest('Prefix And Suffix One In', () {
184 var model = toObservable(['a', 'b', 'c', 'd']); 188 var model = toObservable(['a', 'b', 'c', 'd']);
185 var copy = model.toList(); 189 var copy = model.toList();
186 observeArray(model); 190 observeArray(model);
187 191
188 model.insert(0, 'z'); 192 model.insert(0, 'z');
189 model.add('z'); 193 model.add('z');
190 194
191 applyAndCheckDeltas(model, copy); 195 applyAndCheckDeltas(model, copy);
192 }); 196 });
193 197
194 test('Remove First', () { 198 observeTest('Remove First', () {
195 var model = toObservable([16, 15, 15]); 199 var model = toObservable([16, 15, 15]);
196 var copy = model.toList(); 200 var copy = model.toList();
197 observeArray(model); 201 observeArray(model);
198 202
199 model.removeAt(0); 203 model.removeAt(0);
200 204
201 applyAndCheckDeltas(model, copy); 205 applyAndCheckDeltas(model, copy);
202 }); 206 });
203 207
204 test('Update Remove', () { 208 observeTest('Update Remove', () {
205 var model = toObservable(['a', 'b', 'c', 'd']); 209 var model = toObservable(['a', 'b', 'c', 'd']);
206 var copy = model.toList(); 210 var copy = model.toList();
207 observeArray(model); 211 observeArray(model);
208 212
209 model.removeAt(2); 213 model.removeAt(2);
210 model.insertAll(2, ['e', 'f', 'g']); // a b [e f g] d 214 model.insertAll(2, ['e', 'f', 'g']); // a b [e f g] d
211 model[0] = 'h'; 215 model[0] = 'h';
212 model.removeAt(1); 216 model.removeAt(1);
213 217
214 applyAndCheckDeltas(model, copy); 218 applyAndCheckDeltas(model, copy);
215 }); 219 });
216 220
217 test('Remove Mid List', () { 221 observeTest('Remove Mid List', () {
218 var model = toObservable(['a', 'b', 'c', 'd']); 222 var model = toObservable(['a', 'b', 'c', 'd']);
219 var copy = model.toList(); 223 var copy = model.toList();
220 observeArray(model); 224 observeArray(model);
221 225
222 model.removeAt(2); 226 model.removeAt(2);
223 227
224 applyAndCheckDeltas(model, copy); 228 applyAndCheckDeltas(model, copy);
225 }); 229 });
226 }); 230 });
227 231
228 group('edit distance', () { 232 group('edit distance', () {
229 var summary = null; 233 var summary = null;
230 234
231 observeArray(model) { 235 observeArray(model) {
232 model.changes.listen((records) { summary = _filter(records); }); 236 sub = model.changes.listen((records) { summary = _filter(records); });
233 } 237 }
234 238
235 assertEditDistance(orig, expectDistance) { 239 assertEditDistance(orig, expectDistance) {
236 summary = null; 240 summary = null;
237 deliverChangeRecords(); 241 performMicrotaskCheckpoint();
238 var actualDistance = 0; 242 var actualDistance = 0;
239 243
240 if (summary != null) { 244 if (summary != null) {
241 for (var delta in summary) { 245 for (var delta in summary) {
242 actualDistance += delta.addedCount + delta.removedCount; 246 actualDistance += delta.addedCount + delta.removedCount;
243 } 247 }
244 } 248 }
245 249
246 expect(actualDistance, expectDistance); 250 expect(actualDistance, expectDistance);
247 } 251 }
248 252
249 test('add items', () { 253 observeTest('add items', () {
250 var model = toObservable([]); 254 var model = toObservable([]);
251 observeArray(model); 255 observeArray(model);
252 model.addAll([1, 2, 3]); 256 model.addAll([1, 2, 3]);
253 assertEditDistance(model, 3); 257 assertEditDistance(model, 3);
254 }); 258 });
255 259
256 test('trunacte and add, sharing a contiguous block', () { 260 observeTest('trunacte and add, sharing a contiguous block', () {
257 var model = toObservable(['x', 'x', 'x', 'x', '1', '2', '3']); 261 var model = toObservable(['x', 'x', 'x', 'x', '1', '2', '3']);
258 observeArray(model); 262 observeArray(model);
259 model.length = 0; 263 model.length = 0;
260 model.addAll(['1', '2', '3', 'y', 'y', 'y', 'y']); 264 model.addAll(['1', '2', '3', 'y', 'y', 'y', 'y']);
261 // Note: unlike the JS implementation, we don't perform a full diff. 265 // Note: unlike the JS implementation, we don't perform a full diff.
262 // The change records are computed with no regards to the *contents* of 266 // The change records are computed with no regards to the *contents* of
263 // the array. Thus, we get 14 instead of 8. 267 // the array. Thus, we get 14 instead of 8.
264 assertEditDistance(model, 14); 268 assertEditDistance(model, 14);
265 }); 269 });
266 270
267 test('truncate and add, sharing a discontiguous block', () { 271 observeTest('truncate and add, sharing a discontiguous block', () {
268 var model = toObservable(['1', '2', '3', '4', '5']); 272 var model = toObservable(['1', '2', '3', '4', '5']);
269 observeArray(model); 273 observeArray(model);
270 model.length = 0; 274 model.length = 0;
271 model.addAll(['a', '2', 'y', 'y', '4', '5', 'z', 'z']); 275 model.addAll(['a', '2', 'y', 'y', '4', '5', 'z', 'z']);
272 // Note: unlike the JS implementation, we don't perform a full diff. 276 // Note: unlike the JS implementation, we don't perform a full diff.
273 // The change records are computed with no regards to the *contents* of 277 // The change records are computed with no regards to the *contents* of
274 // the array. Thus, we get 13 instead of 7. 278 // the array. Thus, we get 13 instead of 7.
275 assertEditDistance(model, 13); 279 assertEditDistance(model, 13);
276 }); 280 });
277 281
278 test('insert at beginning and end', () { 282 observeTest('insert at beginning and end', () {
279 var model = toObservable([2, 3, 4]); 283 var model = toObservable([2, 3, 4]);
280 observeArray(model); 284 observeArray(model);
281 model.insert(0, 5); 285 model.insert(0, 5);
282 model[2] = 6; 286 model[2] = 6;
283 model.add(7); 287 model.add(7);
284 assertEditDistance(model, 4); 288 assertEditDistance(model, 4);
285 }); 289 });
286 }); 290 });
287 } 291 }
288 292
289 _delta(i, r, a) => new ListChangeRecord(i, removedCount: r, addedCount: a); 293 _delta(i, r, a) => new ListChangeRecord(i, removedCount: r, addedCount: a);
290 _filter(records) => records.where((r) => r is ListChangeRecord).toList(); 294 _filter(records) => records.where((r) => r is ListChangeRecord).toList();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698