diff --git a/example/ios/Podfile b/example/ios/Podfile index a6e26b957..9b49aa25b 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -30,7 +30,7 @@ target 'Runner' do use_frameworks! use_modular_headers! - pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/faeture-screen_rendering-release/15.1.17/Instabug.podspec' + pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/faeture-screen_rendering-release/15.1.23/Instabug.podspec' flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 45894bba4..43ffb07ea 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,14 +1,14 @@ PODS: - Flutter (1.0.0) - - Instabug (15.1.17) + - Instabug (15.1.23) - instabug_flutter (14.3.0): - Flutter - - Instabug (= 15.1.17) + - Instabug (= 15.1.23) - OCMock (3.6) DEPENDENCIES: - Flutter (from `Flutter`) - - Instabug (from `https://ios-releases.instabug.com/custom/faeture-screen_rendering-release/15.1.17/Instabug.podspec`) + - Instabug (from `https://ios-releases.instabug.com/custom/faeture-screen_rendering-release/15.1.23/Instabug.podspec`) - instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`) - OCMock (= 3.6) @@ -20,16 +20,16 @@ EXTERNAL SOURCES: Flutter: :path: Flutter Instabug: - :podspec: https://ios-releases.instabug.com/custom/faeture-screen_rendering-release/15.1.17/Instabug.podspec + :podspec: https://ios-releases.instabug.com/custom/faeture-screen_rendering-release/15.1.23/Instabug.podspec instabug_flutter: :path: ".symlinks/plugins/instabug_flutter/ios" SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: 316559a02c9b752a3854a6453c2fa414d36252f3 - instabug_flutter: 7ae7f3d1c47b9a699e76a7358b6a3e818a253ac3 + Instabug: b4659339dc6f67693cf9bd1224abc66831b8722f + instabug_flutter: eeb2e13eefca00e94de1f9156df4889f5481506a OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 -PODFILE CHECKSUM: 8f3e14dab36cc02b0a8767c3086e109fadaa55f3 +PODFILE CHECKSUM: 6d8ca5577997736d9cc2249886c9f6d10238385d COCOAPODS: 1.15.2 diff --git a/example/lib/main.dart b/example/lib/main.dart index bc67d868a..ada18a241 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -20,6 +20,7 @@ import 'src/widget/section_title.dart'; part 'src/components/animated_box.dart'; part 'src/components/apm_switch.dart'; + part 'src/components/fatal_crashes_content.dart'; part 'src/components/flows_content.dart'; part 'src/components/network_content.dart'; @@ -44,6 +45,7 @@ void main() { Instabug.init( token: 'ed6f659591566da19b67857e1b9d40ab', + // token: '4d75635ae06e5afb4360c04cfcf1987c', invocationEvents: [InvocationEvent.floatingButton], debugLogsLevel: LogLevel.verbose, ).then((_) { diff --git a/ios/instabug_flutter.podspec b/ios/instabug_flutter.podspec index 4cda9fd30..b4625d3ad 100644 --- a/ios/instabug_flutter.podspec +++ b/ios/instabug_flutter.podspec @@ -17,6 +17,6 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-framework "Flutter" -framework "InstabugSDK"'} s.dependency 'Flutter' - s.dependency 'Instabug', '15.1.17' + s.dependency 'Instabug', '15.1.23' end diff --git a/lib/src/modules/apm.dart b/lib/src/modules/apm.dart index d832964f8..05b2298c8 100644 --- a/lib/src/modules/apm.dart +++ b/lib/src/modules/apm.dart @@ -196,6 +196,9 @@ class APM { (_) async { // Start screen render collector for custom ui trace if enabled. if (await FlagsConfig.screenRendering.isEnabled()) { + InstabugScreenRenderManager.I.endScreenRenderCollector(); + + // final uiTraceId = IBGDateTime.I.now().millisecondsSinceEpoch; InstabugScreenRenderManager.I .startScreenRenderCollectorForTraceId(0, UiTraceType.custom); } @@ -210,8 +213,7 @@ class APM { static Future endUITrace() async { // End screen render collector for custom ui trace if enabled. if (InstabugScreenRenderManager.I.screenRenderEnabled) { - return InstabugScreenRenderManager.I - .endScreenRenderCollectorForCustomUiTrace(); + return InstabugScreenRenderManager.I.endScreenRenderCollector(); } return _host.endUITrace(); diff --git a/lib/src/utils/instabug_navigator_observer.dart b/lib/src/utils/instabug_navigator_observer.dart index 5793f07fe..b20b56b89 100644 --- a/lib/src/utils/instabug_navigator_observer.dart +++ b/lib/src/utils/instabug_navigator_observer.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; @@ -27,6 +28,7 @@ class InstabugNavigatorObserver extends NavigatorObserver { name: maskedScreenName, ); + InstabugScreenRenderManager.I.endScreenRenderCollector(); ScreenLoadingManager.I .startUiTrace(maskedScreenName, screenName) .then(_startScreenRenderCollector); @@ -67,6 +69,7 @@ class InstabugNavigatorObserver extends NavigatorObserver { FutureOr _startScreenRenderCollector(int? uiTraceId) async { final isScreenRenderEnabled = await FlagsConfig.screenRendering.isEnabled(); + log("isScreenRenderEnabled $isScreenRenderEnabled", name: "Andrew"); await _checkForScreenRenderInitialization(isScreenRenderEnabled); if (uiTraceId != null && isScreenRenderEnabled) { InstabugScreenRenderManager.I diff --git a/lib/src/utils/screen_rendering/instabug_screen_render_manager.dart b/lib/src/utils/screen_rendering/instabug_screen_render_manager.dart index eed37d9e5..4876d0b15 100644 --- a/lib/src/utils/screen_rendering/instabug_screen_render_manager.dart +++ b/lib/src/utils/screen_rendering/instabug_screen_render_manager.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer' show log; import 'dart:ui' show TimingsCallback, FrameTiming, FramePhase; import 'package:flutter/widgets.dart'; @@ -131,29 +132,11 @@ class InstabugScreenRenderManager { return; } - //Save the memory cached data to be sent to native side - if (_delayedFrames.isNotEmpty) { - _saveCollectedData(); - _resetCachedFrameData(); - } - - //Sync the captured screen render data of the Custom UI trace when starting new one if (type == UiTraceType.custom) { - // Report only if the collector was active - if (_screenRenderForCustomUiTrace.isActive) { - _reportScreenRenderForCustomUiTrace(_screenRenderForCustomUiTrace); - _screenRenderForCustomUiTrace.clear(); - } _screenRenderForCustomUiTrace.traceId = traceId; } - //Sync the captured screen render data of the Auto UI trace when starting new one if (type == UiTraceType.auto) { - // Report only if the collector was active - if (_screenRenderForAutoUiTrace.isActive) { - _reportScreenRenderForAutoUiTrace(_screenRenderForAutoUiTrace); - _screenRenderForAutoUiTrace.clear(); - } _screenRenderForAutoUiTrace.traceId = traceId; } } catch (error, stackTrace) { @@ -161,43 +144,56 @@ class InstabugScreenRenderManager { } } - /// Stop screen render collector and sync the captured data. @internal - void stopScreenRenderCollector() { + void endScreenRenderCollector([ + UiTraceType type = UiTraceType.auto, + ]) { try { + // Return if frameTimingListener not attached + if (!screenRenderEnabled || !_isTimingsListenerAttached) { + return; + } + + //Save the memory cached data to be sent to native side if (_delayedFrames.isNotEmpty) { _saveCollectedData(); + _resetCachedFrameData(); } - // Sync Screen Render data for custom ui trace if exists - if (_screenRenderForCustomUiTrace.isActive) { + //Sync the captured screen render data of the Custom UI trace if the collector was active + if (type == UiTraceType.custom && + _screenRenderForCustomUiTrace.isActive) { _reportScreenRenderForCustomUiTrace(_screenRenderForCustomUiTrace); + _screenRenderForCustomUiTrace.clear(); } - // Sync Screen Render data for auto ui trace if exists - if (_screenRenderForAutoUiTrace.isActive) { + //Sync the captured screen render data of the Auto UI trace if the collector was active + if (type == UiTraceType.auto && _screenRenderForAutoUiTrace.isActive) { _reportScreenRenderForAutoUiTrace(_screenRenderForAutoUiTrace); + _screenRenderForAutoUiTrace.clear(); } } catch (error, stackTrace) { _logExceptionErrorAndStackTrace(error, stackTrace); } } - /// Sync the capture screen render data of the custom UI trace without stopping the collector. + /// Stop screen render collector and sync the captured data. @internal - void endScreenRenderCollectorForCustomUiTrace() { + void stopScreenRenderCollector() { try { - if (!_screenRenderForCustomUiTrace.isActive) { - return; + if (_delayedFrames.isNotEmpty) { + _saveCollectedData(); } - // Save the captured screen rendering data to be synced - _updateCustomUiData(); - - // Sync the saved screen rendering data - _reportScreenRenderForCustomUiTrace(_screenRenderForCustomUiTrace); + // Sync Screen Render data for custom ui trace if exists + if (_screenRenderForCustomUiTrace.isActive) { + _reportScreenRenderForCustomUiTrace(_screenRenderForCustomUiTrace); + } - _screenRenderForCustomUiTrace.clear(); + // Sync Screen Render data for auto ui trace if exists + if (_screenRenderForAutoUiTrace.isActive) { + _reportScreenRenderForAutoUiTrace(_screenRenderForAutoUiTrace); + } } catch (error, stackTrace) { _logExceptionErrorAndStackTrace(error, stackTrace); } @@ -300,6 +296,10 @@ class InstabugScreenRenderManager { /// Save Slow/Frozen Frames data void _onDelayedFrameDetected(int startTime, int durationInMicroseconds) { + log( + "${durationInMicroseconds >= 700000 ? "🚨Frozen" : "⚠️Slow"} Frame Detected (startTime: $startTime, duration: $durationInMicroseconds µs)", + name: tag, + ); _delayedFrames.add( InstabugFrameData( startTime, @@ -315,6 +315,10 @@ class InstabugScreenRenderManager { InstabugScreenRenderData screenRenderData, ) async { try { + log( + "reportScreenRenderForCustomUiTrace $screenRenderData", + name: tag, + ); await APM.endScreenRenderForCustomUiTrace(screenRenderData); return true; } catch (error, stackTrace) { @@ -332,6 +336,10 @@ class InstabugScreenRenderManager { try { // Save the end time for the running ui trace, it's only needed in Android SDK. screenRenderData.saveEndTime(); + log( + "reportScreenRenderForAutoUiTrace $screenRenderData", + name: tag, + ); await APM.endScreenRenderForAutoUiTrace(screenRenderData); return true; diff --git a/test/apm_test.dart b/test/apm_test.dart index e080f6f88..76aaf28f4 100644 --- a/test/apm_test.dart +++ b/test/apm_test.dart @@ -346,13 +346,10 @@ void main() { () async { when(mHost.isScreenRenderEnabled()).thenAnswer((_) async => true); when(mScreenRenderManager.screenRenderEnabled).thenReturn(true); - const traceName = "traceNameTest"; - await APM.startUITrace(traceName); await APM.endUITrace(); - verify(mHost.startUITrace(traceName)).called(1); verify( - mScreenRenderManager.endScreenRenderCollectorForCustomUiTrace(), + mScreenRenderManager.endScreenRenderCollector(), ).called(1); verifyNever(mHost.endUITrace()); }); @@ -371,7 +368,7 @@ void main() { mHost.endUITrace(), ).called(1); verifyNever( - mScreenRenderManager.endScreenRenderCollectorForCustomUiTrace(), + mScreenRenderManager.endScreenRenderCollector(), ); }); }); diff --git a/test/utils/screen_render/instabug_screen_render_manager_test.dart b/test/utils/screen_render/instabug_screen_render_manager_test.dart index 4565f22fb..adbe7d74d 100644 --- a/test/utils/screen_render/instabug_screen_render_manager_test.dart +++ b/test/utils/screen_render/instabug_screen_render_manager_test.dart @@ -55,25 +55,6 @@ void main() { ); // the one form initForTesting() }); - test( - 'should report data to native when starting new trace from the same type', - () async { - final frameTestData = InstabugScreenRenderData( - traceId: 123, - frameData: [ - InstabugFrameData(10000, 200), - InstabugFrameData(20000, 1000), - ], - frozenFramesTotalDurationMicro: 1000, - slowFramesTotalDurationMicro: 200, - ); - - manager.startScreenRenderCollectorForTraceId(frameTestData.traceId); - manager.setFrameData(frameTestData); - manager.startScreenRenderCollectorForTraceId(2); - verify(mApmHost.endScreenRenderForAutoUiTrace(any)).called(1); - }); - test('should attach timing listener if it is not attached', () async { manager.stopScreenRenderCollector(); // this should detach listener safely @@ -272,7 +253,7 @@ void main() { manager.setFrameData(frameTestData); - manager.endScreenRenderCollectorForCustomUiTrace(); + manager.endScreenRenderCollector(); expect(manager.screenRenderForCustomUiTrace.isActive, false); expect(manager.screenRenderForCustomUiTrace == frameTestData, false); @@ -298,11 +279,11 @@ void main() { manager.setFrameData(frameTestData); - manager.endScreenRenderCollectorForCustomUiTrace(); + manager.endScreenRenderCollector(); }); test('should not remove timing callback listener', () { - manager.endScreenRenderCollectorForCustomUiTrace(); + manager.endScreenRenderCollector(); verifyNever(mWidgetBinding.removeTimingsCallback(any)); }); @@ -320,7 +301,7 @@ void main() { manager.startScreenRenderCollectorForTraceId(0, UiTraceType.custom); manager.setFrameData(frameTestData); - manager.endScreenRenderCollectorForCustomUiTrace(); + manager.endScreenRenderCollector(UiTraceType.custom); verify(mApmHost.endScreenRenderForCustomUiTrace(any)).called(1); }); }); @@ -419,4 +400,50 @@ void main() { ); }); }); + + group('InstabugScreenRenderManager.endScreenRenderCollector', () { + test('should save and reset cached data if delayed frames exist', () { + final frameTestData = InstabugScreenRenderData( + traceId: 123, + frameData: [ + InstabugFrameData(10000, 200), + InstabugFrameData(20000, 1000), + ], + frozenFramesTotalDurationMicro: 1000, + slowFramesTotalDurationMicro: 200, + ); + manager.startScreenRenderCollectorForTraceId(1); + manager.setFrameData(frameTestData); + manager.endScreenRenderCollector(); + verify(mApmHost.endScreenRenderForAutoUiTrace(any)).called(1); + expect(manager.screenRenderForAutoUiTrace.isEmpty, true); + expect(manager.screenRenderForAutoUiTrace.isActive, false); + }); + + test('should report and clear custom trace if type is custom and active', + () { + final frameTestData = InstabugScreenRenderData( + traceId: 123, + frameData: [ + InstabugFrameData(10000, 200), + InstabugFrameData(20000, 1000), + ], + frozenFramesTotalDurationMicro: 1000, + slowFramesTotalDurationMicro: 200, + ); + manager.startScreenRenderCollectorForTraceId(1, UiTraceType.custom); + manager.setFrameData(frameTestData); + manager.endScreenRenderCollector(UiTraceType.custom); + verify(mApmHost.endScreenRenderForCustomUiTrace(any)).called(1); + expect(manager.screenRenderForCustomUiTrace.isEmpty, true); + expect(manager.screenRenderForCustomUiTrace.isActive, false); + }); + + test('should return early if not enabled or timings not attached', () { + manager.screenRenderEnabled = false; + manager.endScreenRenderCollector(); + verifyNever(mApmHost.endScreenRenderForAutoUiTrace(any)); + verifyNever(mApmHost.endScreenRenderForCustomUiTrace(any)); + }); + }); } diff --git a/test/utils/screen_render/instabug_screen_render_manager_test_manual_mocks.dart b/test/utils/screen_render/instabug_screen_render_manager_test_manual_mocks.dart index 4e363eb75..64d147846 100644 --- a/test/utils/screen_render/instabug_screen_render_manager_test_manual_mocks.dart +++ b/test/utils/screen_render/instabug_screen_render_manager_test_manual_mocks.dart @@ -39,8 +39,8 @@ class _FakeSingletonFlutterWindow_1 extends _i1.Fake class _FakePlatformDispatcher_2 extends _i1.Fake implements _i4.PlatformDispatcher {} -class _FakeHardwareKeyboard_3 extends _i1.Fake - implements _i5.HardwareKeyboard {} +class _FakeHardwareKeyboard_3 extends _i1.Fake implements + _i5.HardwareKeyboard {} class _FakeKeyEventManager_4 extends _i1.Fake implements _i5.KeyEventManager {} @@ -645,21 +645,21 @@ class MockWidgetsBinding extends _i1.Mock implements _i10.WidgetsBinding { super.noSuchMethod(Invocation.method(#cancelPointer, [pointer]), returnValueForMissingStub: null); @override - void handlePointerEvent(_i7.PointerEvent? event) => + void handlePointerEvent(_i6.PointerEvent? event) => super.noSuchMethod(Invocation.method(#handlePointerEvent, [event]), returnValueForMissingStub: null); @override - void hitTest(_i7.HitTestResult? result, _i4.Offset? position) => + void hitTest(_i6.HitTestResult? result, _i4.Offset? position) => super.noSuchMethod(Invocation.method(#hitTest, [result, position]), returnValueForMissingStub: null); @override void dispatchEvent( - _i7.PointerEvent? event, _i7.HitTestResult? hitTestResult) => + _i6.PointerEvent? event, _i6.HitTestResult? hitTestResult) => super.noSuchMethod( Invocation.method(#dispatchEvent, [event, hitTestResult]), returnValueForMissingStub: null); @override - void handleEvent(_i7.PointerEvent? event, _i7.HitTestEntry? entry) => + void handleEvent(_i6.PointerEvent? event, _i6.HitTestEntry? entry) => super.noSuchMethod(Invocation.method(#handleEvent, [event, entry]), returnValueForMissingStub: null); @override