-
Notifications
You must be signed in to change notification settings - Fork 87
Fix exception for combined event on canceled sub #2345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -167,6 +167,26 @@ void main() { | |
| expect(emittedValues, [3, 5]); | ||
| }); | ||
| }); | ||
|
|
||
| test('handles combined results after stream is canceled', () async { | ||
| final source = Stream.value(1); | ||
| final other = Stream.value(2); | ||
| late final Completer<void> combineDelay; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could just create the completer here? It's only set once. Or make the variable nullable, so you could use it to check whether the |
||
| Future<int> delayedSum(int a, int b) async { | ||
| combineDelay = Completer<void>(); | ||
| await combineDelay.future; | ||
| return a + b; | ||
| } | ||
|
|
||
| final subscription = source | ||
| .combineLatest(other, delayedSum) | ||
| .listen(expectAsync1((_) {}, count: 0)); | ||
|
|
||
| await pumpEventQueue(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I see that there is nothing to synchronize on in the code that runs after the The first pump should be possibler to handle with a synchronization future, like you do Consider (or ignore, the test is fine if you're not unreasonably paranoid about var stream1 = StreamController<int>();
var stream2 = StreamController<int>();
final onDelayedSum = Completer<void>();
final delayedSumContinue = Completer<void>();
Future<int> delayedSum(int a, int b) async {
onDelayedSum.complete(null);
await delayedSumContinue.future;
return a + b;
}
final combinedStream = stream1.stream.combineLatest(stream2.stream);
expect(stream1.hasListener, false);
expect(stream2.hasListener, false);
final subscription = combinedStream.listen(expectAsync1((_) {}, count: 0));
expect(stream1.hasListener, true);
expect(stream2.hasListener, true);
expect(stream1.isPaused, false);
expect(stream2.isPaused, false);
stream1.add(1);
stream2.add(2);
await onDelayedSum.future;
expect(stream1.hasListener, true);
expect(stream2.hasListener, true);
expect(stream1.isPaused, true);
expect(stream2.isPaused, true);
await subscription.cancel();
expect(stream1.hasListener, false);
expect(stream2.hasListener, false);
delayedSumContinue.complete(null);
// Should not cause error.
// Do we need to pump-and-wait here?
// Any later error should be ascribed to the test no matter what.
// Is it because the test would considered complete before the error would happen?
// (If so, it'll still be an errror, it'll just be an error-after-test-completed. The program
// won't end before all code has run.)
// If so:
await pumpEventQueue();
// Or my preference, since it doesn't arbitrarily do 20 schedule microtasks,
// but actually waits for all microtasks. It does allow other non-microtask events
// to run first, but this is a controlled environment,
// we know there shouldn't be any (that we care about).
await Future<void>.delayed(Duration.zero); // Skips all microtasks. |
||
| unawaited(subscription.cancel()); | ||
| combineDelay.complete(); | ||
| await pumpEventQueue(); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Could we put it all into the
.then?Mainly because
whenCompletecan be more expensive than it looks, because of the way it has to propagete the prior result conditionally. And in any case,.then+.whenCompleteis more work, and more closures, than just a.then.Can probably use
?.instad of!.everywhere. I expect it to generate marginally smaller code to not have a throwing branch, even if it's just calling a common function, but I haven't checked. The!is better documentation, though.)