Repository: flutter
Issue: `flutter test` stack traces are truncated when `pumpAndSettle` timeouts
Labels: a: tests,framework,has reproducible steps,P2,found in release: 2.3,team-framework,triaged-framework

Unless otherwise specified, the following examples are executed with `flutter
test`, which has `--chain-stack-traces` [enabled](https://github.com/flutter/flutter/blob/757c3add1470b54fbc7dab8993dc14c7163fde13/packages/flutter_tools/lib/src/test/runner.dart#L127).

(There may be other instances of this problem besides `pumpAndSettle`)

Consider the following:

```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';

void main() {
  testWidgets('foo', (tester) async {
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        // Infinite animation so [pumpAndSettle] will timeout.
        child: LinearProgressIndicator(),
      ),
    );
    await tester.pumpAndSettle();
  });
}
```

Because a `LinearProgressIndicator` that animates infinitely, `pumpAndSettle` will never terminate. The following is the stack trace for the timeout:

```
The following assertion was thrown running a test:
pumpAndSettle timed out

When the exception was thrown, this was the stack:
#0      WidgetTester.pumpAndSettle.<anonymous closure> (package:flutter_test/src/widget_tester.dart:650:11)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
```

Observe that the current file is not referenced in this stack trace, which is problematic because it is difficult for users to find the line where the timeout happens.

## More Minimal Repro

The following is a more minimal repro that distils a particular pattern in `TestAsyncUtils.guard`, [used by](https://github.com/flutter/flutter/blob/757c3add1470b54fbc7dab8993dc14c7163fde13/packages/flutter_test/lib/src/widget_tester.dart#L645) `pumpAndSettle` and many other test methods under the hood causes the problem.

```dart
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('repro', (tester) async {
    await problem()
        .then((x) => x, onError: (error, stack) => Future.error(error, stack));
  });
}

Future<void> problem() async {
  await null;
  throw Exception('Some error');
}
```

Error:

```
The following _Exception was thrown running a test:
Exception: Some error

When the exception was thrown, this was the stack:
#0      problem (file:///Users/jiahaog/dev/hello_flutter/test/widget_test.dart:12:3)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
```

Again, we see that L5 does not appear in the stack trace.

This is the same pattern appearing in `TestAsyncUtils.guard`:

https://github.com/flutter/flutter/blob/757c3add1470b54fbc7dab8993dc14c7163fde13/packages/flutter_test/lib/src/test_async_utils.dart#L114-L120

## Possible Fix

`--chain-stack-traces` is enabled Flutter open source users, but is not enabled internally. It is also not enabled in raw-Dart projects using `package:test` (relevant https://github.com/dart-lang/test/issues/1505).

This proposed fix only works when `--chain-stack-traces` is **disabled**.

Instead of using raw futures, the `TestAsyncUtils.guard` can be changed to use try-catches.

```dart
try {
  resultValue = await result;
  return completionHandler(null, null);
} catch (error, stackTrace) {
  return completionHandler(error, stackTrace);
}
```

Draft PR: #84955

To fix the minimal repro above, we would do the following instead:

```dart
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('minimal repro fixed', (tester) async {
    try {
      return await problem();
    } catch (error, stack) {
      return Future.error(error, stack);
    }
  });
}

Future<void> problem() async {
  await null;
  throw Exception('Some error');
}
```

This may be a deeper bug in `package:stack_trace`. 

@natebosch - do you know if there are any plans to stop using `--chain-stack-traces` for `flutter test`? If so, we may be able to move forward with this proposed fix.

## Alternative Fix

Again, this only works when `--chain-stack-traces` is **disabled**.

`TestAsyncUtils.guard` was added a while ago and may already be superseded by the [`unawaited_futures`](https://dart-lang.github.io/linter/lints/unawaited_futures.html) lint, though this is not enabled by default for new Flutter projects.