@@ -34,6 +34,7 @@ void main() {
3434 int ? firstExitCode;
3535 late MemoryFileSystem fileSystem;
3636 late FakeAnalytics fakeAnalytics;
37+ late FakeStdio fakeStdio;
3738
3839 setUp (() {
3940 // Instead of exiting with dart:io exit(), this causes an exception to
@@ -58,6 +59,7 @@ void main() {
5859 fs: fileSystem,
5960 fakeFlutterVersion: FakeFlutterVersion (),
6061 );
62+ fakeStdio = FakeStdio ();
6163 });
6264
6365 tearDown (() {
@@ -120,6 +122,66 @@ void main() {
120122 },
121123 );
122124
125+ testUsingContext (
126+ 'error handling crash report (bot)' ,
127+ () async {
128+ final Completer <void > completer = Completer <void >();
129+ // runner.run() asynchronously calls the exit function set above, so we
130+ // catch it in a zone.
131+ unawaited (
132+ runZonedGuarded <Future <void >?>(
133+ () {
134+ unawaited (
135+ runner.run (
136+ < String > ['crash' ],
137+ () => < FlutterCommand > [CrashingFlutterCommand ()],
138+ // This flutterVersion disables crash reporting.
139+ flutterVersion: '[user-branch]/' ,
140+ shutdownHooks: ShutdownHooks (),
141+ ),
142+ );
143+ return null ;
144+ },
145+ (Object error, StackTrace stack) {
146+ expect (firstExitCode, isNotNull);
147+ expect (firstExitCode, isNot (0 ));
148+ expect (error.toString (), 'Exception: test exit' );
149+ completer.complete ();
150+ },
151+ ),
152+ );
153+ await completer.future;
154+
155+ expect (
156+ fakeAnalytics.sentEvents,
157+ isNot (contains (Event .exception (exception: '_Exception' ))),
158+ reason: 'Does not send a report on a bot' ,
159+ );
160+
161+ expect (
162+ fakeStdio.writtenToStderr,
163+ contains (contains ('Feature flags enabled:' )),
164+ reason: 'Should emit feature flags (ignore specifics for test stability)' ,
165+ );
166+ },
167+ overrides: < Type , Generator > {
168+ Platform :
169+ () => FakePlatform (
170+ environment: < String , String > {
171+ 'FLUTTER_ANALYTICS_LOG_FILE' : 'test' ,
172+ 'FLUTTER_ROOT' : '/' ,
173+ },
174+ ),
175+ FileSystem : () => fileSystem,
176+ ProcessManager : () => FakeProcessManager .any (),
177+ Artifacts : () => Artifacts .test (),
178+ HttpClientFactory : () => () => FakeHttpClient .any (),
179+ Analytics : () => fakeAnalytics,
180+ BotDetector : () => const FakeBotDetector (true ),
181+ io.Stdio : () => fakeStdio,
182+ },
183+ );
184+
123185 // This Completer completes when CrashingFlutterCommand.runCommand
124186 // completes, but ideally we'd want it to complete when execution resumes
125187 // runner.run. Currently the distinction does not matter, but if it ever
@@ -282,6 +344,7 @@ void main() {
282344 fileSystem.currentDirectory = currentDirectory;
283345 inTestSetup = false ;
284346 });
347+
285348 testUsingContext (
286349 'create local report in temporary directory' ,
287350 () async {
0 commit comments