From 0c84e5e9b08201c951466a55a6401eab04fb36cc Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Wed, 14 Aug 2024 11:10:48 +0200 Subject: [PATCH 1/6] feat: add debouncer for SentryWidgetsBindingObserver.didChangeMetrics --- flutter/lib/src/utils/debouncer.dart | 18 ++++++++++++++++++ flutter/lib/src/widgets_binding_observer.dart | 12 +++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 flutter/lib/src/utils/debouncer.dart diff --git a/flutter/lib/src/utils/debouncer.dart b/flutter/lib/src/utils/debouncer.dart new file mode 100644 index 0000000000..d1967ceab6 --- /dev/null +++ b/flutter/lib/src/utils/debouncer.dart @@ -0,0 +1,18 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; + +class Debouncer { + final int milliseconds; + Timer? _timer; + + Debouncer({required this.milliseconds}); + + void run(VoidCallback action) { + _timer?.cancel(); + _timer = Timer(Duration(milliseconds: milliseconds), action); + } + + void dispose() { + _timer?.cancel(); + } +} diff --git a/flutter/lib/src/widgets_binding_observer.dart b/flutter/lib/src/widgets_binding_observer.dart index b199b7f8a5..7c0db1842f 100644 --- a/flutter/lib/src/widgets_binding_observer.dart +++ b/flutter/lib/src/widgets_binding_observer.dart @@ -4,6 +4,7 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../sentry_flutter.dart'; +import 'utils/debouncer.dart'; /// This is a `WidgetsBindingObserver` which can observe some events of a /// Flutter application. @@ -50,6 +51,8 @@ class SentryWidgetsBindingObserver with WidgetsBindingObserver { // ignore: deprecated_member_use final StreamController _screenSizeStreamController; + final _didChangeMetricsDebouncer = Debouncer(milliseconds: 100); + /// This method records lifecycle events. /// It tries to mimic the behavior of ActivityBreadcrumbsIntegration of Sentry /// Android for lifecycle events. @@ -88,9 +91,12 @@ class SentryWidgetsBindingObserver with WidgetsBindingObserver { if (!_options.enableWindowMetricBreadcrumbs) { return; } - // ignore: deprecated_member_use - final window = _options.bindingUtils.instance?.window; - _screenSizeStreamController.add(window); + + _didChangeMetricsDebouncer.run(() { + // ignore: deprecated_member_use + final window = _options.bindingUtils.instance?.window; + _screenSizeStreamController.add(window); + }); } void _onScreenSizeChanged(Map data) { From 06af5bba804e382150b168b02f2c9a9a75c8fe6b Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Wed, 14 Aug 2024 12:25:25 +0200 Subject: [PATCH 2/6] adapt tests for debouncing --- .../test/widgets_binding_observer_test.dart | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/flutter/test/widgets_binding_observer_test.dart b/flutter/test/widgets_binding_observer_test.dart index 8e3bff9c25..8d450088fd 100644 --- a/flutter/test/widgets_binding_observer_test.dart +++ b/flutter/test/widgets_binding_observer_test.dart @@ -193,6 +193,9 @@ void main() { // ignore: deprecated_member_use window.physicalSizeTestValue = Size(newWidth, newHeight); + //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + await tester.pump(Duration(milliseconds: 150)); + final breadcrumb = verify(hub.addBreadcrumb(captureAny)).captured.single as Breadcrumb; @@ -230,6 +233,9 @@ void main() { // ignore: deprecated_member_use window.devicePixelRatioTestValue = newPixelRatio; + //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + await tester.pump(Duration(milliseconds: 150)); + final breadcrumb = verify(hub.addBreadcrumb(captureAny)).captured.single as Breadcrumb; @@ -265,6 +271,9 @@ void main() { // ignore: deprecated_member_use window.viewInsetsTestValue = WindowPadding.zero; + //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + await tester.pump(Duration(milliseconds: 150)); + verifyNever(hub.addBreadcrumb(captureAny)); instance.removeObserver(observer); @@ -286,6 +295,9 @@ void main() { window.onMetricsChanged!(); + //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + await tester.pump(Duration(milliseconds: 150)); + verifyNever(hub.addBreadcrumb(captureAny)); instance.removeObserver(observer); @@ -400,5 +412,70 @@ void main() { instance.removeObserver(observer); }); + + testWidgets('debouncing didChangeMetrics with 100ms delay', + (WidgetTester tester) async { + final hub = MockHub(); + + final observer = SentryWidgetsBindingObserver( + hub: hub, + options: flutterTrackingEnabledOptions, + ); + final instance = tester.binding; + instance.addObserver(observer); + + // ignore: deprecated_member_use + final window = instance.window; + + // ignore: deprecated_member_use + window.physicalSizeTestValue = window.physicalSize; + + const newPixelRatio = 1.7; + // ignore: deprecated_member_use + window.devicePixelRatioTestValue = newPixelRatio; + + verifyNever(hub.addBreadcrumb(captureAny)); + + //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + await tester.pump(Duration(milliseconds: 150)); + + verify(hub.addBreadcrumb(captureAny)); + + instance.removeObserver(observer); + }); + + testWidgets('debouncing: didChangeMetrics is called only once in 100ms', + (WidgetTester tester) async { + final hub = MockHub(); + + final observer = SentryWidgetsBindingObserver( + hub: hub, + options: flutterTrackingEnabledOptions, + ); + final instance = tester.binding; + instance.addObserver(observer); + + // ignore: deprecated_member_use + final window = instance.window; + + // ignore: deprecated_member_use + window.physicalSizeTestValue = window.physicalSize; + + // ignore: deprecated_member_use + window.devicePixelRatioTestValue = 2.1; + // ignore: deprecated_member_use + window.devicePixelRatioTestValue = 2.2; + // ignore: deprecated_member_use + window.devicePixelRatioTestValue = 2.3; + + verifyNever(hub.addBreadcrumb(captureAny)); + + //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + await tester.pump(Duration(milliseconds: 150)); + + verify(hub.addBreadcrumb(captureAny)).called(1); + + instance.removeObserver(observer); + }); }); } From d63d2f02f28e56f97e07d5a03f77e31f06202001 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Wed, 14 Aug 2024 12:33:31 +0200 Subject: [PATCH 3/6] add changelog entry for debouncer --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf5117d226..672ed7fce9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ SentryNavigatorObserver(ignoreRoutes: ["/ignoreThisRoute"]), ``` +### Improvements + +- Debouncing of SentryWidgetsBindingObserver.didChangeMetrics with delay of 100ms. ([#2232](https://github.com/getsentry/sentry-dart/pull/2232)) + ### Dependencies - Bump Android SDK from v7.13.0 to v7.14.0 ([#2228](https://github.com/getsentry/sentry-dart/pull/2228)) From ba9a6a5cb00c08933a78053844cea96e165f376e Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 19 Aug 2024 09:52:56 +0200 Subject: [PATCH 4/6] Update flutter/lib/src/utils/debouncer.dart Co-authored-by: Giancarlo Buenaflor --- flutter/lib/src/utils/debouncer.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter/lib/src/utils/debouncer.dart b/flutter/lib/src/utils/debouncer.dart index d1967ceab6..252541d0fc 100644 --- a/flutter/lib/src/utils/debouncer.dart +++ b/flutter/lib/src/utils/debouncer.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; +@internal class Debouncer { final int milliseconds; Timer? _timer; From 7b20277e70e45d289a25e7d8f43ff53e9f778738 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 19 Aug 2024 09:53:10 +0200 Subject: [PATCH 5/6] Update flutter/test/widgets_binding_observer_test.dart Co-authored-by: Giancarlo Buenaflor --- flutter/test/widgets_binding_observer_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/test/widgets_binding_observer_test.dart b/flutter/test/widgets_binding_observer_test.dart index 8d450088fd..3b103ac30b 100644 --- a/flutter/test/widgets_binding_observer_test.dart +++ b/flutter/test/widgets_binding_observer_test.dart @@ -436,7 +436,7 @@ void main() { verifyNever(hub.addBreadcrumb(captureAny)); - //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + // waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 await tester.pump(Duration(milliseconds: 150)); verify(hub.addBreadcrumb(captureAny)); From 491201f0d23874eb86bc7eb3261c05dd0bb128e3 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 19 Aug 2024 09:58:50 +0200 Subject: [PATCH 6/6] add internal to debouncer and add whitespaces to comments --- flutter/lib/src/utils/debouncer.dart | 1 + flutter/test/widgets_binding_observer_test.dart | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/flutter/lib/src/utils/debouncer.dart b/flutter/lib/src/utils/debouncer.dart index 252541d0fc..b714b41b4e 100644 --- a/flutter/lib/src/utils/debouncer.dart +++ b/flutter/lib/src/utils/debouncer.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; @internal class Debouncer { diff --git a/flutter/test/widgets_binding_observer_test.dart b/flutter/test/widgets_binding_observer_test.dart index 3b103ac30b..86973ba91c 100644 --- a/flutter/test/widgets_binding_observer_test.dart +++ b/flutter/test/widgets_binding_observer_test.dart @@ -193,7 +193,7 @@ void main() { // ignore: deprecated_member_use window.physicalSizeTestValue = Size(newWidth, newHeight); - //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + // waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 await tester.pump(Duration(milliseconds: 150)); final breadcrumb = @@ -233,7 +233,7 @@ void main() { // ignore: deprecated_member_use window.devicePixelRatioTestValue = newPixelRatio; - //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + // waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 await tester.pump(Duration(milliseconds: 150)); final breadcrumb = @@ -271,7 +271,7 @@ void main() { // ignore: deprecated_member_use window.viewInsetsTestValue = WindowPadding.zero; - //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + // waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 await tester.pump(Duration(milliseconds: 150)); verifyNever(hub.addBreadcrumb(captureAny)); @@ -295,7 +295,7 @@ void main() { window.onMetricsChanged!(); - //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + // waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 await tester.pump(Duration(milliseconds: 150)); verifyNever(hub.addBreadcrumb(captureAny)); @@ -470,7 +470,7 @@ void main() { verifyNever(hub.addBreadcrumb(captureAny)); - //waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 + // waiting for debouncing with 100ms added https://github.com/getsentry/sentry-dart/issues/400 await tester.pump(Duration(milliseconds: 150)); verify(hub.addBreadcrumb(captureAny)).called(1);