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

Side by Side Diff: tests/lib/async/stream_state_test.dart

Issue 12393009: Change and structure how Stream implementations do callbacks. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 9 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 // Test the event/callback protocol of the stream implementations.
6
7 import "../../../pkg/unittest/lib/unittest.dart";
8 import "dart:collection";
9 import "dart:async";
10
11 const ms5 = const Duration(milliseconds: 5);
12
13 main() {
14 mainTest(false);
15 mainTest(true);
16 }
17
18 mainTest(bool broadcast) {
19 var p = broadcast ? "BC" : "SC";
20 test("$p-sub-data-done", () {
21 var t = new StreamProtocolTest(broadcast);
22 t..expectSubscription(true, false)
23 ..expectData(42)
24 ..expectDone()
25 ..expectSubscription(false, false);
26 t..subscribe()..add(42)..close();
27 });
28
29 test("$p-data-done-sub", () {
30 var t = new StreamProtocolTest(broadcast);
31 if (broadcast) {
32 t..expectDone();
33 } else {
34 t..expectSubscription(true, false)
35 ..expectData(42)
36 ..expectDone()
37 ..expectSubscription(false, false);
38 }
39 t..add(42)..close()..subscribe();
40 });
41
42 test("$p-sub-data/pause-done", () {
43 var t = new StreamProtocolTest(broadcast);
44 t..expectSubscription(true, false)
45 ..expectData(42, () {
46 t.pause(new Future.delayed(ms5, () => null));
47 })
48 ..expectPause(true)
49 ..expectDone()
50 ..expectSubscription(false, false);
51 // We are calling "close" while the controller is actually paused,
52 // and it will stay paused until the pending events are sent.
53 t..subscribe()..add(42)..close();
54 });
55
56 test("$p-sub-data/pause-resume/done", () {
57 var t = new StreamProtocolTest(broadcast);
58 t..expectSubscription(true, false)
59 ..expectData(42, () {
60 t.pause(new Future.delayed(ms5, () => null));
61 })
62 ..expectPause(true)
63 ..expectPause(false, () { t.close(); })
64 ..expectDone()
65 ..expectSubscription(false, false);
66 t..subscribe()..add(42);
67 });
68
69 test("$p-sub-data/pause/resume/pause/resume-done", () {
70 var t = new StreamProtocolTest(broadcast);
71 t..expectSubscription(true, false)
72 ..expectData(42, () {
73 t.pause();
74 })
75 ..expectPause(true, () { t.resume(); })
76 ..expectPause(false, () { t.pause(); })
77 ..expectPause(true, () { t.resume(); })
78 ..expectPause(false, () { t.close(); })
79 ..expectDone()
80 ..expectSubscription(false, false);
81 t..subscribe()..add(42);
82 });
83
84 test("$p-sub-data/pause+resume-done", () {
85 var t = new StreamProtocolTest(broadcast);
86 t..expectSubscription(true, false)
87 ..expectData(42, () {
88 t.pause();
89 t.resume();
90 t.close();
91 })
92 ..expectDone()
93 ..expectSubscription(false, false);
94 t..subscribe()..add(42);
95 });
96
97 test("$p-sub-data/data+pause-data-resume-done", () {
98 var t = new StreamProtocolTest(broadcast);
99 t..expectSubscription(true, false)
100 ..expectData(42, () {
101 t.add(43);
102 t.pause(new Future.delayed(ms5, () => null));
103 // Should now be paused until the future finishes.
104 // After that, the controller stays paused until the pending queue
105 // is empty.
106 })
107 ..expectPause(true)
108 ..expectData(43)
109 ..expectPause(false, () { t.close(); })
110 ..expectDone()
111 ..expectSubscription(false, false);
112 t..subscribe()..add(42);
113 });
114
115 test("$p-sub-data-unsubonerror", () {
116 var t = new StreamProtocolTest(broadcast);
117 t..expectSubscription(true, false)
118 ..expectData(42)
119 ..expectError("bad")
120 ..expectSubscription(false, !broadcast);
floitsch 2013/03/01 21:52:30 why is the non-broadcast one paused? It should be
Lasse Reichstein Nielsen 2013/03/04 11:53:02 Because that's how single-subscription streams wor
121 t..subscribe(unsubscribeOnError: true)
122 ..add(42)
123 ..error("bad")
124 ..add(43)
125 ..close();
126 });
127
128 test("$p-sub-data-no-unsubonerror", () {
129 var t = new StreamProtocolTest(broadcast);
130 t..expectSubscription(true, false)
131 ..expectData(42)
132 ..expectError("bad")
133 ..expectData(43)
134 ..expectDone()
135 ..expectSubscription(false, false);
136 t..subscribe(unsubscribeOnError: false)
137 ..add(42)
138 ..error("bad")
139 ..add(43)
140 ..close();
141 });
142
143 test("$p-pause-during-callback", () {
144 var t = new StreamProtocolTest(broadcast);
145 t..expectSubscription(true, false)
146 ..expectData(42, () {
147 t.pause();
148 })
149 ..expectPause(true, () {
150 t.resume();
151 })
152 ..expectPause(false, () {
153 t.pause();
154 t.resume();
155 t.close();
156 })
157 ..expectDone()
158 ..expectSubscription(false, false);
159 t..subscribe()
160 ..add(42);
161 });
162
163 test("$p-pause-resume-during-event", () {
164 var t = new StreamProtocolTest(broadcast);
165 t..expectSubscription(true, false)
166 ..expectData(42, () {
167 t.pause();
168 t.resume();
169 })
170 ..expectDone()
171 ..expectSubscription(false, false);
172 t..subscribe()
173 ..add(42)
174 ..close();
175 });
176
177 test("$p-cancel-sub-during-event", () {
178 var t = new StreamProtocolTest(broadcast);
179 t..expectSubscription(true, false)
180 ..expectData(42, () {
181 t.cancel();
182 t.subscribe();
floitsch 2013/03/01 21:52:30 ok. That's new behavior.
Lasse Reichstein Nielsen 2013/03/04 11:53:02 Depends on what you are referring to. It is new th
183 })
184 ..expectData(43)
185 ..expectDone()
186 ..expectSubscription(false, false);
187 t..subscribe()
188 ..add(42)
189 ..add(43)
190 ..close();
191 });
192
193 test("$p-cancel-sub-during-callback", () {
194 var t = new StreamProtocolTest(broadcast);
195 t..expectSubscription(true, false)
196 ..expectData(42, () {
197 t.pause();
198 })
199 ..expectPause(true, () {
200 t.cancel(); // Cancels pause
201 t.subscribe();
202 })
203 ..expectPause(false)
204 ..expectData(43)
205 ..expectDone()
206 ..expectSubscription(false, false);
207 t..subscribe()
208 ..add(42)
209 ..add(43)
210 ..close();
211 });
212
213 test("$p-sub-after-done-is-done", () {
214 var t = new StreamProtocolTest(broadcast);
215 t..expectSubscription(true, false)
216 ..expectDone()
217 ..expectSubscription(false, false)
218 ..expectDone();
219 t..subscribe()
220 ..close()
221 ..subscribe(); // Subscribe after done does not cause callbacks at all.
222 });
223 }
224
225 // --------------------------------------------------------------------
226 // Utility classes.
227
228 class StreamProtocolTest {
229 StreamController _controller;
230 StreamSubscription _subscription;
231 List<Event> _expectations = new List<Event>();
232 int _nextExpectationIndex = 0;
233 Function _onComplete;
234
235 StreamProtocolTest([bool broadcast = false]) {
236 if (broadcast) {
237 _controller = new StreamController.broadcast(
238 onPauseStateChange: _onPause,
239 onSubscriptionStateChange: _onSubcription);
240 // TODO(lrn): Make it work with multiple subscribers too.
241 } else {
242 _controller = new StreamController(
243 onPauseStateChange: _onPause,
244 onSubscriptionStateChange: _onSubcription);
245 }
246 _onComplete = expectAsync0((){
247 _onComplete = null; // Being null marks the test to be complete.
248 });
249 }
250
251 // Actions on the stream and controller.
252 void add(var data) { _controller.add(data); }
253 void error(var error) { _controller.signalError(error); }
254 void close() { _controller.close(); }
255
256 void subscribe({bool unsubscribeOnError : false}) {
257 // TODO(lrn): Handle more subscriptions (e.g., a subscription-id
258 // per subscription, and an id on event _expectations).
259 if (_subscription != null) throw new StateError("Already subscribed");
260 _subscription = _controller.stream.listen(_onData,
261 onError: _onError,
262 onDone: _onDone,
263 unsubscribeOnError:
264 unsubscribeOnError);
265 }
266
267 void pause([Future resumeSignal]) {
268 if (_subscription == null) throw new StateError("Not subscribed");
269 _subscription.pause(resumeSignal);
270 }
271
272 void resume([Future resumeSignal]) {
273 if (_subscription == null) throw new StateError("Not subscribed");
274 _subscription.resume();
275 }
276
277 void cancel() {
278 if (_subscription == null) throw new StateError("Not subscribed");
279 _subscription.cancel();
280 _subscription = null;
281 }
282
283 // Handling of stream events.
284 void _onData(var data) {
285 _withNextExpectation((Event expect) {
286 if (!expect.matchData(data)) {
287 _fail("Expected: $expect\n"
288 "Found : [Data: $data]");
289 }
290 });
291 _checkDone();
292 }
293
294 void _onError(AsyncError error) {
295 _withNextExpectation((Event expect) {
296 if (!expect.matchError(error)) {
297 _fail("Expected: $expect\n"
298 "Found : [Data: ${error.error}]");
299 }
300 });
301 _checkDone();
302 }
303
304 void _onDone() {
305 _subscription = null;
306 _withNextExpectation((Event expect) {
307 if (!expect.matchDone()) {
308 _fail("Expected: $expect\n"
309 "Found : [Done]");
310 }
311 });
312 _checkDone();
313 }
314
315 void _onPause() {
316 _withNextExpectation((Event expect) {
317 if (!expect.matchPauseChange(_controller)) {
318 _fail("Expected: $expect\n"
319 "Found : [Paused:${_controller.isPaused}]");
320 }
321 });
322 _checkDone();
323 }
324
325 void _onSubcription() {
326 _withNextExpectation((Event expect) {
327 if (!expect.matchSubscriptionChange(_controller)) {
328 _fail("Expected: $expect\n"
329 "Found: [Subscribed:${_controller.hasSubscribers}, "
330 "Paused:${_controller.isPaused}]");
331 }
332 });
333 _checkDone();
334 }
335
336 void _withNextExpectation(void action(Event expect)) {
337 if (_nextExpectationIndex == _expectations.length) {
338 action(new MismatchEvent());
339 } else {
340 Event next = _expectations[_nextExpectationIndex];
341 action(next);
342 _nextExpectationIndex++;
343 }
344 }
345
346 void _checkDone() {
347 if (_nextExpectationIndex == _expectations.length) {
348 _onComplete();
349 }
350 }
351
352
353 // Adds _expectations.
354 void expectAny([void action()]) {
355 if (_onComplete == null) {
356 _fail("Adding expectation after completing");
357 }
358 _expectations.add(new LogAnyEvent(action));
359 }
360 void expectData(var data, [void action()]) {
361 if (_onComplete == null) {
362 _fail("Adding expectation after completing");
363 }
364 _expectations.add(new DataEvent(data, action));
365 }
366 void expectError(var error, [void action()]) {
367 if (_onComplete == null) {
368 _fail("Adding expectation after completing");
369 }
370 _expectations.add(new ErrorEvent(error, action));
371 }
372 void expectDone([void action()]) {
373 if (_onComplete == null) {
374 _fail("Adding expectation after completing");
375 }
376 _expectations.add(new DoneEvent(action));
377 }
378 void expectPause(bool isPaused, [void action()]) {
379 if (_onComplete == null) {
380 _fail("Adding expectation after completing");
381 }
382 _expectations.add(new PauseCallbackEvent(isPaused, action));
383 }
384 void expectSubscription(bool hasSubscribers, bool isPaused, [void action()]) {
385 if (_onComplete == null) {
386 _fail("Adding expectation after completing");
387 }
388 _expectations.add(
389 new SubscriptionCallbackEvent(hasSubscribers, isPaused, action));
390 }
391
392 void _fail(String message) {
393 if (_nextExpectationIndex == 0) {
394 throw "Unexpected event:\n$message\nNo earlier events matched.";
395 }
396 throw "Unexpected event:\n$message\nMatched so far:\n"
397 " ${_expectations.take(_nextExpectationIndex).join("\n ")}";
398 }
399 }
400
401 class EventCollector {
402 final Queue<Event> events = new Queue<Event>();
403
404 }
405
406 class Event {
407 Function _action;
408 Event(void this._action());
409
410 bool matchData(var data) {
411 if (!_testData(data)) return false;
412 if (_action != null) _action();
413 return true;
414 }
415 bool matchError(AsyncError e) {
416 if (!_testError(e)) return false;
417 if (_action != null) _action();
418 return true;
419 }
420 bool matchDone() {
421 if (!_testDone()) return false;
422 if (_action != null) _action();
423 return true;
424 }
425 bool matchPauseChange(StreamController c) {
426 if (!_testPause(c)) return false;
427 if (_action != null) _action();
428 return true;
429 }
430 bool matchSubscriptionChange(StreamController c) {
431 if (!_testSubscribe(c)) return false;
432 if (_action != null) _action();
433 return true;
434 }
435
436 bool _testData(_) => false;
437 bool _testError(_) => false;
438 bool _testDone() => false;
439 bool _testPause(_) => false;
440 bool _testSubscribe(_) => false;
441 }
442
443 class MismatchEvent extends Event {
444 MismatchEvent() : super(null);
445 toString() => "[No event expected]";
446 }
447
448 class DataEvent extends Event {
449 final data;
450 DataEvent(this.data, void action()) : super(action);
451 bool _testData(var data) => this.data == data;
452 String toString() => "[Data: $data]";
453 }
454
455 class ErrorEvent extends Event {
456 final error;
457 ErrorEvent(this.error, void action()) : super(action);
458 bool _testError(AsyncError error) => this.error == error.error;
459 String toString() => "[Error: $error]";
460 }
461
462 class DoneEvent extends Event {
463 DoneEvent(void action()) : super(action);
464 bool _testDone() => true;
465 String toString() => "[Done]";
466 }
467
468 class PauseCallbackEvent extends Event {
469 final bool isPaused;
470 PauseCallbackEvent(this.isPaused, void action())
471 : super(action);
472 bool _testPause(StreamController c) => isPaused == c.isPaused;
473 String toString() => "[Paused:$isPaused]";
474 }
475
476 class SubscriptionCallbackEvent extends Event {
477 final bool hasSubscribers;
478 final bool isPaused;
479 SubscriptionCallbackEvent(this.hasSubscribers, this.isPaused, void action())
480 : super(action);
481 bool _testSubscribe(StreamController c) {
482 return hasSubscribers == c.hasSubscribers && isPaused == c.isPaused;
483 }
484 String toString() => "[Subscribers:$hasSubscribers, Paused:$isPaused]";
485 }
486
487
488 class LogAnyEvent extends Event {
489 String _actual = "*Not matched yet*";
490 LogAnyEvent() : super(null);
491 bool _testData(var data) {
492 _actual = "*[Data $data]";
493 return true;
494 }
495 bool _testError(AsyncError error) {
496 _actual = "*[Error ${error.error}]";
497 return true;
498 }
499 bool _testDone() {
500 _actual = "*[Done]";
501 return true;
502 }
503 bool _testPause(StreamController c) {
504 _actual = "*[Paused:${c.isPaused}]";
505 return true;
506 }
507 bool _testSubcribe(StreamController c) {
508 _actual = "*[Subscribers:${c.hasSubscribers}, Paused:${c.isPaused}]";
509 return true;
510 }
511
512 String toString() => _actual;
513 }
OLDNEW
« sdk/lib/async/stream_impl.dart ('K') | « sdk/lib/async/stream_impl.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698