Chromium Code Reviews| 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 scheduled_test.stream_matcher; | 5 library scheduled_test.stream_matcher; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import '../scheduled_stream.dart'; | 10 import '../scheduled_stream.dart'; |
| 11 import '../scheduled_test.dart'; | 11 import '../scheduled_test.dart'; |
| 12 import 'utils.dart'; | 12 import 'utils.dart'; |
| 13 | 13 |
| 14 /// An abstract superclass for matchers that validate and consume zero or more | 14 /// An abstract superclass for matchers that validate and consume zero or more |
| 15 /// values emitted by a [ScheduledStream]. | 15 /// values emitted by a [ScheduledStream]. |
| 16 /// | 16 /// |
| 17 /// [StreamMatcher]s are most commonly used by passing them to | 17 /// [StreamMatcher]s are most commonly used by passing them to |
| 18 /// [ScheduledStream.expect]. | 18 /// [ScheduledStream.expect]. |
| 19 abstract class StreamMatcher { | 19 abstract class StreamMatcher { |
| 20 /// Wrap a [Matcher], [StreamMatcher] or [Object] in a [StreamMatcher]. | 20 /// Wrap a [Matcher], [StreamMatcher] or [Object] in a [StreamMatcher]. |
| 21 /// | 21 /// |
| 22 /// If this isn't a [StreamMatcher], a [nextValue] matcher is used. | 22 /// If this isn't a [StreamMatcher], a [nextValue] matcher is used. |
| 23 factory StreamMatcher.wrap(matcher) => | 23 factory StreamMatcher.wrap(matcher) => |
| 24 matcher is StreamMatcher ? matcher : nextValue(matcher); | 24 matcher is StreamMatcher ? matcher : nextValue(matcher); |
| 25 | 25 |
| 26 StreamMatcher(); | |
|
Bob Nystrom
2014/03/12 21:12:25
Is this needed?
nweiz
2014/03/12 21:49:06
Since the matchers now extend rather than implemen
| |
| 27 | |
| 26 /// Tries to match [this] against [stream]. | 28 /// Tries to match [this] against [stream]. |
| 27 /// | 29 /// |
| 28 /// If the match succeeds, this returns `null`. If it fails, this returns a | 30 /// If the match succeeds, this returns `null`. If it fails, this returns a |
| 29 /// [Description] describing the failure. | 31 /// [Description] describing the failure. |
| 30 Future<Description> tryMatch(ScheduledStream stream); | 32 Future<Description> tryMatch(ScheduledStream stream); |
| 31 | 33 |
| 34 /// Returns whether [this] would match [stream] without actually consuming | |
| 35 /// values from [stream]. | |
| 36 /// | |
| 37 /// If the match succeeds, this returns `null`. If it fails, this returns a | |
| 38 /// [Description] describing the failure. | |
| 39 Future<Description> hasMatch(ScheduledStream stream) { | |
| 40 var fork = stream.fork(); | |
| 41 return tryMatch(fork).whenComplete(fork.close); | |
| 42 } | |
| 43 | |
| 32 String toString(); | 44 String toString(); |
| 33 } | 45 } |
| 34 | 46 |
| 35 /// A matcher that consumes and matches a single value. | 47 /// A matcher that consumes and matches a single value. |
| 36 /// | 48 /// |
| 37 /// [matcher] can be a [Matcher] or an [Object], but not a [StreamMatcher]. | 49 /// [matcher] can be a [Matcher] or an [Object], but not a [StreamMatcher]. |
| 38 StreamMatcher nextValue(matcher) => new _NextValueMatcher(matcher); | 50 StreamMatcher nextValue(matcher) => new _NextValueMatcher(matcher); |
| 39 | 51 |
| 40 /// A matcher that consumes [n] values and matches a list containing those | 52 /// A matcher that consumes [n] values and matches a list containing those |
| 41 /// objects against [matcher]. | 53 /// objects against [matcher]. |
| 42 /// | 54 /// |
| 43 /// [matcher] can be a [Matcher] or an [Object], but not a [StreamMatcher]. | 55 /// [matcher] can be a [Matcher] or an [Object], but not a [StreamMatcher]. |
| 44 StreamMatcher nextValues(int n, matcher) => new _NextValuesMatcher(n, matcher); | 56 StreamMatcher nextValues(int n, matcher) => new _NextValuesMatcher(n, matcher); |
| 45 | 57 |
| 46 /// A matcher that matches several sub-matchers in sequence. | 58 /// A matcher that matches several sub-matchers in sequence. |
| 47 /// | 59 /// |
| 48 /// Each element of [streamMatchers] can be a [StreamMatcher], a [Matcher], or | 60 /// Each element of [streamMatchers] can be a [StreamMatcher], a [Matcher], or |
| 49 /// an [Object]. | 61 /// an [Object]. |
| 50 StreamMatcher inOrder(Iterable streamMatchers) { | 62 StreamMatcher inOrder(Iterable streamMatchers) { |
| 51 streamMatchers = streamMatchers.toList(); | 63 streamMatchers = streamMatchers.toList(); |
| 52 if (streamMatchers.length == 1) { | 64 if (streamMatchers.length == 1) { |
| 53 return new StreamMatcher.wrap(streamMatchers.first); | 65 return new StreamMatcher.wrap(streamMatchers.first); |
| 54 } else { | 66 } else { |
| 55 return new _InOrderMatcher(streamMatchers); | 67 return new _InOrderMatcher(streamMatchers); |
| 56 } | 68 } |
| 57 } | 69 } |
| 58 | 70 |
| 59 /// A matcher that consumes values emitted by a stream until one matching | 71 /// A matcher that consumes values emitted by a stream until one matching |
| 60 /// [matcher] is emitted. | 72 /// [streamMatcher] is emitted. |
| 61 /// | 73 /// |
| 62 /// This will fail if the stream never emits a value that matches [matcher]. | 74 /// This will fail if the stream never emits a value that matches |
| 75 /// [streamMatcher]. | |
| 63 /// | 76 /// |
| 64 /// [matcher] can be a [Matcher] or an [Object], but not a [StreamMatcher]. | 77 /// [streamMatcher] can be a [StreamMatcher], a [Matcher] or an [Object]. |
| 65 StreamMatcher consumeThrough(matcher) => new _ConsumeThroughMatcher(matcher); | 78 StreamMatcher consumeThrough(streamMatcher) => |
| 79 new _ConsumeThroughMatcher(streamMatcher); | |
| 66 | 80 |
| 67 /// A matcher that consumes values emitted by a stream as long as they match | 81 /// A matcher that consumes values emitted by a stream as long as they match |
| 68 /// [matcher]. | 82 /// [streamMatcher]. |
| 69 /// | 83 /// |
| 70 /// This matcher will always match a stream. It exists to consume values. | 84 /// This matcher will always match a stream. It exists to consume values. If |
| 85 /// [streamMatcher] consumes more than one value, the next match will begin | |
| 86 /// after all the consumed values. | |
| 71 /// | 87 /// |
| 72 /// [matcher] can be a [Matcher] or an [Object], but not a [StreamMatcher]. | 88 /// [streamMatcher] can be a [StreamMatcher], a [Matcher] or an [Object]. |
| 73 StreamMatcher consumeWhile(matcher) => new _ConsumeWhileMatcher(matcher); | 89 StreamMatcher consumeWhile(streamMatcher) => |
| 90 new _ConsumeWhileMatcher(streamMatcher); | |
| 74 | 91 |
| 75 /// A matcher that matches either [streamMatcher1], [streamMatcher2], or both. | 92 /// A matcher that matches either [streamMatcher1], [streamMatcher2], or both. |
| 76 /// | 93 /// |
| 77 /// If both matchers match the stream, the one that consumed more values will be | 94 /// If both matchers match the stream, the one that consumed more values will be |
| 78 /// used. | 95 /// used. |
| 79 /// | 96 /// |
| 80 /// Both [streamMatcher1] and [streamMatcher2] can be a [StreamMatcher], a | 97 /// Both [streamMatcher1] and [streamMatcher2] can be a [StreamMatcher], a |
| 81 /// [Matcher], or an [Object]. | 98 /// [Matcher], or an [Object]. |
| 82 StreamMatcher either(streamMatcher1, streamMatcher2) => | 99 StreamMatcher either(streamMatcher1, streamMatcher2) => |
| 83 new _EitherMatcher(streamMatcher1, streamMatcher2); | 100 new _EitherMatcher(streamMatcher1, streamMatcher2); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 95 /// | 112 /// |
| 96 /// This will consume the remainder of a stream. | 113 /// This will consume the remainder of a stream. |
| 97 /// | 114 /// |
| 98 /// [streamMatcher] can be a [StreamMatcher], a [Matcher], or an [Object]. | 115 /// [streamMatcher] can be a [StreamMatcher], a [Matcher], or an [Object]. |
| 99 StreamMatcher never(streamMatcher) => new _NeverMatcher(streamMatcher); | 116 StreamMatcher never(streamMatcher) => new _NeverMatcher(streamMatcher); |
| 100 | 117 |
| 101 /// A matcher that matches a stream that emits no more values. | 118 /// A matcher that matches a stream that emits no more values. |
| 102 StreamMatcher get isDone => new _IsDoneMatcher(); | 119 StreamMatcher get isDone => new _IsDoneMatcher(); |
| 103 | 120 |
| 104 /// See [nextValue]. | 121 /// See [nextValue]. |
| 105 class _NextValueMatcher implements StreamMatcher { | 122 class _NextValueMatcher extends StreamMatcher { |
| 106 final Matcher _matcher; | 123 final Matcher _matcher; |
| 107 | 124 |
| 108 _NextValueMatcher(matcher) | 125 _NextValueMatcher(matcher) |
| 109 : _matcher = wrapMatcher(matcher); | 126 : _matcher = wrapMatcher(matcher); |
| 110 | 127 |
| 111 Future<Description> tryMatch(ScheduledStream stream) { | 128 Future<Description> tryMatch(ScheduledStream stream) { |
| 112 return stream.hasNext.then((hasNext) { | 129 return stream.hasNext.then((hasNext) { |
| 113 if (!hasNext) { | 130 if (!hasNext) { |
| 114 return new StringDescription("unexpected end of stream"); | 131 return new StringDescription("unexpected end of stream"); |
| 115 } | 132 } |
| 116 return stream.next().then((value) { | 133 return stream.next().then((value) { |
| 117 var matchState = {}; | 134 var matchState = {}; |
| 118 if (_matcher.matches(value, matchState)) return null; | 135 if (_matcher.matches(value, matchState)) return null; |
| 119 return _matcher.describeMismatch(value, new StringDescription(), | 136 return _matcher.describeMismatch(value, new StringDescription(), |
| 120 matchState, false); | 137 matchState, false); |
| 121 }); | 138 }); |
| 122 }); | 139 }); |
| 123 } | 140 } |
| 124 | 141 |
| 125 String toString() => _matcher.describe(new StringDescription()).toString(); | 142 String toString() => _matcher.describe(new StringDescription()).toString(); |
| 126 } | 143 } |
| 127 | 144 |
| 128 /// See [nextValues]. | 145 /// See [nextValues]. |
| 129 class _NextValuesMatcher implements StreamMatcher { | 146 class _NextValuesMatcher extends StreamMatcher { |
| 130 final int _n; | 147 final int _n; |
| 131 final Matcher _matcher; | 148 final Matcher _matcher; |
| 132 | 149 |
| 133 _NextValuesMatcher(this._n, matcher) | 150 _NextValuesMatcher(this._n, matcher) |
| 134 : _matcher = wrapMatcher(matcher); | 151 : _matcher = wrapMatcher(matcher); |
| 135 | 152 |
| 136 Future<Description> tryMatch(ScheduledStream stream) { | 153 Future<Description> tryMatch(ScheduledStream stream) { |
| 137 var collectedValues = []; | 154 var collectedValues = []; |
| 138 collectValues(count) { | 155 collectValues(count) { |
| 139 if (count == 0) return null; | 156 if (count == 0) return null; |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 158 } | 175 } |
| 159 | 176 |
| 160 String toString() { | 177 String toString() { |
| 161 return new StringDescription('$_n values that ') | 178 return new StringDescription('$_n values that ') |
| 162 .addDescriptionOf(_matcher) | 179 .addDescriptionOf(_matcher) |
| 163 .toString(); | 180 .toString(); |
| 164 } | 181 } |
| 165 } | 182 } |
| 166 | 183 |
| 167 /// See [inOrder]. | 184 /// See [inOrder]. |
| 168 class _InOrderMatcher implements StreamMatcher { | 185 class _InOrderMatcher extends StreamMatcher { |
| 169 final List<StreamMatcher> _matchers; | 186 final List<StreamMatcher> _matchers; |
| 170 | 187 |
| 171 _InOrderMatcher(Iterable streamMatchers) | 188 _InOrderMatcher(Iterable streamMatchers) |
| 172 : _matchers = streamMatchers.map((matcher) => | 189 : _matchers = streamMatchers.map((matcher) => |
| 173 new StreamMatcher.wrap(matcher)).toList(); | 190 new StreamMatcher.wrap(matcher)).toList(); |
| 174 | 191 |
| 175 Future<Description> tryMatch(ScheduledStream stream) { | 192 Future<Description> tryMatch(ScheduledStream stream) { |
| 176 var matchers = new Queue.from(_matchers); | 193 var matchers = new Queue.from(_matchers); |
| 177 | 194 |
| 178 matchNext() { | 195 matchNext() { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 189 | 206 |
| 190 return matchNext(); | 207 return matchNext(); |
| 191 } | 208 } |
| 192 | 209 |
| 193 String toString() => _matchers | 210 String toString() => _matchers |
| 194 .map((matcher) => prefixLines(matcher.toString(), firstPrefix: '* ')) | 211 .map((matcher) => prefixLines(matcher.toString(), firstPrefix: '* ')) |
| 195 .join('\n'); | 212 .join('\n'); |
| 196 } | 213 } |
| 197 | 214 |
| 198 /// See [consumeThrough]. | 215 /// See [consumeThrough]. |
| 199 class _ConsumeThroughMatcher implements StreamMatcher { | 216 class _ConsumeThroughMatcher extends StreamMatcher { |
| 200 final Matcher _matcher; | 217 final StreamMatcher _matcher; |
| 201 | 218 |
| 202 _ConsumeThroughMatcher(matcher) | 219 _ConsumeThroughMatcher(streamMatcher) |
| 203 : _matcher = wrapMatcher(matcher); | 220 : _matcher = new StreamMatcher.wrap(streamMatcher); |
| 204 | 221 |
| 205 Future<Description> tryMatch(ScheduledStream stream) { | 222 Future<Description> tryMatch(ScheduledStream stream) { |
| 206 consumeNext() { | 223 consumeNext() { |
| 207 return stream.hasNext.then((hasNext) { | 224 return stream.hasNext.then((hasNext) { |
| 208 if (!hasNext) return new StringDescription("unexpected end of stream"); | 225 if (!hasNext) return new StringDescription("unexpected end of stream"); |
| 209 | 226 |
| 210 return stream.next().then((value) { | 227 return _matcher.hasMatch(stream).then((failure) { |
| 211 if (_matcher.matches(value, {})) return null; | 228 if (failure != null) return stream.next().then((_) => consumeNext()); |
| 212 return consumeNext(); | 229 return _matcher.tryMatch(stream); |
| 213 }); | 230 }); |
| 214 }); | 231 }); |
| 215 } | 232 } |
| 216 | 233 |
| 217 return consumeNext(); | 234 return consumeNext(); |
| 218 } | 235 } |
| 219 | 236 |
| 220 String toString() { | 237 String toString() { |
| 221 return new StringDescription('values followed by ') | 238 var matcherString = _matcher.toString(); |
| 222 .addDescriptionOf(_matcher).toString(); | 239 if (matcherString.contains("\n")) { |
| 240 return 'values followed by:\n' + prefixLines(matcherString, prefix: ' '); | |
| 241 } else { | |
| 242 return 'values followed by $matcherString'; | |
| 243 } | |
| 223 } | 244 } |
| 224 } | 245 } |
| 225 | 246 |
| 226 /// See [consumeWhile]. | 247 /// See [consumeWhile]. |
| 227 class _ConsumeWhileMatcher implements StreamMatcher { | 248 class _ConsumeWhileMatcher extends StreamMatcher { |
| 228 final Matcher _matcher; | 249 final StreamMatcher _matcher; |
| 229 | 250 |
| 230 _ConsumeWhileMatcher(matcher) | 251 _ConsumeWhileMatcher(matcher) |
| 231 : _matcher = wrapMatcher(matcher); | 252 : _matcher = new StreamMatcher.wrap(matcher); |
| 232 | 253 |
| 233 Future<Description> tryMatch(ScheduledStream stream) { | 254 Future<Description> tryMatch(ScheduledStream stream) { |
| 234 consumeNext() { | 255 consumeNext() { |
| 235 return stream.hasNext.then((hasNext) { | 256 return stream.hasNext.then((hasNext) { |
| 236 if (!hasNext) return new Future.value(); | 257 if (!hasNext) return new Future.value(); |
| 237 | 258 |
| 238 return _peek(stream).then((value) { | 259 return _matcher.hasMatch(stream).then((failure) { |
| 239 if (!_matcher.matches(value, {})) return null; | 260 if (failure != null) return new Future.value(); |
| 240 return stream.next().then((_) => consumeNext()); | 261 return _matcher.tryMatch(stream).then((_) => consumeNext()); |
| 241 }); | 262 }); |
| 242 }); | 263 }); |
| 243 } | 264 } |
| 244 | 265 |
| 245 return consumeNext(); | 266 return consumeNext(); |
| 246 } | 267 } |
| 247 | 268 |
| 248 String toString() { | 269 String toString() { |
| 249 return new StringDescription('any number of ') | 270 var matcherString = _matcher.toString(); |
| 250 .addDescriptionOf(_matcher).toString(); | 271 if (matcherString.contains("\n")) { |
| 272 return 'any number of\n' + prefixLines(matcherString, prefix: ' '); | |
| 273 } else { | |
| 274 return 'any number of $matcherString'; | |
| 275 } | |
| 251 } | 276 } |
| 252 } | 277 } |
| 253 | 278 |
| 254 /// See [either]. | 279 /// See [either]. |
| 255 class _EitherMatcher implements StreamMatcher { | 280 class _EitherMatcher extends StreamMatcher { |
| 256 final StreamMatcher _matcher1; | 281 final StreamMatcher _matcher1; |
| 257 final StreamMatcher _matcher2; | 282 final StreamMatcher _matcher2; |
| 258 | 283 |
| 259 _EitherMatcher(streamMatcher1, streamMatcher2) | 284 _EitherMatcher(streamMatcher1, streamMatcher2) |
| 260 : _matcher1 = new StreamMatcher.wrap(streamMatcher1), | 285 : _matcher1 = new StreamMatcher.wrap(streamMatcher1), |
| 261 _matcher2 = new StreamMatcher.wrap(streamMatcher2); | 286 _matcher2 = new StreamMatcher.wrap(streamMatcher2); |
| 262 | 287 |
| 263 Future<Description> tryMatch(ScheduledStream stream) { | 288 Future<Description> tryMatch(ScheduledStream stream) { |
| 264 var stream1 = stream.fork(); | 289 var stream1 = stream.fork(); |
| 265 var stream2 = stream.fork(); | 290 var stream2 = stream.fork(); |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 295 String toString() { | 320 String toString() { |
| 296 return new StringDescription('either\n') | 321 return new StringDescription('either\n') |
| 297 .add(prefixLines(_matcher1.toString(), prefix: ' ')) | 322 .add(prefixLines(_matcher1.toString(), prefix: ' ')) |
| 298 .add('\nor\n') | 323 .add('\nor\n') |
| 299 .add(prefixLines(_matcher2.toString(), prefix: ' ')) | 324 .add(prefixLines(_matcher2.toString(), prefix: ' ')) |
| 300 .toString(); | 325 .toString(); |
| 301 } | 326 } |
| 302 } | 327 } |
| 303 | 328 |
| 304 /// See [allow]. | 329 /// See [allow]. |
| 305 class _AllowMatcher implements StreamMatcher { | 330 class _AllowMatcher extends StreamMatcher { |
| 306 final StreamMatcher _matcher; | 331 final StreamMatcher _matcher; |
| 307 | 332 |
| 308 _AllowMatcher(streamMatcher) | 333 _AllowMatcher(streamMatcher) |
| 309 : _matcher = new StreamMatcher.wrap(streamMatcher); | 334 : _matcher = new StreamMatcher.wrap(streamMatcher); |
| 310 | 335 |
| 311 Future<Description> tryMatch(ScheduledStream stream) { | 336 Future<Description> tryMatch(ScheduledStream stream) { |
| 312 var fork = stream.fork(); | 337 return _matcher.hasMatch(stream).then((failure) { |
| 313 return _matcher.tryMatch(fork).whenComplete(fork.close).then((failure) { | |
| 314 if (failure != null) return null; | 338 if (failure != null) return null; |
| 315 return _matcher.tryMatch(stream); | 339 return _matcher.tryMatch(stream); |
| 316 }); | 340 }); |
| 317 } | 341 } |
| 318 | 342 |
| 319 String toString() { | 343 String toString() { |
| 320 return new StringDescription('allow\n') | 344 return new StringDescription('allow\n') |
| 321 .add(prefixLines(_matcher.toString())) | 345 .add(prefixLines(_matcher.toString())) |
| 322 .toString(); | 346 .toString(); |
| 323 } | 347 } |
| 324 } | 348 } |
| 325 | 349 |
| 326 /// See [never]. | 350 /// See [never]. |
| 327 class _NeverMatcher implements StreamMatcher { | 351 class _NeverMatcher extends StreamMatcher { |
| 328 final StreamMatcher _matcher; | 352 final StreamMatcher _matcher; |
| 329 | 353 |
| 330 _NeverMatcher(streamMatcher) | 354 _NeverMatcher(streamMatcher) |
| 331 : _matcher = new StreamMatcher.wrap(streamMatcher); | 355 : _matcher = new StreamMatcher.wrap(streamMatcher); |
| 332 | 356 |
| 333 Future<Description> tryMatch(ScheduledStream stream) { | 357 Future<Description> tryMatch(ScheduledStream stream) { |
| 334 consumeNext() { | 358 consumeNext() { |
| 335 return stream.hasNext.then((hasNext) { | 359 return stream.hasNext.then((hasNext) { |
| 336 if (!hasNext) return new Future.value(); | 360 if (!hasNext) return new Future.value(); |
| 337 | 361 |
| 338 var fork = stream.fork(); | 362 return _matcher.hasMatch(stream).then((failure) { |
| 339 return _matcher.tryMatch(fork).whenComplete(fork.close) | |
| 340 .then((failure) { | |
| 341 if (failure != null) { | 363 if (failure != null) { |
| 342 return stream.next().then((_) => consumeNext()); | 364 return stream.next().then((_) => consumeNext()); |
| 343 } | 365 } |
| 344 | 366 |
| 345 return new StringDescription("matched\n") | 367 return new StringDescription("matched\n") |
| 346 .add(prefixLines(_matcher.toString(), prefix: ' ')); | 368 .add(prefixLines(_matcher.toString(), prefix: ' ')); |
| 347 }); | 369 }); |
| 348 }); | 370 }); |
| 349 } | 371 } |
| 350 | 372 |
| 351 return consumeNext(); | 373 return consumeNext(); |
| 352 } | 374 } |
| 353 | 375 |
| 354 String toString() => | 376 String toString() => |
| 355 'never\n${prefixLines(_matcher.toString(), prefix: ' ')}'; | 377 'never\n${prefixLines(_matcher.toString(), prefix: ' ')}'; |
| 356 } | 378 } |
| 357 | 379 |
| 358 /// See [isDone]. | 380 /// See [isDone]. |
| 359 class _IsDoneMatcher implements StreamMatcher { | 381 class _IsDoneMatcher extends StreamMatcher { |
| 360 _IsDoneMatcher(); | 382 _IsDoneMatcher(); |
| 361 | 383 |
| 362 Future<Description> tryMatch(ScheduledStream stream) { | 384 Future<Description> tryMatch(ScheduledStream stream) { |
| 363 return stream.hasNext.then((hasNext) { | 385 return stream.hasNext.then((hasNext) { |
| 364 if (!hasNext) return null; | 386 if (!hasNext) return null; |
| 365 return new StringDescription("stream wasn't finished"); | 387 return new StringDescription("stream wasn't finished"); |
| 366 }); | 388 }); |
| 367 } | 389 } |
| 368 | 390 |
| 369 String toString() => 'is done'; | 391 String toString() => 'is done'; |
| 370 } | 392 } |
| 371 | 393 |
| 372 /// Returns a [Future] that completes to the next value emitted by [stream] | 394 /// Returns a [Future] that completes to the next value emitted by [stream] |
| 373 /// without actually consuming that value. | 395 /// without actually consuming that value. |
| 374 Future _peek(ScheduledStream stream) { | 396 Future _peek(ScheduledStream stream) { |
| 375 var fork = stream.fork(); | 397 var fork = stream.fork(); |
| 376 return fork.next().whenComplete(fork.close); | 398 return fork.next().whenComplete(fork.close); |
| 377 } | 399 } |
| OLD | NEW |