sdk icon indicating copy to clipboard operation
sdk copied to clipboard

Test failed after update flutter on 3.3.1 (dart 2.18.0)

Open karabanovbs opened this issue 3 years ago • 7 comments

Original issue

import 'dart:async';

import 'package:flutter_test/flutter_test.dart';

void main() {
  test('test case', () async {
    await expectLater(
      // create stream that emit Completer, and wait it
      ((() async* {
        try {
          final completer = Completer<void>();
          yield completer;
          await completer.future;
        } catch (_) {
          // handle error
        }
      })())
          .map((event) {
        // add error to completer
        event.completeError(Exception());

        return event;
      }),
      emitsThrough(anything),
    );
  });
}

with 3.0.5 results:

flutter doctor -v
[✓] Flutter (Channel stable, 3.0.5, on macOS 12.2.1 21D62 darwin-x64, locale ru-RU)
    • Flutter version 3.0.5 at /Users/boriskarabanov/dev/sdk/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f1875d570e (8 weeks ago), 2022-07-13 11:24:16 -0700
    • Engine revision e85ea0e79c
    • Dart version 2.17.6
    • DevTools version 2.12.2

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc1)
    • Android SDK at /Users/boriskarabanov/Library/Android/sdk
    • Platform android-33, build-tools 33.0.0-rc1
    • ANDROID_HOME = /Users/boriskarabanov/Library/Android/sdk
    • ANDROID_SDK_ROOT = /Users/boriskarabanov/Library/Android/sdk
    • Java binary at: /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8815526/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.2)
    • Android Studio at /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8815526/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] Android Studio (version 2021.2)
    • Android Studio at /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8609683/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] IntelliJ IDEA Ultimate Edition (version 2022.2.1)
    • IntelliJ at /Users/boriskarabanov/Applications/JetBrains Toolbox/IntelliJ IDEA Ultimate.app
    • Flutter plugin version 69.0.5
    • Dart plugin version 222.3739.24

[✓] IntelliJ IDEA Ultimate Edition (version 2022.2.1)
    • IntelliJ at /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/222.3739.54/IntelliJ IDEA.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] IntelliJ IDEA Ultimate Edition (version 2022.2)
    • IntelliJ at /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/222.3345.118/IntelliJ IDEA.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] VS Code (version 1.69.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.46.0

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-x64     • macOS 12.2.1 21D62 darwin-x64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 105.0.5195.102
    ! Error: DNS s4502 has recently restarted. Xcode will continue when DNS s4502 is unlocked. (code -14)

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

00:08 +1: All tests passed!   

with 3.3.1 results:

flutter doctor -v
[✓] Flutter (Channel stable, 3.3.1, on macOS 12.2.1 21D62 darwin-x64, locale ru-RU)
    • Flutter version 3.3.1 on channel stable at /Users/boriskarabanov/dev/sdk/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 4f9d92fbbd (34 hours ago), 2022-09-06 17:54:53 -0700
    • Engine revision 3efdf03e73
    • Dart version 2.18.0
    • DevTools version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc1)
    • Android SDK at /Users/boriskarabanov/Library/Android/sdk
    • Platform android-33, build-tools 33.0.0-rc1
    • ANDROID_HOME = /Users/boriskarabanov/Library/Android/sdk
    • ANDROID_SDK_ROOT = /Users/boriskarabanov/Library/Android/sdk
    • Java binary at: /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8815526/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 13F100
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.2)
    • Android Studio at /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8815526/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] Android Studio (version 2021.2)
    • Android Studio at /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8609683/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] IntelliJ IDEA Ultimate Edition (version 2022.2.1)
    • IntelliJ at /Users/boriskarabanov/Applications/JetBrains Toolbox/IntelliJ IDEA Ultimate.app
    • Flutter plugin version 69.0.5
    • Dart plugin version 222.3739.24

