From 73d70bf9f339b62b1177a64faa8267846bb14aaa Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Wed, 14 Aug 2024 11:42:55 +0200 Subject: [PATCH 1/2] test: flutter wasm (#2231) * test: flutter wasm * temporarily disable failing tests * fixup ci * cleanup --- .github/workflows/flutter.yml | 17 ++++++++++------- .../flutter_error_integration_test.dart | 8 ++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index cf2b71069c..236a8546eb 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -29,6 +29,7 @@ jobs: defaults: run: shell: bash + working-directory: flutter strategy: fail-fast: false matrix: @@ -77,21 +78,23 @@ jobs: - run: flutter upgrade - name: Pub Get - run: | - cd flutter - flutter pub get + run: flutter pub get - - name: Test chrome + - name: Test web (JS) if: matrix.target == 'web' run: | - cd flutter flutter test --platform chrome --test-randomize-ordering-seed=random --exclude-tags canvasKit flutter test --platform chrome --test-randomize-ordering-seed=random --tags canvasKit --web-renderer canvaskit + - name: Test web (WASM) + if: matrix.target == 'web' + run: | + flutter test --platform chrome --wasm --test-randomize-ordering-seed=random --exclude-tags canvasKit + flutter test --platform chrome --wasm --test-randomize-ordering-seed=random --tags canvasKit --web-renderer canvaskit + - name: Test VM with coverage if: matrix.target == 'linux' || matrix.target == 'macos' || matrix.target == 'windows' run: | - cd flutter flutter test --coverage --test-randomize-ordering-seed=random dart run remove_from_coverage -f coverage/lcov.info -r 'binding.dart' @@ -111,11 +114,11 @@ jobs: exclude: 'lib/src/native/cocoa/binding.dart' - name: Build ${{ matrix.target }} + working-directory: flutter/example run: | flutter config --enable-windows-desktop flutter config --enable-macos-desktop flutter config --enable-linux-desktop - cd flutter/example TARGET=${{ matrix.target }} flutter pub get case $TARGET in diff --git a/flutter/test/integrations/flutter_error_integration_test.dart b/flutter/test/integrations/flutter_error_integration_test.dart index 73d92565c2..40a8ef7c36 100644 --- a/flutter/test/integrations/flutter_error_integration_test.dart +++ b/flutter/test/integrations/flutter_error_integration_test.dart @@ -82,6 +82,10 @@ void main() { expect(event.contexts['flutter_error_details']['context'], 'thrown while handling a gesture'); expect(event.contexts['flutter_error_details']['information'], 'foo bar'); + }, onPlatform: { + // TODO stacktrace parsing for wasm is not implemented yet + // https://github.com/getsentry/sentry-dart/issues/1480 + 'wasm': Skip('WASM stack trace parsing not implemented yet'), }); test('captures error with long FlutterErrorDetails.information', () async { @@ -115,6 +119,10 @@ void main() { 'thrown while handling a gesture'); expect(event.contexts['flutter_error_details']['information'], 'foo bar\nHello World!'); + }, onPlatform: { + // TODO stacktrace parsing for wasm is not implemented yet + // https://github.com/getsentry/sentry-dart/issues/1480 + 'wasm': Skip('WASM stack trace parsing not implemented yet'), }); test('captures error with no FlutterErrorDetails', () async { From 256df44bb670822fc3087877f775a3d82bb476b0 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 19 Aug 2024 10:00:08 +0200 Subject: [PATCH 2/2] feat: Debouncing of SentryWidgetsBindingObserver.didChangeMetrics. #400 (#2232) * feat: add debouncer for SentryWidgetsBindingObserver.didChangeMetrics * adapt tests for debouncing * add changelog entry for debouncer * Update flutter/lib/src/utils/debouncer.dart Co-authored-by: Giancarlo Buenaflor * Update flutter/test/widgets_binding_observer_test.dart Co-authored-by: Giancarlo Buenaflor * add internal to debouncer and add whitespaces to comments --------- Co-authored-by: Giancarlo Buenaflor --- CHANGELOG.md | 4 + flutter/lib/src/utils/debouncer.dart | 20 +++++ flutter/lib/src/widgets_binding_observer.dart | 12 ++- .../test/widgets_binding_observer_test.dart | 77 +++++++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 flutter/lib/src/utils/debouncer.dart 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)) diff --git a/flutter/lib/src/utils/debouncer.dart b/flutter/lib/src/utils/debouncer.dart new file mode 100644 index 0000000000..b714b41b4e --- /dev/null +++ b/flutter/lib/src/utils/debouncer.dart @@ -0,0 +1,20 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; + +@internal +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) { diff --git a/flutter/test/widgets_binding_observer_test.dart b/flutter/test/widgets_binding_observer_test.dart index 8e3bff9c25..86973ba91c 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); + }); }); }