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 library stream_state_helper; | 5 library stream_state_helper; |
6 | 6 |
7 import "../../../pkg/unittest/lib/unittest.dart"; | 7 import "../../../pkg/unittest/lib/unittest.dart"; |
8 import "dart:async"; | 8 import "dart:async"; |
9 import "dart:collection"; | 9 import "dart:collection"; |
10 | 10 |
11 class SubscriptionProtocolTest { | |
12 final StreamProtocolTest _streamTest; | |
13 final int id; | |
14 StreamSubscription _subscription; | |
15 | |
16 SubscriptionProtocolTest(this.id, this._subscription, this._streamTest); | |
17 | |
18 void pause([Future resumeSignal]) { | |
19 if (_subscription == null) throw new StateError("Not subscribed"); | |
20 _subscription.pause(resumeSignal); | |
21 } | |
22 | |
23 void resume() { | |
24 if (_subscription == null) throw new StateError("Not subscribed"); | |
25 _subscription.resume(); | |
26 } | |
27 | |
28 void cancel() { | |
29 if (_subscription == null) throw new StateError("Not subscribed"); | |
30 _subscription.cancel(); | |
31 _subscription = null; | |
32 } | |
33 | |
34 void expectData(var data, [void action()]) { | |
35 _streamTest._expectData(this, data, action); | |
36 } | |
37 | |
38 void expectError(var error, [void action()]) { | |
39 _streamTest._expectError(this, error, action); | |
40 } | |
41 | |
42 void expectDone([void action()]) { | |
43 _streamTest._expectDone(this, action); | |
44 } | |
45 } | |
46 | |
11 class StreamProtocolTest { | 47 class StreamProtocolTest { |
12 bool trace = false; | 48 bool trace = false; |
49 // If not a broadcast stream, the onComplete is called automatically by | |
50 // the first onCancel. | |
51 bool isBroadcast; | |
13 StreamController _controller; | 52 StreamController _controller; |
14 Stream _controllerStream; | 53 Stream _controllerStream; |
15 StreamSubscription _subscription; | 54 // Most recent subscription created. Used as default for pause/resume. |
55 SubscriptionProtocolTest _latestSubscription; | |
16 List<Event> _expectations = new List<Event>(); | 56 List<Event> _expectations = new List<Event>(); |
17 int _nextExpectationIndex = 0; | 57 int _nextExpectationIndex = 0; |
58 int _subscriptionIdCounter = 0; | |
18 Function _onComplete; | 59 Function _onComplete; |
19 | 60 |
20 StreamProtocolTest([bool broadcast = false]) { | 61 StreamProtocolTest.broadcast({ bool sync: false }) : isBroadcast = true { |
floitsch
2013/05/30 12:13:48
move initializer list to next line.
Lasse Reichstein Nielsen
2013/05/31 05:51:59
There is room?
But ok.
| |
62 _controller = new StreamController.broadcast( | |
63 sync: sync, | |
64 onListen: _onListen, | |
65 onCancel: _onCancel); | |
66 _controllerStream = _controller.stream; | |
67 _onComplete = expectAsync0((){ | |
68 _onComplete = null; // Being null marks the test as being complete. | |
69 }); | |
70 } | |
71 | |
72 StreamProtocolTest({ bool broadcast: false, bool sync: false }) | |
73 : isBroadcast = false { | |
21 _controller = new StreamController( | 74 _controller = new StreamController( |
22 onListen: _onSubcription, | 75 sync: sync, |
23 onPause: _onPause, | 76 onListen: _onListen, |
24 onResume: _onResume, | 77 onPause: _onPause, |
25 onCancel: _onCancel); | 78 onResume: _onResume, |
79 onCancel: _onCancel); | |
26 if (broadcast) { | 80 if (broadcast) { |
27 _controllerStream = _controller.stream.asBroadcastStream(); | 81 _controllerStream = _controller.stream.asBroadcastStream(); |
28 } else { | 82 } else { |
29 _controllerStream = _controller.stream; | 83 _controllerStream = _controller.stream; |
30 } | 84 } |
31 _onComplete = expectAsync0((){ | 85 _onComplete = expectAsync0((){ |
32 _onComplete = null; // Being null marks the test to be complete. | 86 _onComplete = null; // Being null marks the test as being complete. |
33 }); | 87 }); |
34 } | 88 } |
35 | 89 |
36 // Actions on the stream and controller. | 90 // Actions on the stream and controller. |
37 void add(var data) { _controller.add(data); } | 91 void add(var data) { _controller.add(data); } |
38 void error(var error) { _controller.addError(error); } | 92 void error(var error) { _controller.addError(error); } |
39 void close() { _controller.close(); } | 93 void close() { _controller.close(); } |
40 | 94 |
41 void subscribe({bool cancelOnError : false}) { | 95 SubscriptionProtocolTest listen({bool cancelOnError : false}) { |
42 // TODO(lrn): Handle more subscriptions (e.g., a subscription-id | 96 int subscriptionId = _subscriptionIdCounter++; |
43 // per subscription, and an id on event _expectations). | 97 |
44 if (_subscription != null) throw new StateError("Already subscribed"); | 98 StreamSubscription subscription = _controllerStream.listen( |
45 _subscription = _controllerStream.listen(_onData, | 99 (var data) { _onData(subscriptionId, data); }, |
46 onError: _onError, | 100 onError: (Object error) { _onError(subscriptionId, error); }, |
47 onDone: _onDone, | 101 onDone: () { _onDone(subscriptionId); }, |
48 cancelOnError: | 102 cancelOnError: cancelOnError); |
49 cancelOnError); | 103 _latestSubscription = |
104 new SubscriptionProtocolTest(subscriptionId, subscription, this); | |
105 return _latestSubscription; | |
50 } | 106 } |
51 | 107 |
108 // Actions on the most recently created subscription. | |
52 void pause([Future resumeSignal]) { | 109 void pause([Future resumeSignal]) { |
53 if (_subscription == null) throw new StateError("Not subscribed"); | 110 _latestSubscription.pause(resumeSignal); |
54 _subscription.pause(resumeSignal); | |
55 } | 111 } |
56 | 112 |
57 void resume() { | 113 void resume() { |
58 if (_subscription == null) throw new StateError("Not subscribed"); | 114 _latestSubscription.resume(); |
59 _subscription.resume(); | |
60 } | 115 } |
61 | 116 |
62 void cancel() { | 117 void cancel() { |
63 if (_subscription == null) throw new StateError("Not subscribed"); | 118 _latestSubscription.cancel(); |
64 _subscription.cancel(); | 119 _latestSubscription = null; |
65 _subscription = null; | 120 } |
121 | |
122 // End the test now. There must be no open expectations, and no furter | |
123 // expectations will be allowed. | |
124 // Called automatically by an onCancel event on a non-broadcast stream. | |
125 void terminate() { | |
126 if (_nextExpectationIndex != _expectations.length) { | |
127 _withNextExpectation((Event expect) { | |
128 _fail("Expected: $expect\n" | |
129 "Found : Early termination."); | |
130 }); | |
131 } | |
132 _onComplete(); | |
66 } | 133 } |
67 | 134 |
68 // Handling of stream events. | 135 // Handling of stream events. |
69 void _onData(var data) { | 136 void _onData(int id, var data) { |
70 if (trace) print("[Data : $data]"); | 137 if (trace) print("[Data#$id : $data]"); |
71 _withNextExpectation((Event expect) { | 138 _withNextExpectation((Event expect) { |
72 if (!expect.matchData(data)) { | 139 if (!expect.matchData(id, data)) { |
73 _fail("Expected: $expect\n" | 140 _fail("Expected: $expect\n" |
74 "Found : [Data: $data]"); | 141 "Found : [Data#$id: $data]"); |
75 } | 142 } |
76 }); | 143 }); |
77 } | 144 } |
78 | 145 |
79 void _onError(error) { | 146 void _onError(int id, Object error) { |
80 if (trace) print("[Error : $error]"); | 147 if (trace) print("[Error#$id : $error]"); |
81 _withNextExpectation((Event expect) { | 148 _withNextExpectation((Event expect) { |
82 if (!expect.matchError(error)) { | 149 if (!expect.matchError(id, error)) { |
83 _fail("Expected: $expect\n" | 150 _fail("Expected: $expect\n" |
84 "Found : [Error: ${error}]"); | 151 "Found : [Error#$id: ${error}]"); |
85 } | 152 } |
86 }); | 153 }); |
87 } | 154 } |
88 | 155 |
89 void _onDone() { | 156 void _onDone(int id) { |
90 if (trace) print("[Done]"); | 157 if (trace) print("[Done#$id]"); |
91 _subscription = null; | |
92 _withNextExpectation((Event expect) { | 158 _withNextExpectation((Event expect) { |
93 if (!expect.matchDone()) { | 159 if (!expect.matchDone(id)) { |
94 _fail("Expected: $expect\n" | 160 _fail("Expected: $expect\n" |
95 "Found : [Done]"); | 161 "Found : [Done#$id]"); |
96 } | 162 } |
97 }); | 163 }); |
98 } | 164 } |
99 | 165 |
100 void _onPause() { | 166 void _onPause() { |
101 if (trace) print("[Pause]"); | 167 if (trace) print("[Pause]"); |
102 _withNextExpectation((Event expect) { | 168 _withNextExpectation((Event expect) { |
103 if (!expect.matchPause()) { | 169 if (!expect.matchPause()) { |
104 _fail("Expected: $expect\n" | 170 _fail("Expected: $expect\n" |
105 "Found : [Paused]"); | 171 "Found : [Paused]"); |
106 } | 172 } |
107 }); | 173 }); |
108 } | 174 } |
109 | 175 |
110 void _onResume() { | 176 void _onResume() { |
111 if (trace) print("[Resumed]"); | 177 if (trace) print("[Resumed]"); |
112 _withNextExpectation((Event expect) { | 178 _withNextExpectation((Event expect) { |
113 if (!expect.matchResume()) { | 179 if (!expect.matchResume()) { |
114 _fail("Expected: $expect\n" | 180 _fail("Expected: $expect\n" |
115 "Found : [Resumed]"); | 181 "Found : [Resumed]"); |
116 } | 182 } |
117 }); | 183 }); |
118 } | 184 } |
119 | 185 |
120 void _onSubcription() { | 186 void _onListen() { |
121 if (trace) print("[Subscribed]"); | 187 if (trace) print("[Subscribed]"); |
122 _withNextExpectation((Event expect) { | 188 _withNextExpectation((Event expect) { |
123 if (!expect.matchSubscribe()) { | 189 if (!expect.matchSubscribe()) { |
124 _fail("Expected: $expect\n" | 190 _fail("Expected: $expect\n" |
125 "Found: [Subscribed]"); | 191 "Found: [Subscribed]"); |
126 } | 192 } |
127 }); | 193 }); |
128 } | 194 } |
129 | 195 |
130 void _onCancel() { | 196 void _onCancel() { |
131 if (trace) print("[Cancelled]"); | 197 if (trace) print("[Cancelled]"); |
132 _withNextExpectation((Event expect) { | 198 _withNextExpectation((Event expect) { |
133 if (!expect.matchCancel()) { | 199 if (!expect.matchCancel()) { |
134 _fail("Expected: $expect\n" | 200 _fail("Expected: $expect\n" |
135 "Found: [Cancelled]"); | 201 "Found: [Cancelled]"); |
136 } | 202 } |
137 }); | 203 }); |
204 if (!isBroadcast) terminate(); | |
138 } | 205 } |
139 | 206 |
140 void _withNextExpectation(void action(Event expect)) { | 207 void _withNextExpectation(void action(Event expect)) { |
141 if (_nextExpectationIndex == _expectations.length) { | 208 if (_nextExpectationIndex == _expectations.length) { |
142 action(new MismatchEvent()); | 209 action(new MismatchEvent()); |
143 } else { | 210 } else { |
144 Event next = _expectations[_nextExpectationIndex]; | 211 Event next = _expectations[_nextExpectationIndex++]; |
145 action(next); | 212 action(next); |
146 } | 213 } |
147 _nextExpectationIndex++; | |
148 _checkDone(); | |
149 } | 214 } |
150 | 215 |
151 void _checkDone() { | |
152 if (_nextExpectationIndex == _expectations.length) { | |
153 _onComplete(); | |
154 } | |
155 } | |
156 | |
157 | |
158 // Adds _expectations. | 216 // Adds _expectations. |
159 void expectAny([void action()]) { | 217 void expectAny([void action()]) { |
160 if (_onComplete == null) { | 218 if (_onComplete == null) { |
161 _fail("Adding expectation after completing"); | 219 _fail("Adding expectation after completing"); |
162 } | 220 } |
163 _expectations.add(new LogAnyEvent(action)); | 221 _expectations.add(new LogAnyEvent(action)); |
164 } | 222 } |
223 | |
165 void expectData(var data, [void action()]) { | 224 void expectData(var data, [void action()]) { |
225 _expectData(null, data, action); | |
226 } | |
227 | |
228 void _expectData(SubscriptionProtocolTest sub, var data, [void action()]) { | |
166 if (_onComplete == null) { | 229 if (_onComplete == null) { |
167 _fail("Adding expectation after completing"); | 230 _fail("Adding expectation after completing"); |
168 } | 231 } |
169 _expectations.add(new DataEvent(data, action)); | 232 _expectations.add(new DataEvent(sub, data, action)); |
170 } | 233 } |
234 | |
171 void expectError(var error, [void action()]) { | 235 void expectError(var error, [void action()]) { |
236 _expectError(null, error, action); | |
237 } | |
238 | |
239 void _expectError(SubscriptionProtocolTest sub, var error, [void action()]) { | |
172 if (_onComplete == null) { | 240 if (_onComplete == null) { |
173 _fail("Adding expectation after completing"); | 241 _fail("Adding expectation after completing"); |
174 } | 242 } |
175 _expectations.add(new ErrorEvent(error, action)); | 243 _expectations.add(new ErrorEvent(sub, error, action)); |
176 } | 244 } |
245 | |
177 void expectDone([void action()]) { | 246 void expectDone([void action()]) { |
247 _expectDone(null, action); | |
248 } | |
249 | |
250 void _expectDone(SubscriptionProtocolTest sub, [void action()]) { | |
178 if (_onComplete == null) { | 251 if (_onComplete == null) { |
179 _fail("Adding expectation after completing"); | 252 _fail("Adding expectation after completing"); |
180 } | 253 } |
181 _expectations.add(new DoneEvent(action)); | 254 _expectations.add(new DoneEvent(sub, action)); |
182 } | 255 } |
256 | |
183 void expectPause([void action()]) { | 257 void expectPause([void action()]) { |
184 if (_onComplete == null) { | 258 if (_onComplete == null) { |
185 _fail("Adding expectation after completing"); | 259 _fail("Adding expectation after completing"); |
186 } | 260 } |
187 _expectations.add(new PauseCallbackEvent(action)); | 261 _expectations.add(new PauseCallbackEvent(action)); |
188 } | 262 } |
189 void expectResume([void action()]) { | 263 void expectResume([void action()]) { |
190 if (_onComplete == null) { | 264 if (_onComplete == null) { |
191 _fail("Adding expectation after completing"); | 265 _fail("Adding expectation after completing"); |
192 } | 266 } |
193 _expectations.add(new ResumeCallbackEvent(action)); | 267 _expectations.add(new ResumeCallbackEvent(action)); |
194 } | 268 } |
195 void expectSubscription([void action()]) { | 269 void expectListen([void action()]) { |
196 if (_onComplete == null) { | 270 if (_onComplete == null) { |
197 _fail("Adding expectation after completing"); | 271 _fail("Adding expectation after completing"); |
198 } | 272 } |
199 _expectations.add( | 273 _expectations.add( |
200 new SubscriptionCallbackEvent(action)); | 274 new SubscriptionCallbackEvent(action)); |
201 } | 275 } |
202 void expectCancel([void action()]) { | 276 void expectCancel([void action()]) { |
203 if (_onComplete == null) { | 277 if (_onComplete == null) { |
204 _fail("Adding expectation after completing"); | 278 _fail("Adding expectation after completing"); |
205 } | 279 } |
206 _expectations.add( | 280 _expectations.add( |
207 new CancelCallbackEvent(action)); | 281 new CancelCallbackEvent(action)); |
208 } | 282 } |
209 | 283 |
210 void _fail(String message) { | 284 void _fail(String message) { |
211 if (_nextExpectationIndex == 0) { | 285 if (_nextExpectationIndex == 0) { |
212 throw "Unexpected event:\n$message\nNo earlier events matched."; | 286 throw "Unexpected event:\n$message\nNo earlier events matched."; |
213 } | 287 } |
214 throw "Unexpected event:\n$message\nMatched so far:\n" | 288 throw "Unexpected event:\n$message\nMatched so far:\n" |
215 " ${_expectations.take(_nextExpectationIndex).join("\n ")}"; | 289 " ${_expectations.take(_nextExpectationIndex - 1).join("\n ")}"; |
216 } | 290 } |
217 } | 291 } |
218 | 292 |
219 class Event { | 293 class Event { |
220 Function _action; | 294 Function _action; |
221 Event(void this._action()); | 295 Event(void this._action()); |
222 | 296 |
223 bool matchData(var data) { | 297 bool matchData(int id, var data) { |
224 if (!_testData(data)) return false; | 298 return false; |
225 if (_action != null) _action(); | |
226 return true; | |
227 } | 299 } |
228 bool matchError(e) { | 300 bool matchError(int id, e) { |
229 if (!_testError(e)) return false; | 301 return false; |
230 if (_action != null) _action(); | |
231 return true; | |
232 } | 302 } |
233 bool matchDone() { | 303 bool matchDone(int id) { |
234 if (!_testDone()) return false; | 304 return false; |
235 if (_action != null) _action(); | |
236 return true; | |
237 } | 305 } |
238 bool matchPause() { | 306 bool matchPause() { |
239 if (!_testPause()) return false; | 307 if (!_testPause()) return false; |
240 if (_action != null) _action(); | 308 if (_action != null) _action(); |
241 return true; | 309 return true; |
242 } | 310 } |
243 bool matchResume() { | 311 bool matchResume() { |
244 if (!_testResume()) return false; | 312 if (!_testResume()) return false; |
245 if (_action != null) _action(); | 313 if (_action != null) _action(); |
246 return true; | 314 return true; |
(...skipping 11 matching lines...) Expand all Loading... | |
258 | 326 |
259 bool _testData(_) => false; | 327 bool _testData(_) => false; |
260 bool _testError(_) => false; | 328 bool _testError(_) => false; |
261 bool _testDone() => false; | 329 bool _testDone() => false; |
262 bool _testPause() => false; | 330 bool _testPause() => false; |
263 bool _testResume() => false; | 331 bool _testResume() => false; |
264 bool _testSubscribe() => false; | 332 bool _testSubscribe() => false; |
265 bool _testCancel() => false; | 333 bool _testCancel() => false; |
266 } | 334 } |
267 | 335 |
336 class SubscriptionEvent extends Event { | |
337 SubscriptionProtocolTest subscription; | |
338 SubscriptionEvent(this.subscription, void action()) : super(action); | |
339 | |
340 bool matchData(int id, var data) { | |
341 if (subscription != null && subscription.id != id) return false; | |
342 if (!_testData(data)) return false; | |
343 if (_action != null) _action(); | |
344 return true; | |
345 } | |
346 bool matchError(int id, e) { | |
347 if (subscription != null && subscription.id != id) return false; | |
348 if (!_testError(e)) return false; | |
349 if (_action != null) _action(); | |
350 return true; | |
351 } | |
352 bool matchDone(int id) { | |
353 if (subscription != null && subscription.id != id) return false; | |
354 if (!_testDone()) return false; | |
355 if (_action != null) _action(); | |
356 return true; | |
357 } | |
358 String get _id => (subscription == null) ? "" : "#${subscription.id}"; | |
359 } | |
360 | |
268 class MismatchEvent extends Event { | 361 class MismatchEvent extends Event { |
269 MismatchEvent() : super(null); | 362 MismatchEvent() : super(null); |
270 toString() => "[No event expected]"; | 363 toString() => "[No event expected]"; |
271 } | 364 } |
272 | 365 |
273 class DataEvent extends Event { | 366 class DataEvent extends SubscriptionEvent { |
274 final data; | 367 final data; |
275 DataEvent(this.data, void action()) : super(action); | 368 DataEvent(SubscriptionProtocolTest sub, this.data, void action()) |
369 : super(sub, action); | |
276 bool _testData(var data) => this.data == data; | 370 bool _testData(var data) => this.data == data; |
277 String toString() => "[Data: $data]"; | 371 String toString() => "[Data$_id: $data]"; |
278 } | 372 } |
279 | 373 |
280 class ErrorEvent extends Event { | 374 class ErrorEvent extends SubscriptionEvent { |
281 final error; | 375 final error; |
282 ErrorEvent(this.error, void action()) : super(action); | 376 ErrorEvent(SubscriptionProtocolTest sub, this.error, void action()) |
377 : super(sub, action); | |
283 bool _testError(error) => this.error == error; | 378 bool _testError(error) => this.error == error; |
284 String toString() => "[Error: $error]"; | 379 String toString() => "[Error$_id: $error]"; |
285 } | 380 } |
286 | 381 |
287 class DoneEvent extends Event { | 382 class DoneEvent extends SubscriptionEvent { |
288 DoneEvent(void action()) : super(action); | 383 DoneEvent(SubscriptionProtocolTest sub, void action()) : super(sub, action); |
289 bool _testDone() => true; | 384 bool _testDone() => true; |
290 String toString() => "[Done]"; | 385 String toString() => "[Done$_id]"; |
291 } | 386 } |
292 | 387 |
293 class PauseCallbackEvent extends Event { | 388 class PauseCallbackEvent extends Event { |
294 PauseCallbackEvent(void action()) : super(action); | 389 PauseCallbackEvent(void action()) : super(action); |
295 bool _testPause() => true; | 390 bool _testPause() => true; |
296 String toString() => "[Paused]"; | 391 String toString() => "[Paused]"; |
297 } | 392 } |
298 | 393 |
299 class ResumeCallbackEvent extends Event { | 394 class ResumeCallbackEvent extends Event { |
300 ResumeCallbackEvent(void action()) : super(action); | 395 ResumeCallbackEvent(void action()) : super(action); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
342 _actual = "*[Subscribed]"; | 437 _actual = "*[Subscribed]"; |
343 return true; | 438 return true; |
344 } | 439 } |
345 bool _testCancel() { | 440 bool _testCancel() { |
346 _actual = "*[Cancelled]"; | 441 _actual = "*[Cancelled]"; |
347 return true; | 442 return true; |
348 } | 443 } |
349 | 444 |
350 String toString() => _actual; | 445 String toString() => _actual; |
351 } | 446 } |
OLD | NEW |