[✓] IntelliJ IDEA Ultimate Edition (version 2022.2.1)
    • IntelliJ at /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/222.3739.54/IntelliJ IDEA.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] IntelliJ IDEA Ultimate Edition (version 2022.2)
    • IntelliJ at /Users/boriskarabanov/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/222.3345.118/IntelliJ IDEA.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] VS Code (version 1.69.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.46.0

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-x64     • macOS 12.2.1 21D62 darwin-x64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 105.0.5195.102
    ! Error: DNS s4502 has recently restarted. Xcode will continue when DNS s4502 is unlocked. (code -14)

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!
00:09 +0 -1: test case [E]                                                                                                                                                          
  Exception
  dart:async                                          _Completer.completeError
  test/tst.dart 20:15                                 main.<fn>.<fn>
  dart:async                                          _AsyncStarStreamController.add
  test/tst.dart                                       main.<fn>.<fn>
  ===== asynchronous gap ===========================
  dart:async                                          _ForwardingStream.listen
  package:async/src/stream_queue.dart 475:31          StreamQueue._ensureListening
  package:async/src/stream_queue.dart 531:7           StreamQueue._addRequest
  package:async/src/stream_queue.dart 291:5           StreamQueue.startTransaction
  package:test_api                                    expectLater
  package:flutter_test/src/widget_tester.dart 497:23  expectLater
  test/tst.dart 7:11   

karabanovbs avatar Sep 09 '22 07:09 karabanovbs

same on pure dart

import 'dart:async';

void main(List<String> arguments) {
  // create stream that emit Completer, and wait it
  ((() async* {
    try {
      final completer = Completer<void>();
      yield completer;
      await completer.future;
    } catch (_) {
      // handle error
    }
  })())
      .map((event) {
    // add error to completer
    event.completeError(Exception());

    return event;
  }).listen((_) => print('done'));

}

on Dart version 2.17.6

done

on Dart version 2.18.0

done
Unhandled exception:
Exception

karabanovbs avatar Sep 09 '22 09:09 karabanovbs

/cc @alexmarkov likely result of async changes

mraleph avatar Sep 09 '22 09:09 mraleph

Bisected this change in behavior to https://github.com/dart-lang/sdk/commit/efdffab8b77f5ca21ee08e2b7bb8b05a312a1317. /cc @mkustermann @lrhn

Before that change, execution of async* closure continued up to await completer.future and the exception from event.completeError(Exception()) was thrown and caught in catch (_), so it was not printed as an unhandled exception.

After that change, async* generator no longer runs past yield completer, so completer.future is not awaited and the error is printed as an unhandled exception, similarly to the following code:

import 'dart:async';

void main(List<String> args) {
  final completer = Completer<void>();
  completer.completeError(Exception());
}

The new behavior looks correct to me - async* generator should not continue running until the generated event is delivered.

alexmarkov avatar Sep 09 '22 16:09 alexmarkov

@alexmarkov so, how should i handle this error now?

karabanovbs avatar Sep 09 '22 17:09 karabanovbs

Something like this looks strange.

import 'dart:async';

void main(List<String> arguments) {
  // create stream that emit Completer, and wait it
  ((() async* {
    try {
      final completer = Completer<void>();
      yield completer;
      await completer.future;
    } catch (e) {
      // handle error
      print(e);
    }
  })())
      .map((event) {
    // add error to completer
    event.completeError(Exception());
    // looks strange
    event.future.onError((error, stackTrace) => null);
    return event;
  }).listen((_) => print('done'));
}

karabanovbs avatar Sep 09 '22 22:09 karabanovbs

So, now I can see different behavior on the web and on android/ios. When i run this code on android i get an unhandled exception. When I ran it on the web I didn't get an exception.

Is it ok? I prefer see same behavior on each platforms.))

karabanovbs avatar Sep 12 '22 09:09 karabanovbs

The error doesn't necessarily have ti be uncaught, because it's valid behavior for the async* method to continue running immediately after synchronously delivering the event. In that case, the await on the future should happen before the future completes. It's also valid behavior to pause the async* method and continue in a later microtask. In that case, the future might complete with its exception before the await, and that seems to be what happens here.

Either is valid, we do not promise how unrelated asynchronous events are interleaved, and you should be coding defensively when ordering matters.

The solution I'd use is to make the future not to cause unhandled errors if it completes with an error. I'd do that by adding:

  completer.future.ignore();

before the yield. (That's effectively what event.future.onError((_, __) => null) does too, just more verbosely and less efficiently. I'd put it in the async* function instead, because that's the place where the delayed await happens, so it is in a position to know that it might not start listening in time.)

lrhn avatar Sep 12 '22 12:09 lrhn

Closing issue as 'works as intended' based on https://github.com/dart-lang/sdk/issues/49932#issuecomment-1242211731

a-siva avatar Sep 18 '23 18:09 a-siva