From 54b7edf25c3703477aab892611ef9856c5bbef54 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Thu, 11 Sep 2025 13:39:11 +0000 Subject: [PATCH 01/13] fix(crashlytics, iOS): reorder error reason logging to match Android implementation --- .../Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m index 79cf8414b416..6e2a51da06f6 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m +++ b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m @@ -126,10 +126,10 @@ - (void)recordError:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallRes } if (![reason isEqual:[NSNull null]]) { - reason = [NSString stringWithFormat:@"%@. Error thrown %@.", dartExceptionMessage, reason]; // Log additional custom value to match Android. [[FIRCrashlytics crashlytics] setCustomValue:[NSString stringWithFormat:@"thrown %@", reason] forKey:@"flutter_error_reason"]; + reason = [NSString stringWithFormat:@"%@. Error thrown %@.", dartExceptionMessage, reason]; } else { reason = dartExceptionMessage; } From 9f4ef22dab9ceb3c79c8fbfc62509fc50df4889c Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 12:35:53 +0000 Subject: [PATCH 02/13] feat(crashlytics): add test event channel and CI detection for error reasons --- .../FlutterFirebaseCrashlyticsPlugin.java | 26 ++++++++++++++-- .../FLTFirebaseCrashlyticsPlugin.m | 31 +++++++++++++++++++ .../firebase_crashlytics_e2e_test.dart | 25 +++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java index e02fe5f6a1a3..88b4fc6e726e 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java +++ b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java @@ -21,6 +21,7 @@ import com.google.firebase.crashlytics.internal.Logger; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; @@ -34,9 +35,11 @@ /** FlutterFirebaseCrashlyticsPlugin */ public class FlutterFirebaseCrashlyticsPlugin - implements FlutterFirebasePlugin, FlutterPlugin, MethodCallHandler { + implements FlutterFirebasePlugin, FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler { public static final String TAG = "FLTFirebaseCrashlytics"; private MethodChannel channel; + private EventChannel testEventChannel; + private EventChannel.EventSink testEventSink; private static final String FIREBASE_CRASHLYTICS_COLLECTION_ENABLED = "firebase_crashlytics_collection_enabled"; @@ -61,6 +64,11 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { } } + private boolean isRunningInCI() { + Map env = System.getenv(); + return env.containsKey("GITHUB_ACTIONS"); + } + private Task> checkForUnsentReports() { TaskCompletionSource> taskCompletionSource = new TaskCompletionSource<>(); @@ -160,8 +168,12 @@ private Task recordError(final Map arguments) { Exception exception; if (reason != null) { + final String crashlyticsErrorReason = "thrown" + reason; + if (isRunningInCI() && testEventSink != null) { + testEventSink.success(crashlyticsErrorReason); + } // Set a "reason" (to match iOS) to show where the exception was thrown. - crashlytics.setCustomKey(Constants.FLUTTER_ERROR_REASON, "thrown " + reason); + crashlytics.setCustomKey(Constants.FLUTTER_ERROR_REASON, crashlyticsErrorReason); exception = new FlutterError(dartExceptionMessage + ". " + "Error thrown " + reason + "."); } else { @@ -466,4 +478,14 @@ public Task didReinitializeFirebaseCore() { return taskCompletionSource.getTask(); } + + @Override + public void onListen(Object arguments, EventChannel.EventSink events) { + testEventSink = events; + } + + @Override + public void onCancel(Object arguments) { + testEventSink = null; + } } diff --git a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m index 6e2a51da06f6..56e6225fb4b8 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m +++ b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m @@ -15,6 +15,8 @@ #endif NSString *const kFLTFirebaseCrashlyticsChannelName = @"plugins.flutter.io/firebase_crashlytics"; +NSString *const kFLTFirebaseCrashlyticsTestChannelName = + @"plugins.flutter.io/firebase_crashlytics_test_stream"; // Argument Keys NSString *const kCrashlyticsArgumentException = @"exception"; @@ -34,6 +36,11 @@ NSString *const kCrashlyticsArgumentUnsentReports = @"unsentReports"; NSString *const kCrashlyticsArgumentDidCrashOnPreviousExecution = @"didCrashOnPreviousExecution"; +@interface FLTFirebaseCrashlyticsPlugin () +@property(nonatomic, strong) FlutterEventChannel *testEventChannel; +@property(nonatomic, strong) FlutterEventSink testEventSink; +@end + @implementation FLTFirebaseCrashlyticsPlugin #pragma mark - FlutterPlugin @@ -61,6 +68,15 @@ + (void)registerWithRegistrar:(NSObject *)registrar { binaryMessenger:[registrar messenger]]; FLTFirebaseCrashlyticsPlugin *instance = [FLTFirebaseCrashlyticsPlugin sharedInstance]; [registrar addMethodCallDelegate:instance channel:channel]; + instance.testEventChannel = + [FlutterEventChannel eventChannelWithName:kFLTFirebaseCrashlyticsTestChannelName + binaryMessenger:[registrar messenger]]; + [instance.testEventChannel setStreamHandler:instance]; +} + +- (BOOL)isRunningInCI { + NSDictionary *env = [[NSProcessInfo processInfo] environment]; + return env[@"GITHUB_ACTIONS"] != nil; } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)flutterResult { @@ -126,6 +142,10 @@ - (void)recordError:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallRes } if (![reason isEqual:[NSNull null]]) { + NSString *crashlyticsErrorReason = [NSString stringWithFormat:@"thrown %@", reason]; + if ([self isRunningInCI] && self.testEventSink) { + self.testEventSink(crashlyticsErrorReason); + } // Log additional custom value to match Android. [[FIRCrashlytics crashlytics] setCustomValue:[NSString stringWithFormat:@"thrown %@", reason] forKey:@"flutter_error_reason"]; @@ -247,4 +267,15 @@ - (NSString *_Nonnull)flutterChannelName { return kFLTFirebaseCrashlyticsChannelName; } +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + self.testEventSink = nil; + return nil; +} + +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments + eventSink:(nonnull FlutterEventSink)events { + self.testEventSink = events; + return nil; +} + @end diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index 9d73803b78a9..8db86c0fbc2a 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -100,6 +100,31 @@ void main() { ); }); + test( + 'should have consistent error reason format', + () async { + final eventChannel = EventChannel('plugins.flutter.io/firebase_crashlytics_test_stream'); + final eventStream = eventChannel.receiveBroadcastStream(); + + final capturedEvents = []; + + eventStream.listen((event) { + capturedEvents.add(event); + }); + + await FirebaseCrashlytics.instance.recordError( + 'foo exception', + StackTrace.fromString('during testing'), + reason: 'foo reason', + ); + + expect(capturedEvents, ['thrown foooo reason']); + + }, + skip: (kIsWeb || defaultTargetPlatform == TargetPlatform.macOS) && !isCI, + ); + }); + group('log', () { // This is currently only testing that we can log without crashing. test('accepts any value', () async { From 33489239703476ae36d5b0e7580d9444f494b4f3 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 12:52:50 +0000 Subject: [PATCH 03/13] fix(tests): update imports and correct test formatting for consistency --- .../firebase_crashlytics/firebase_crashlytics_e2e_test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index 8db86c0fbc2a..71dcb86f1ada 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -5,9 +5,11 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:tests/firebase_options.dart'; +import 'e2e_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -98,9 +100,8 @@ void main() { ); }, ); - }); - test( + test( 'should have consistent error reason format', () async { final eventChannel = EventChannel('plugins.flutter.io/firebase_crashlytics_test_stream'); From dd5ed150d9dfb06e8a54f0d4cba9c99c27705365 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 13:04:43 +0000 Subject: [PATCH 04/13] fix(tests): correct import path and streamline event stream listener in crashlytics e2e tests --- .../firebase_crashlytics_e2e_test.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index 71dcb86f1ada..986f7037d5ca 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -9,7 +9,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:tests/firebase_options.dart'; -import 'e2e_test.dart'; +import '../e2e_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -104,14 +104,12 @@ void main() { test( 'should have consistent error reason format', () async { - final eventChannel = EventChannel('plugins.flutter.io/firebase_crashlytics_test_stream'); + final eventChannel = const EventChannel('plugins.flutter.io/firebase_crashlytics_test_stream'); final eventStream = eventChannel.receiveBroadcastStream(); final capturedEvents = []; - eventStream.listen((event) { - capturedEvents.add(event); - }); + eventStream.listen(capturedEvents.add); await FirebaseCrashlytics.instance.recordError( 'foo exception', From 210ed6abb5bb0cee80ab0e29cbdf6c918423cc95 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 13:15:09 +0000 Subject: [PATCH 05/13] fix(tests): improve event channel declaration and enhance event stream listener for consistency --- .../firebase_crashlytics/firebase_crashlytics_e2e_test.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index 986f7037d5ca..b2a830528cbc 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -104,12 +104,12 @@ void main() { test( 'should have consistent error reason format', () async { - final eventChannel = const EventChannel('plugins.flutter.io/firebase_crashlytics_test_stream'); + const eventChannel = EventChannel('plugins.flutter.io/firebase_crashlytics_test_stream'); final eventStream = eventChannel.receiveBroadcastStream(); final capturedEvents = []; - eventStream.listen(capturedEvents.add); + eventStream.listen((event) => capturedEvents.add(event.toString())); await FirebaseCrashlytics.instance.recordError( 'foo exception', @@ -120,7 +120,7 @@ void main() { expect(capturedEvents, ['thrown foooo reason']); }, - skip: (kIsWeb || defaultTargetPlatform == TargetPlatform.macOS) && !isCI, + skip: kIsWeb || defaultTargetPlatform == TargetPlatform.macOS || !isCI, ); }); From e7a95e5e6e2d97bebc35d2776ed91da9ca904c17 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 13:39:15 +0000 Subject: [PATCH 06/13] feat(crashlytics): add test event channel and modify CI detection logic --- .../crashlytics/FlutterFirebaseCrashlyticsPlugin.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java index 88b4fc6e726e..a58ce1bc007d 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java +++ b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java @@ -49,6 +49,9 @@ private void initInstance(BinaryMessenger messenger) { channel = new MethodChannel(messenger, channelName); channel.setMethodCallHandler(this); FlutterFirebasePluginRegistry.registerPlugin(channelName, this); + testEventChannel = + new EventChannel(messenger, "plugins.flutter.io/firebase_crashlytics_test_stream"); + testEventChannel.setStreamHandler(this); } @Override @@ -62,11 +65,16 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { channel.setMethodCallHandler(null); channel = null; } + if (testEventChannel != null) { + testEventChannel.setStreamHandler(null); + testEventChannel = null; + } } private boolean isRunningInCI() { Map env = System.getenv(); - return env.containsKey("GITHUB_ACTIONS"); + // return env.containsKey("GITHUB_ACTIONS"); + return true; } private Task> checkForUnsentReports() { From fd1b46f0558fda1477216261e5eb8a8d68699a14 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 14:02:53 +0000 Subject: [PATCH 07/13] fix(crashlytics): ensure main thread execution for test event success callback --- .../firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java | 3 ++- .../firebase_crashlytics/firebase_crashlytics_e2e_test.dart | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java index a58ce1bc007d..992f09587f44 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java +++ b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java @@ -150,6 +150,7 @@ private Task> didCrashOnPreviousExecution() { private Task recordError(final Map arguments) { TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); + Handler mainHandler = new Handler(Looper.getMainLooper()); cachedThreadPool.execute( () -> { @@ -178,7 +179,7 @@ private Task recordError(final Map arguments) { if (reason != null) { final String crashlyticsErrorReason = "thrown" + reason; if (isRunningInCI() && testEventSink != null) { - testEventSink.success(crashlyticsErrorReason); + mainHandler.post(() -> testEventSink.success(crashlyticsErrorReason)); } // Set a "reason" (to match iOS) to show where the exception was thrown. crashlytics.setCustomKey(Constants.FLUTTER_ERROR_REASON, crashlyticsErrorReason); diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index b2a830528cbc..726c33fdbf97 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -117,6 +117,8 @@ void main() { reason: 'foo reason', ); + await Future.delayed(const Duration(seconds: 3)); + expect(capturedEvents, ['thrown foooo reason']); }, From ba8f793969b30746af680bf613768f6941929d50 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 14:29:19 +0000 Subject: [PATCH 08/13] fix(crashlytics): correct formatting of crashlytics error reason for consistency across platforms --- .../crashlytics/FlutterFirebaseCrashlyticsPlugin.java | 2 +- .../firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java index 992f09587f44..f4985c8700e3 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java +++ b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java @@ -177,7 +177,7 @@ private Task recordError(final Map arguments) { Exception exception; if (reason != null) { - final String crashlyticsErrorReason = "thrown" + reason; + final String crashlyticsErrorReason = "thrown " + reason; if (isRunningInCI() && testEventSink != null) { mainHandler.post(() -> testEventSink.success(crashlyticsErrorReason)); } diff --git a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m index 56e6225fb4b8..c544d52f3d0e 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m +++ b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m @@ -143,9 +143,9 @@ - (void)recordError:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallRes if (![reason isEqual:[NSNull null]]) { NSString *crashlyticsErrorReason = [NSString stringWithFormat:@"thrown %@", reason]; - if ([self isRunningInCI] && self.testEventSink) { - self.testEventSink(crashlyticsErrorReason); - } + // if ([self isRunningInCI] && self.testEventSink) { + self.testEventSink(crashlyticsErrorReason); + // } // Log additional custom value to match Android. [[FIRCrashlytics crashlytics] setCustomValue:[NSString stringWithFormat:@"thrown %@", reason] forKey:@"flutter_error_reason"]; From c2e91390a45ef0440e6aafdc1c079a024a761f84 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 14:32:25 +0000 Subject: [PATCH 09/13] refactor(crashlytics): remove CI check from error reporting in Android and iOS implementations --- .../crashlytics/FlutterFirebaseCrashlyticsPlugin.java | 10 +--------- .../FLTFirebaseCrashlyticsPlugin.m | 8 +------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java index f4985c8700e3..a854cea95bf6 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java +++ b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java @@ -71,12 +71,6 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { } } - private boolean isRunningInCI() { - Map env = System.getenv(); - // return env.containsKey("GITHUB_ACTIONS"); - return true; - } - private Task> checkForUnsentReports() { TaskCompletionSource> taskCompletionSource = new TaskCompletionSource<>(); @@ -178,9 +172,7 @@ private Task recordError(final Map arguments) { Exception exception; if (reason != null) { final String crashlyticsErrorReason = "thrown " + reason; - if (isRunningInCI() && testEventSink != null) { - mainHandler.post(() -> testEventSink.success(crashlyticsErrorReason)); - } + mainHandler.post(() -> testEventSink.success(crashlyticsErrorReason)); // Set a "reason" (to match iOS) to show where the exception was thrown. crashlytics.setCustomKey(Constants.FLUTTER_ERROR_REASON, crashlyticsErrorReason); exception = diff --git a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m index c544d52f3d0e..6f67de86bc4b 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m +++ b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Sources/firebase_crashlytics/FLTFirebaseCrashlyticsPlugin.m @@ -74,11 +74,6 @@ + (void)registerWithRegistrar:(NSObject *)registrar { [instance.testEventChannel setStreamHandler:instance]; } -- (BOOL)isRunningInCI { - NSDictionary *env = [[NSProcessInfo processInfo] environment]; - return env[@"GITHUB_ACTIONS"] != nil; -} - - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)flutterResult { FLTFirebaseMethodCallErrorBlock errorBlock = ^(NSString *_Nullable code, NSString *_Nullable message, NSDictionary *_Nullable details, @@ -143,9 +138,8 @@ - (void)recordError:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallRes if (![reason isEqual:[NSNull null]]) { NSString *crashlyticsErrorReason = [NSString stringWithFormat:@"thrown %@", reason]; - // if ([self isRunningInCI] && self.testEventSink) { + self.testEventSink(crashlyticsErrorReason); - // } // Log additional custom value to match Android. [[FIRCrashlytics crashlytics] setCustomValue:[NSString stringWithFormat:@"thrown %@", reason] forKey:@"flutter_error_reason"]; From c0ba3526c7f0f093f69727129a0da1cf70798aa3 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 22 Sep 2025 14:53:02 +0000 Subject: [PATCH 10/13] refactor(tests): comment out delay in crashlytics e2e test for faster execution --- .../firebase_crashlytics/firebase_crashlytics_e2e_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index 726c33fdbf97..e4d87b7a5935 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -117,7 +117,7 @@ void main() { reason: 'foo reason', ); - await Future.delayed(const Duration(seconds: 3)); + // await Future.delayed(const Duration(seconds: 3)); expect(capturedEvents, ['thrown foooo reason']); From 2312d96a50db1a34bdc28a292b8362010044b4e4 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Tue, 23 Sep 2025 14:15:45 +0000 Subject: [PATCH 11/13] fix(tests): update event handling in crashlytics e2e test for accurate event capture --- .../firebase_crashlytics_e2e_test.dart | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index e4d87b7a5935..b543495549b4 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -107,20 +107,20 @@ void main() { const eventChannel = EventChannel('plugins.flutter.io/firebase_crashlytics_test_stream'); final eventStream = eventChannel.receiveBroadcastStream(); - final capturedEvents = []; + final completer = Completer(); - eventStream.listen((event) => capturedEvents.add(event.toString())); + eventStream.listen((event) { + completer.complete(event.toString()); + }); await FirebaseCrashlytics.instance.recordError( - 'foo exception', - StackTrace.fromString('during testing'), - reason: 'foo reason', - ); - - // await Future.delayed(const Duration(seconds: 3)); - - expect(capturedEvents, ['thrown foooo reason']); + 'foo exception', + StackTrace.fromString('during testing'), + reason: 'foo reason', + ); + final event = await completer.future; + expect(event, 'thrown foooo reason'); }, skip: kIsWeb || defaultTargetPlatform == TargetPlatform.macOS || !isCI, ); From 5ce041dde5f66422b65d25535fa535527c5a541e Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Tue, 23 Sep 2025 14:23:40 +0000 Subject: [PATCH 12/13] fix(tests): add missing import for async functionality in crashlytics e2e test --- .../firebase_crashlytics/firebase_crashlytics_e2e_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index b543495549b4..81881e5de0aa 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -10,6 +10,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:tests/firebase_options.dart'; import '../e2e_test.dart'; +import 'dart:async'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); From 4f36df7600a94acfde5475e9f1b4adb84c055e05 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Tue, 23 Sep 2025 14:37:12 +0000 Subject: [PATCH 13/13] fix(tests): add logging for received events in crashlytics e2e test --- .../firebase_crashlytics/firebase_crashlytics_e2e_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart index 81881e5de0aa..33c64e6c8de8 100644 --- a/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart +++ b/tests/integration_test/firebase_crashlytics/firebase_crashlytics_e2e_test.dart @@ -111,6 +111,7 @@ void main() { final completer = Completer(); eventStream.listen((event) { + print('Received event: $event'); completer.complete(event.toString()); });