diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b45101ab6..2ce461cd63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- Tag all spans with thread info ([#3101](https://github.com/getsentry/sentry-dart/pull/3101)) + ## 9.6.0 Note: this release might require updating your Android Gradle Plugin version to at least `8.1.4`. diff --git a/dart/lib/src/hub.dart b/dart/lib/src/hub.dart index a4533793c7..72ef667167 100644 --- a/dart/lib/src/hub.dart +++ b/dart/lib/src/hub.dart @@ -650,22 +650,6 @@ class Hub { SentryProfilerFactory? _profilerFactory; - @internal - Map> get lifecycleCallbacks => - _peek().client.lifeCycleRegistry.lifecycleCallbacks; - - @internal - void registerSdkLifecycleCallback( - SdkLifecycleCallback callback) { - _peek().client.lifeCycleRegistry.registerCallback(callback); - } - - @internal - void removeSdkLifecycleCallback( - SdkLifecycleCallback callback) { - _peek().client.lifeCycleRegistry.removeCallback(callback); - } - SentryEvent _assignTraceContext(SentryEvent event) { // assign trace context if (event.throwable != null && event.contexts.trace == null) { diff --git a/dart/lib/src/hub_adapter.dart b/dart/lib/src/hub_adapter.dart index 7595954444..dc5080479f 100644 --- a/dart/lib/src/hub_adapter.dart +++ b/dart/lib/src/hub_adapter.dart @@ -8,7 +8,6 @@ import 'profiling.dart'; import 'protocol.dart'; import 'protocol/sentry_feedback.dart'; import 'scope.dart'; -import 'sdk_lifecycle_hooks.dart'; import 'sentry.dart'; import 'sentry_client.dart'; import 'sentry_options.dart'; @@ -200,20 +199,4 @@ class HubAdapter implements Hub { @override FutureOr captureLog(SentryLog log) => Sentry.currentHub.captureLog(log); - - @override - Map> get lifecycleCallbacks => - Sentry.currentHub.lifecycleCallbacks; - - @override - void registerSdkLifecycleCallback( - SdkLifecycleCallback callback) { - Sentry.currentHub.registerSdkLifecycleCallback(callback); - } - - @override - void removeSdkLifecycleCallback( - SdkLifecycleCallback callback) { - Sentry.currentHub.removeSdkLifecycleCallback(callback); - } } diff --git a/dart/lib/src/logs_enricher_integration.dart b/dart/lib/src/logs_enricher_integration.dart index 1885a95c19..ba9f0479d1 100644 --- a/dart/lib/src/logs_enricher_integration.dart +++ b/dart/lib/src/logs_enricher_integration.dart @@ -15,7 +15,7 @@ class LogsEnricherIntegration extends Integration { @override FutureOr call(Hub hub, SentryOptions options) { if (options.enableLogs) { - hub.registerSdkLifecycleCallback( + options.lifecycleRegistry.registerCallback( (event) async { final os = getSentryOperatingSystem(); diff --git a/dart/lib/src/noop_hub.dart b/dart/lib/src/noop_hub.dart index de5de1a2b7..e085e0eecd 100644 --- a/dart/lib/src/noop_hub.dart +++ b/dart/lib/src/noop_hub.dart @@ -8,7 +8,6 @@ import 'profiling.dart'; import 'protocol.dart'; import 'protocol/sentry_feedback.dart'; import 'scope.dart'; -import 'sdk_lifecycle_hooks.dart'; import 'sentry_client.dart'; import 'sentry_options.dart'; import 'tracing.dart'; @@ -145,15 +144,4 @@ class NoOpHub implements Hub { @override Scope get scope => Scope(_options); - - @override - Map> get lifecycleCallbacks => {}; - - @override - void registerSdkLifecycleCallback( - SdkLifecycleCallback callback) {} - - @override - void removeSdkLifecycleCallback( - SdkLifecycleCallback callback) {} } diff --git a/dart/lib/src/noop_sentry_client.dart b/dart/lib/src/noop_sentry_client.dart index a0badcc7d9..d071edbf16 100644 --- a/dart/lib/src/noop_sentry_client.dart +++ b/dart/lib/src/noop_sentry_client.dart @@ -4,10 +4,8 @@ import 'hint.dart'; import 'protocol.dart'; import 'protocol/sentry_feedback.dart'; import 'scope.dart'; -import 'sdk_lifecycle_hooks.dart'; import 'sentry_client.dart'; import 'sentry_envelope.dart'; -import 'sentry_options.dart'; import 'sentry_trace_context_header.dart'; class NoOpSentryClient implements SentryClient { @@ -71,9 +69,4 @@ class NoOpSentryClient implements SentryClient { @override FutureOr captureLog(SentryLog log, {Scope? scope}) async {} - - final _lifeCycleRegistry = SdkLifecycleRegistry(SentryOptions.empty()); - - @override - SdkLifecycleRegistry get lifeCycleRegistry => _lifeCycleRegistry; } diff --git a/dart/lib/src/sdk_lifecycle_hooks.dart b/dart/lib/src/sdk_lifecycle_hooks.dart index c5ffd6e018..d50910bff7 100644 --- a/dart/lib/src/sdk_lifecycle_hooks.dart +++ b/dart/lib/src/sdk_lifecycle_hooks.dart @@ -71,3 +71,11 @@ class OnBeforeSendEvent extends SdkLifecycleEvent { final SentryEvent event; final Hint hint; } + +/// Dispatched when a sampled span is started. +@internal +class OnSpanStart extends SdkLifecycleEvent { + OnSpanStart(this.span); + + final ISentrySpan span; +} diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index 7b27aa63e3..b8b1e7e5d0 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -46,12 +46,6 @@ class SentryClient { static final _emptySentryId = Future.value(SentryId.empty()); - late final SdkLifecycleRegistry _lifecycleRegistry; - - /// Allows registration and dispatching of callbacks outside of SentryClient - @internal - SdkLifecycleRegistry get lifeCycleRegistry => _lifecycleRegistry; - SentryExceptionFactory get _exceptionFactory => _options.exceptionFactory; SentryStackTraceFactory get _stackTraceFactory => _options.stackTraceFactory; @@ -89,9 +83,7 @@ class SentryClient { /// Instantiates a client using [SentryOptions] SentryClient._(this._options) - : _random = _options.sampleRate == null ? null : Random() { - _lifecycleRegistry = SdkLifecycleRegistry(_options); - } + : _random = _options.sampleRate == null ? null : Random(); /// Reports an [event] to Sentry.io. Future captureEvent( @@ -172,7 +164,7 @@ class SentryClient { } // Event is fully processed and ready to be sent - await _lifecycleRegistry + await _options.lifecycleRegistry .dispatchCallback(OnBeforeSendEvent(preparedEvent, hint)); var attachments = List.from(scope?.attachments ?? []); @@ -579,7 +571,7 @@ class SentryClient { } if (processedLog != null) { - await _lifecycleRegistry + await _options.lifecycleRegistry .dispatchCallback(OnBeforeCaptureLog(processedLog)); _options.logBatcher.addLog(processedLog); } else { diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 7aba9721d8..32090e0bc1 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -412,6 +412,9 @@ class SentryOptions { @internal late ClientReportRecorder recorder = NoOpClientReportRecorder(); + @internal + late SdkLifecycleRegistry lifecycleRegistry = SdkLifecycleRegistry(this); + /// List of strings/regex controlling to which outgoing requests /// the SDK will attach tracing headers. /// diff --git a/dart/lib/src/sentry_tracer.dart b/dart/lib/src/sentry_tracer.dart index 1cc618a716..10132bbb58 100644 --- a/dart/lib/src/sentry_tracer.dart +++ b/dart/lib/src/sentry_tracer.dart @@ -88,6 +88,7 @@ class SentryTracer extends ISentrySpan { collector.onSpanStarted(_rootSpan); } } + _dispatchOnSpanStart(_rootSpan); } @override @@ -271,6 +272,7 @@ class SentryTracer extends ISentrySpan { collector.onSpanStarted(child); } } + _dispatchOnSpanStart(child); return child; } @@ -435,4 +437,8 @@ class SentryTracer extends ISentrySpan { }); } } + + void _dispatchOnSpanStart(ISentrySpan span) { + _hub.options.lifecycleRegistry.dispatchCallback(OnSpanStart(span)); + } } diff --git a/dart/lib/src/span_data_convention.dart b/dart/lib/src/span_data_convention.dart index 5979e6035a..7297b00eaf 100644 --- a/dart/lib/src/span_data_convention.dart +++ b/dart/lib/src/span_data_convention.dart @@ -6,5 +6,10 @@ class SpanDataConvention { static const frozenFrames = 'frames.frozen'; static const framesDelay = 'frames.delay'; + // Thread/Isolate data keys according to Sentry span data conventions + // https://develop.sentry.dev/sdk/telemetry/traces/span-data-conventions/#thread + static const threadId = 'thread.id'; + static const threadName = 'thread.name'; + // TODO: eventually add other data keys here as well } diff --git a/dart/test/sentry_client_lifecycle_test.dart b/dart/test/sentry_client_lifecycle_test.dart index 7d780d0cf9..7f1ae69697 100644 --- a/dart/test/sentry_client_lifecycle_test.dart +++ b/dart/test/sentry_client_lifecycle_test.dart @@ -43,7 +43,8 @@ void main() { final client = fixture.getSut(); fixture.options.logBatcher = MockLogBatcher(); - client.lifeCycleRegistry.registerCallback((event) { + fixture.options.lifecycleRegistry + .registerCallback((event) { event.log.attributes['test'] = SentryLogAttribute.string('test-value'); }); @@ -74,7 +75,8 @@ void main() { final client = fixture.getSut(); fixture.options.logBatcher = MockLogBatcher(); - client.lifeCycleRegistry.registerCallback((event) { + fixture.options.lifecycleRegistry + .registerCallback((event) { event.event.release = '999'; }); diff --git a/dart/test/sentry_client_sdk_lifecycle_test.dart b/dart/test/sentry_client_sdk_lifecycle_test.dart index 7d780d0cf9..7f1ae69697 100644 --- a/dart/test/sentry_client_sdk_lifecycle_test.dart +++ b/dart/test/sentry_client_sdk_lifecycle_test.dart @@ -43,7 +43,8 @@ void main() { final client = fixture.getSut(); fixture.options.logBatcher = MockLogBatcher(); - client.lifeCycleRegistry.registerCallback((event) { + fixture.options.lifecycleRegistry + .registerCallback((event) { event.log.attributes['test'] = SentryLogAttribute.string('test-value'); }); @@ -74,7 +75,8 @@ void main() { final client = fixture.getSut(); fixture.options.logBatcher = MockLogBatcher(); - client.lifeCycleRegistry.registerCallback((event) { + fixture.options.lifecycleRegistry + .registerCallback((event) { event.event.release = '999'; }); diff --git a/dart/test/sentry_client_test.dart b/dart/test/sentry_client_test.dart index 33eb43697c..d0423fc2fd 100644 --- a/dart/test/sentry_client_test.dart +++ b/dart/test/sentry_client_test.dart @@ -2007,7 +2007,8 @@ void main() { final client = fixture.getSut(); fixture.options.logBatcher = MockLogBatcher(); - client.lifeCycleRegistry.registerCallback((event) { + fixture.options.lifecycleRegistry + .registerCallback((event) { event.log.attributes['test'] = SentryLogAttribute.string('test-value'); }); diff --git a/dart/test/sentry_tracer_test.dart b/dart/test/sentry_tracer_test.dart index 86f6f02ccb..1c67645824 100644 --- a/dart/test/sentry_tracer_test.dart +++ b/dart/test/sentry_tracer_test.dart @@ -598,6 +598,137 @@ void main() { expect(context.sampleRate, '1'); expect(context.sampled, 'true'); }); + + group('OnSpanStart lifecycle callbacks', () { + test('dispatches OnSpanStart for root span on tracer creation', () async { + final fixture = Fixture(); + final capturedSpans = []; + + // Register OnSpanStart callback + fixture.hub.options.lifecycleRegistry.registerCallback( + (OnSpanStart event) async { + capturedSpans.add(event.span); + }, + ); + + // Create tracer (this should dispatch OnSpanStart for the root span) + final sut = fixture.getSut(); + + // Allow async callbacks to complete + await Future.delayed(Duration.zero); + + expect(capturedSpans.length, equals(1)); + // The root span is a SentrySpan, not the SentryTracer itself + expect(capturedSpans.first, isA()); + expect(capturedSpans.first.context.operation, equals('op')); + expect(capturedSpans.first.context.description, + isNull); // No description set for root span + expect( + capturedSpans.first.context.traceId, equals(sut.context.traceId)); + }); + + test('dispatches OnSpanStart for child spans', () async { + final fixture = Fixture(); + final capturedSpans = []; + + // Register OnSpanStart callback + fixture.hub.options.lifecycleRegistry.registerCallback( + (OnSpanStart event) async { + capturedSpans.add(event.span); + }, + ); + + final sut = fixture.getSut(); + + // Clear the root span callback + capturedSpans.clear(); + + // Create child spans + sut.startChild('child_op1'); + sut.startChild('child_op2', description: 'child description'); + + // Allow async callbacks to complete + await Future.delayed(Duration.zero); + + expect(capturedSpans.length, equals(2)); + + // Verify first child span + expect(capturedSpans[0].context.operation, equals('child_op1')); + expect(capturedSpans[0].context.description, isNull); + expect( + capturedSpans[0].context.parentSpanId, equals(sut.context.spanId)); + + // Verify second child span + expect(capturedSpans[1].context.operation, equals('child_op2')); + expect( + capturedSpans[1].context.description, equals('child description')); + expect( + capturedSpans[1].context.parentSpanId, equals(sut.context.spanId)); + }); + + test('dispatches OnSpanStart for nested child spans', () async { + final fixture = Fixture(); + final capturedSpans = []; + + // Register OnSpanStart callback + fixture.hub.options.lifecycleRegistry.registerCallback( + (OnSpanStart event) async { + capturedSpans.add(event.span); + }, + ); + + final sut = fixture.getSut(); + capturedSpans.clear(); // Clear root span + + // Create nested spans + final child = sut.startChild('child_op'); + await Future.delayed(Duration.zero); + + child.startChild('grandchild_op'); + await Future.delayed(Duration.zero); + + expect(capturedSpans.length, equals(2)); + + // Verify child span + expect(capturedSpans[0].context.operation, equals('child_op')); + expect( + capturedSpans[0].context.parentSpanId, equals(sut.context.spanId)); + + // Verify grandchild span + expect(capturedSpans[1].context.operation, equals('grandchild_op')); + expect(capturedSpans[1].context.parentSpanId, + equals(child.context.spanId)); + }); + + test('callbacks are called asynchronously without blocking span creation', + () async { + final fixture = Fixture(); + var callbackExecuted = false; + + // Register callback that takes some time + fixture.hub.options.lifecycleRegistry.registerCallback( + (OnSpanStart event) async { + await Future.delayed(const Duration(milliseconds: 10)); + callbackExecuted = true; + }, + ); + + // Span creation should complete immediately without waiting for callback + final sut = fixture.getSut(); + final child = sut.startChild('child_op'); + + // Callback should not have been executed yet (since it's unawaited) + expect(callbackExecuted, isFalse); + + // Wait a bit for the callback to complete + await Future.delayed(const Duration(milliseconds: 20)); + + // Now callback should have completed + expect(callbackExecuted, isTrue); + expect(sut.context.operation, equals('op')); + expect(child.context.operation, equals('child_op')); + }); + }); }); } diff --git a/flutter/lib/src/integrations/load_contexts_integration.dart b/flutter/lib/src/integrations/load_contexts_integration.dart index d06966ff05..1d972446b5 100644 --- a/flutter/lib/src/integrations/load_contexts_integration.dart +++ b/flutter/lib/src/integrations/load_contexts_integration.dart @@ -55,7 +55,7 @@ class LoadContextsIntegration extends Integration { } // ignore: invalid_use_of_internal_member - hub.registerSdkLifecycleCallback( + options.lifecycleRegistry.registerCallback( (event) async { try { final infos = await _native.loadContexts() ?? {}; diff --git a/flutter/lib/src/integrations/thread_info_integration.dart b/flutter/lib/src/integrations/thread_info_integration.dart new file mode 100644 index 0000000000..94ad83bb8c --- /dev/null +++ b/flutter/lib/src/integrations/thread_info_integration.dart @@ -0,0 +1,68 @@ +// ignore_for_file: invalid_use_of_internal_member + +import 'package:meta/meta.dart'; + +import '../../sentry_flutter.dart'; +import '../isolate_helper.dart'; + +/// Integration for adding thread information to spans. +/// +/// This integration registers a lifecycle callback that adds thread/isolate +/// information to spans when they are started. +@internal +class ThreadInfoIntegration implements Integration { + static const integrationName = 'ThreadInfo'; + + final IsolateHelper _isolateHelper; + Hub? _hub; + + ThreadInfoIntegration([IsolateHelper? isolateHelper]) + : _isolateHelper = isolateHelper ?? IsolateHelper(); + + @override + void call(Hub hub, SentryFlutterOptions options) { + _hub = hub; + + if (!options.isTracingEnabled()) { + options.log( + SentryLevel.info, + '$integrationName disabled: tracing is not enabled', + ); + return; + } + + options.lifecycleRegistry + .registerCallback(_addThreadInfoToSpan); + + options.sdk.addIntegration(integrationName); + } + + @override + void close() { + _hub?.options.lifecycleRegistry + .removeCallback(_addThreadInfoToSpan); + } + + Future _addThreadInfoToSpan(OnSpanStart event) async { + final span = event.span; + // Check if we're in the root isolate first + if (_isolateHelper.isRootIsolate()) { + // For root isolate, always set thread name as "main" + span.setData(SpanDataConvention.threadId, 'main'.hashCode.toString()); + span.setData(SpanDataConvention.threadName, 'main'); + return; + } + + // For non-root isolates, get thread info dynamically for each span to handle multi-isolate scenarios + final isolateName = _isolateHelper.getIsolateName(); + + // Only set thread info if we have a valid isolate name + if (isolateName != null && isolateName.isNotEmpty) { + final threadName = isolateName; + final threadId = isolateName.hashCode.toString(); + + span.setData(SpanDataConvention.threadId, threadId); + span.setData(SpanDataConvention.threadName, threadName); + } + } +} diff --git a/flutter/lib/src/integrations/web_session_integration.dart b/flutter/lib/src/integrations/web_session_integration.dart index 06d1b81fc9..0ec0c7c3f8 100644 --- a/flutter/lib/src/integrations/web_session_integration.dart +++ b/flutter/lib/src/integrations/web_session_integration.dart @@ -21,7 +21,6 @@ class WebSessionIntegration implements Integration { static const integrationName = 'WebSessionIntegration'; SentryFlutterOptions? _options; - Hub? _hub; WebSessionHandler? _webSessionHandler; WebSessionHandler? get webSessionHandler => _webSessionHandler; SdkLifecycleCallback? _onBeforeSendEventCallback; @@ -33,7 +32,6 @@ class WebSessionIntegration implements Integration { @override void call(Hub hub, SentryFlutterOptions options) { _options = options; - _hub = hub; _options?.log(SentryLevel.info, '$integrationName initialization started, waiting for SentryNavigatorObserver to be initialized.'); } @@ -41,7 +39,8 @@ class WebSessionIntegration implements Integration { @override void close() { if (_onBeforeSendEventCallback != null) { - _hub?.removeSdkLifecycleCallback(_onBeforeSendEventCallback!); + _options?.lifecycleRegistry + .removeCallback(_onBeforeSendEventCallback!); } } @@ -60,8 +59,7 @@ class WebSessionIntegration implements Integration { _onBeforeSendEventCallback = (lifecycleEvent) async { await _webSessionHandler?.updateSessionFromEvent(lifecycleEvent.event); }; - _hub?.registerSdkLifecycleCallback( - _onBeforeSendEventCallback!); + _options?.lifecycleRegistry.registerCallback(_onBeforeSendEventCallback!); _options?.sdk.addIntegration(integrationName); _options?.log(SentryLevel.info, '$integrationName successfully enabled.'); } diff --git a/flutter/lib/src/isolate_helper.dart b/flutter/lib/src/isolate_helper.dart new file mode 100644 index 0000000000..c003d47aa9 --- /dev/null +++ b/flutter/lib/src/isolate_helper.dart @@ -0,0 +1,14 @@ +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart'; +// ignore: implementation_imports +import 'package:sentry/src/utils/isolate_utils.dart' as isolate_utils; + +@internal +class IsolateHelper { + // ignore: invalid_use_of_internal_member + String? getIsolateName() => isolate_utils.getIsolateName(); + + bool isRootIsolate() { + return ServicesBinding.rootIsolateToken != null; + } +} diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index bf93512fbd..f0c39e5d98 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -24,6 +24,7 @@ import 'integrations/integrations.dart'; import 'integrations/native_app_start_handler.dart'; import 'integrations/screenshot_integration.dart'; import 'integrations/generic_app_start_integration.dart'; +import 'integrations/thread_info_integration.dart'; import 'integrations/web_session_integration.dart'; import 'native/factory.dart'; import 'native/native_scope_observer.dart'; @@ -229,6 +230,8 @@ mixin SentryFlutter { integrations.add(DebugPrintIntegration()); + integrations.add(ThreadInfoIntegration()); + return integrations; } diff --git a/flutter/test/integrations/thread_info_integration_test.dart b/flutter/test/integrations/thread_info_integration_test.dart new file mode 100644 index 0000000000..3615e84814 --- /dev/null +++ b/flutter/test/integrations/thread_info_integration_test.dart @@ -0,0 +1,236 @@ +@TestOn('vm') +library; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sentry_flutter/src/integrations/thread_info_integration.dart'; +import 'package:sentry_flutter/src/isolate_helper.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; + +import '../mocks.mocks.dart'; + +void main() { + late _Fixture fixture; + + setUp(() { + fixture = _Fixture(); + }); + + group('ThreadInfoIntegration', () { + test('sets main thread name when in root isolate', () async { + fixture.mockHelper.setIsRootIsolate(true); + fixture.mockHelper.setIsolateName("main(debug)"); + + final hub = fixture.createHub(); + final integration = fixture.getSut(); + final span = fixture.createMockSpan(); + + // Call the integration like the real app would + integration.call(hub, fixture.options); + + // Dispatch OnSpanStart event through the lifecycle registry + // ignore: invalid_use_of_internal_member + await fixture.options.lifecycleRegistry + .dispatchCallback(OnSpanStart(span)); + + final setDataCalls = span.setDataCalls; + expect(setDataCalls.length, equals(2)); + + final threadIdCall = setDataCalls + .firstWhere((call) => call.key == SpanDataConvention.threadId); + final threadNameCall = setDataCalls + .firstWhere((call) => call.key == SpanDataConvention.threadName); + + expect(threadIdCall.value, equals('main'.hashCode.toString())); + expect(threadNameCall.value, equals('main')); + }); + + test('adds thread information when isolate has name', () async { + fixture.mockHelper.setIsRootIsolate(false); + fixture.mockHelper.setIsolateName('worker-thread'); + + final hub = fixture.createHub(); + final integration = fixture.getSut(); + final span = fixture.createMockSpan(); + + integration.call(hub, fixture.options); + // ignore: invalid_use_of_internal_member + await fixture.options.lifecycleRegistry + .dispatchCallback(OnSpanStart(span)); + + final setDataCalls = span.setDataCalls; + expect(setDataCalls.length, equals(2)); + + final threadIdCall = setDataCalls + .firstWhere((call) => call.key == SpanDataConvention.threadId); + final threadNameCall = setDataCalls + .firstWhere((call) => call.key == SpanDataConvention.threadName); + + expect(threadIdCall.value, equals('worker-thread'.hashCode.toString())); + expect(threadNameCall.value, equals('worker-thread')); + }); + + test('gets thread info dynamically for each span', () async { + fixture.mockHelper.setIsRootIsolate(false); + fixture.mockHelper.setIsolateName('dynamic-test'); + + final hub = fixture.createHub(); + final integration = fixture.getSut(); + final span = fixture.createMockSpan(); + + integration.call(hub, fixture.options); + // ignore: invalid_use_of_internal_member + await fixture.options.lifecycleRegistry + .dispatchCallback(OnSpanStart(span)); + final firstCallCount = span.setDataCalls.length; + + final span2 = fixture.createMockSpan(); + // ignore: invalid_use_of_internal_member + await fixture.options.lifecycleRegistry + .dispatchCallback(OnSpanStart(span2)); + + // Should have same number of calls for both spans (thread info collected fresh each time) + expect(span2.setDataCalls.length, equals(firstCallCount)); + }); + + test('sets thread ID and name for non-root isolate with valid name', + () async { + fixture.mockHelper.setIsRootIsolate(false); + fixture.mockHelper.setIsolateName('custom-isolate'); + + final hub = fixture.createHub(); + final integration = fixture.getSut(); + final span = fixture.createMockSpan(); + + integration.call(hub, fixture.options); + // ignore: invalid_use_of_internal_member + await fixture.options.lifecycleRegistry + .dispatchCallback(OnSpanStart(span)); + + // Find thread data calls + final threadIdCall = span.setDataCalls + .firstWhere((call) => call.key == SpanDataConvention.threadId); + final threadNameCall = span.setDataCalls + .firstWhere((call) => call.key == SpanDataConvention.threadName); + + expect(threadIdCall.value, equals('custom-isolate'.hashCode.toString())); + expect(threadNameCall.value, equals('custom-isolate')); + }); + + test('does not set thread info when isolate name is null', () async { + fixture.mockHelper.setIsRootIsolate(false); + fixture.mockHelper.setIsolateName(null); + + final hub = fixture.createHub(); + final integration = fixture.getSut(); + final span = fixture.createMockSpan(); + + integration.call(hub, fixture.options); + // ignore: invalid_use_of_internal_member + await fixture.options.lifecycleRegistry + .dispatchCallback(OnSpanStart(span)); + + // When isolate name is null, no thread data should be set + expect(span.setDataCalls, isEmpty); + }); + + test('does not set thread info when isolate name is empty', () async { + fixture.mockHelper.setIsRootIsolate(false); + fixture.mockHelper.setIsolateName(''); + + final hub = fixture.createHub(); + final integration = fixture.getSut(); + final span = fixture.createMockSpan(); + + integration.call(hub, fixture.options); + // ignore: invalid_use_of_internal_member + await fixture.options.lifecycleRegistry + .dispatchCallback(OnSpanStart(span)); + + // When isolate name is empty, no thread data should be set + expect(span.setDataCalls, isEmpty); + }); + + test('does not register callback when tracing is disabled', () async { + fixture.mockHelper.setIsRootIsolate(true); + fixture.mockHelper.setIsolateName("main"); + + final hub = fixture.createHub(); + final integration = fixture.getSut(); + final span = fixture.createMockSpan(); + + // Disable tracing + fixture.options.tracesSampleRate = null; + + integration.call(hub, fixture.options); + // ignore: invalid_use_of_internal_member + await fixture.options.lifecycleRegistry + .dispatchCallback(OnSpanStart(span)); + + // Should not add any thread data when tracing is disabled + expect(span.setDataCalls, isEmpty); + }); + }); +} + +class _Fixture { + late _MockIsolateHelper mockHelper; + late SentryFlutterOptions options; + + _Fixture() { + mockHelper = _MockIsolateHelper(); + options = SentryFlutterOptions(); + options.tracesSampleRate = 1.0; // Enable tracing by default + // Set default return values to avoid null errors + mockHelper.setIsRootIsolate(false); + mockHelper.setIsolateName(null); + } + + ThreadInfoIntegration getSut() { + return ThreadInfoIntegration(mockHelper); + } + + _MockSpan createMockSpan() { + return _MockSpan(); + } + + MockHub createHub() { + final hub = MockHub(); + when(hub.options).thenReturn(options); + return hub; + } +} + +class _MockIsolateHelper extends Mock implements IsolateHelper { + bool _isRootIsolate = false; + String? _isolateName; + + @override + bool isRootIsolate() => _isRootIsolate; + + @override + String? getIsolateName() => _isolateName; + + void setIsRootIsolate(bool value) => _isRootIsolate = value; + void setIsolateName(String? value) => _isolateName = value; +} + +class _MockSpan extends Mock implements SentrySpan { + final SentrySpanContext _context = SentrySpanContext(operation: 'test'); + final List<_SetDataCall> setDataCalls = []; + + @override + SentrySpanContext get context => _context; + + @override + void setData(String key, dynamic value) { + setDataCalls.add(_SetDataCall(key, value)); + } +} + +class _SetDataCall { + final String key; + final dynamic value; + + _SetDataCall(this.key, this.value); +} diff --git a/flutter/test/integrations/web_session_integration_test.dart b/flutter/test/integrations/web_session_integration_test.dart index 1e38e62514..744b8f2bd6 100644 --- a/flutter/test/integrations/web_session_integration_test.dart +++ b/flutter/test/integrations/web_session_integration_test.dart @@ -60,7 +60,9 @@ void main() { }); test('handles enable being called multiple times', () { - expect(hub.lifecycleCallbacks.values.flattened, isEmpty); + expect( + fixture.options.lifecycleRegistry.lifecycleCallbacks.values.flattened, + isEmpty); final sut = fixture.getSut(native); sut.call(hub, fixture.options); @@ -74,29 +76,40 @@ void main() { .length, 1); - expect(hub.lifecycleCallbacks.values.flattened.length, 1); + expect( + fixture.options.lifecycleRegistry.lifecycleCallbacks.values.flattened + .length, + 1); }); test('adds onBeforeSendEventCallback when enabled', () { final sut = fixture.getSut(native); - expect(hub.lifecycleCallbacks.values.flattened, isEmpty); + expect( + fixture.options.lifecycleRegistry.lifecycleCallbacks.values.flattened, + isEmpty); sut.call(hub, fixture.options); sut.enable(); - expect(hub.lifecycleCallbacks.values.flattened, isNotEmpty); + expect( + fixture.options.lifecycleRegistry.lifecycleCallbacks.values.flattened, + isNotEmpty); }); test('removes onBeforeSendEventCallback on close', () { final sut = fixture.getSut(native); sut.call(hub, fixture.options); sut.enable(); - expect(hub.lifecycleCallbacks.values.flattened, isNotEmpty); + expect( + fixture.options.lifecycleRegistry.lifecycleCallbacks.values.flattened, + isNotEmpty); sut.close(); - expect(hub.lifecycleCallbacks.values.flattened, isEmpty); + expect( + fixture.options.lifecycleRegistry.lifecycleCallbacks.values.flattened, + isEmpty); }); test('sets WebSessionHandler when enabled', () { diff --git a/flutter/test/mocks.mocks.dart b/flutter/test/mocks.mocks.dart index fcf32a4f1e..9c0fd9755a 100644 --- a/flutter/test/mocks.mocks.dart +++ b/flutter/test/mocks.mocks.dart @@ -132,9 +132,8 @@ class _FakeSentryTransaction_7 extends _i1.SmartFake ); } -class _FakeSdkLifecycleRegistry_8 extends _i1.SmartFake - implements _i2.SdkLifecycleRegistry { - _FakeSdkLifecycleRegistry_8( +class _FakeMethodCodec_8 extends _i1.SmartFake implements _i4.MethodCodec { + _FakeMethodCodec_8( Object parent, Invocation parentInvocation, ) : super( @@ -143,19 +142,9 @@ class _FakeSdkLifecycleRegistry_8 extends _i1.SmartFake ); } -class _FakeMethodCodec_9 extends _i1.SmartFake implements _i4.MethodCodec { - _FakeMethodCodec_9( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeBinaryMessenger_10 extends _i1.SmartFake +class _FakeBinaryMessenger_9 extends _i1.SmartFake implements _i4.BinaryMessenger { - _FakeBinaryMessenger_10( + _FakeBinaryMessenger_9( Object parent, Invocation parentInvocation, ) : super( @@ -164,9 +153,9 @@ class _FakeBinaryMessenger_10 extends _i1.SmartFake ); } -class _FakeWidgetsBinding_11 extends _i1.SmartFake +class _FakeWidgetsBinding_10 extends _i1.SmartFake implements _i5.WidgetsBinding { - _FakeWidgetsBinding_11( + _FakeWidgetsBinding_10( Object parent, Invocation parentInvocation, ) : super( @@ -175,9 +164,9 @@ class _FakeWidgetsBinding_11 extends _i1.SmartFake ); } -class _FakeSingletonFlutterWindow_12 extends _i1.SmartFake +class _FakeSingletonFlutterWindow_11 extends _i1.SmartFake implements _i6.SingletonFlutterWindow { - _FakeSingletonFlutterWindow_12( + _FakeSingletonFlutterWindow_11( Object parent, Invocation parentInvocation, ) : super( @@ -186,9 +175,9 @@ class _FakeSingletonFlutterWindow_12 extends _i1.SmartFake ); } -class _FakePlatformDispatcher_13 extends _i1.SmartFake +class _FakePlatformDispatcher_12 extends _i1.SmartFake implements _i6.PlatformDispatcher { - _FakePlatformDispatcher_13( + _FakePlatformDispatcher_12( Object parent, Invocation parentInvocation, ) : super( @@ -197,8 +186,8 @@ class _FakePlatformDispatcher_13 extends _i1.SmartFake ); } -class _FakePointerRouter_14 extends _i1.SmartFake implements _i7.PointerRouter { - _FakePointerRouter_14( +class _FakePointerRouter_13 extends _i1.SmartFake implements _i7.PointerRouter { + _FakePointerRouter_13( Object parent, Invocation parentInvocation, ) : super( @@ -207,9 +196,9 @@ class _FakePointerRouter_14 extends _i1.SmartFake implements _i7.PointerRouter { ); } -class _FakeGestureArenaManager_15 extends _i1.SmartFake +class _FakeGestureArenaManager_14 extends _i1.SmartFake implements _i7.GestureArenaManager { - _FakeGestureArenaManager_15( + _FakeGestureArenaManager_14( Object parent, Invocation parentInvocation, ) : super( @@ -218,9 +207,9 @@ class _FakeGestureArenaManager_15 extends _i1.SmartFake ); } -class _FakePointerSignalResolver_16 extends _i1.SmartFake +class _FakePointerSignalResolver_15 extends _i1.SmartFake implements _i7.PointerSignalResolver { - _FakePointerSignalResolver_16( + _FakePointerSignalResolver_15( Object parent, Invocation parentInvocation, ) : super( @@ -229,8 +218,8 @@ class _FakePointerSignalResolver_16 extends _i1.SmartFake ); } -class _FakeDuration_17 extends _i1.SmartFake implements Duration { - _FakeDuration_17( +class _FakeDuration_16 extends _i1.SmartFake implements Duration { + _FakeDuration_16( Object parent, Invocation parentInvocation, ) : super( @@ -239,8 +228,8 @@ class _FakeDuration_17 extends _i1.SmartFake implements Duration { ); } -class _FakeSamplingClock_18 extends _i1.SmartFake implements _i7.SamplingClock { - _FakeSamplingClock_18( +class _FakeSamplingClock_17 extends _i1.SmartFake implements _i7.SamplingClock { + _FakeSamplingClock_17( Object parent, Invocation parentInvocation, ) : super( @@ -249,9 +238,9 @@ class _FakeSamplingClock_18 extends _i1.SmartFake implements _i7.SamplingClock { ); } -class _FakeValueNotifier_19 extends _i1.SmartFake +class _FakeValueNotifier_18 extends _i1.SmartFake implements _i8.ValueNotifier { - _FakeValueNotifier_19( + _FakeValueNotifier_18( Object parent, Invocation parentInvocation, ) : super( @@ -260,9 +249,9 @@ class _FakeValueNotifier_19 extends _i1.SmartFake ); } -class _FakeHardwareKeyboard_20 extends _i1.SmartFake +class _FakeHardwareKeyboard_19 extends _i1.SmartFake implements _i4.HardwareKeyboard { - _FakeHardwareKeyboard_20( + _FakeHardwareKeyboard_19( Object parent, Invocation parentInvocation, ) : super( @@ -271,9 +260,9 @@ class _FakeHardwareKeyboard_20 extends _i1.SmartFake ); } -class _FakeKeyEventManager_21 extends _i1.SmartFake +class _FakeKeyEventManager_20 extends _i1.SmartFake implements _i4.KeyEventManager { - _FakeKeyEventManager_21( + _FakeKeyEventManager_20( Object parent, Invocation parentInvocation, ) : super( @@ -282,9 +271,9 @@ class _FakeKeyEventManager_21 extends _i1.SmartFake ); } -class _FakeChannelBuffers_22 extends _i1.SmartFake +class _FakeChannelBuffers_21 extends _i1.SmartFake implements _i6.ChannelBuffers { - _FakeChannelBuffers_22( + _FakeChannelBuffers_21( Object parent, Invocation parentInvocation, ) : super( @@ -293,9 +282,9 @@ class _FakeChannelBuffers_22 extends _i1.SmartFake ); } -class _FakeRestorationManager_23 extends _i1.SmartFake +class _FakeRestorationManager_22 extends _i1.SmartFake implements _i4.RestorationManager { - _FakeRestorationManager_23( + _FakeRestorationManager_22( Object parent, Invocation parentInvocation, ) : super( @@ -304,8 +293,8 @@ class _FakeRestorationManager_23 extends _i1.SmartFake ); } -class _FakeImageCache_24 extends _i1.SmartFake implements _i9.ImageCache { - _FakeImageCache_24( +class _FakeImageCache_23 extends _i1.SmartFake implements _i9.ImageCache { + _FakeImageCache_23( Object parent, Invocation parentInvocation, ) : super( @@ -314,8 +303,8 @@ class _FakeImageCache_24 extends _i1.SmartFake implements _i9.ImageCache { ); } -class _FakeListenable_25 extends _i1.SmartFake implements _i8.Listenable { - _FakeListenable_25( +class _FakeListenable_24 extends _i1.SmartFake implements _i8.Listenable { + _FakeListenable_24( Object parent, Invocation parentInvocation, ) : super( @@ -324,9 +313,9 @@ class _FakeListenable_25 extends _i1.SmartFake implements _i8.Listenable { ); } -class _FakeAccessibilityFeatures_26 extends _i1.SmartFake +class _FakeAccessibilityFeatures_25 extends _i1.SmartFake implements _i6.AccessibilityFeatures { - _FakeAccessibilityFeatures_26( + _FakeAccessibilityFeatures_25( Object parent, Invocation parentInvocation, ) : super( @@ -335,8 +324,8 @@ class _FakeAccessibilityFeatures_26 extends _i1.SmartFake ); } -class _FakeRenderView_27 extends _i1.SmartFake implements _i10.RenderView { - _FakeRenderView_27( +class _FakeRenderView_26 extends _i1.SmartFake implements _i10.RenderView { + _FakeRenderView_26( Object parent, Invocation parentInvocation, ) : super( @@ -349,8 +338,8 @@ class _FakeRenderView_27 extends _i1.SmartFake implements _i10.RenderView { super.toString(); } -class _FakeMouseTracker_28 extends _i1.SmartFake implements _i10.MouseTracker { - _FakeMouseTracker_28( +class _FakeMouseTracker_27 extends _i1.SmartFake implements _i10.MouseTracker { + _FakeMouseTracker_27( Object parent, Invocation parentInvocation, ) : super( @@ -359,9 +348,9 @@ class _FakeMouseTracker_28 extends _i1.SmartFake implements _i10.MouseTracker { ); } -class _FakePlatformMenuDelegate_29 extends _i1.SmartFake +class _FakePlatformMenuDelegate_28 extends _i1.SmartFake implements _i9.PlatformMenuDelegate { - _FakePlatformMenuDelegate_29( + _FakePlatformMenuDelegate_28( Object parent, Invocation parentInvocation, ) : super( @@ -370,8 +359,8 @@ class _FakePlatformMenuDelegate_29 extends _i1.SmartFake ); } -class _FakeFocusManager_30 extends _i1.SmartFake implements _i9.FocusManager { - _FakeFocusManager_30( +class _FakeFocusManager_29 extends _i1.SmartFake implements _i9.FocusManager { + _FakeFocusManager_29( Object parent, Invocation parentInvocation, ) : super( @@ -384,8 +373,8 @@ class _FakeFocusManager_30 extends _i1.SmartFake implements _i9.FocusManager { super.toString(); } -class _FakeFuture_31 extends _i1.SmartFake implements _i11.Future { - _FakeFuture_31( +class _FakeFuture_30 extends _i1.SmartFake implements _i11.Future { + _FakeFuture_30( Object parent, Invocation parentInvocation, ) : super( @@ -394,8 +383,8 @@ class _FakeFuture_31 extends _i1.SmartFake implements _i11.Future { ); } -class _FakeCodec_32 extends _i1.SmartFake implements _i6.Codec { - _FakeCodec_32( +class _FakeCodec_31 extends _i1.SmartFake implements _i6.Codec { + _FakeCodec_31( Object parent, Invocation parentInvocation, ) : super( @@ -404,9 +393,9 @@ class _FakeCodec_32 extends _i1.SmartFake implements _i6.Codec { ); } -class _FakeSemanticsHandle_33 extends _i1.SmartFake +class _FakeSemanticsHandle_32 extends _i1.SmartFake implements _i12.SemanticsHandle { - _FakeSemanticsHandle_33( + _FakeSemanticsHandle_32( Object parent, Invocation parentInvocation, ) : super( @@ -415,9 +404,9 @@ class _FakeSemanticsHandle_33 extends _i1.SmartFake ); } -class _FakeSemanticsUpdateBuilder_34 extends _i1.SmartFake +class _FakeSemanticsUpdateBuilder_33 extends _i1.SmartFake implements _i6.SemanticsUpdateBuilder { - _FakeSemanticsUpdateBuilder_34( + _FakeSemanticsUpdateBuilder_33( Object parent, Invocation parentInvocation, ) : super( @@ -426,9 +415,9 @@ class _FakeSemanticsUpdateBuilder_34 extends _i1.SmartFake ); } -class _FakeViewConfiguration_35 extends _i1.SmartFake +class _FakeViewConfiguration_34 extends _i1.SmartFake implements _i10.ViewConfiguration { - _FakeViewConfiguration_35( + _FakeViewConfiguration_34( Object parent, Invocation parentInvocation, ) : super( @@ -437,8 +426,8 @@ class _FakeViewConfiguration_35 extends _i1.SmartFake ); } -class _FakeSceneBuilder_36 extends _i1.SmartFake implements _i6.SceneBuilder { - _FakeSceneBuilder_36( +class _FakeSceneBuilder_35 extends _i1.SmartFake implements _i6.SceneBuilder { + _FakeSceneBuilder_35( Object parent, Invocation parentInvocation, ) : super( @@ -447,9 +436,9 @@ class _FakeSceneBuilder_36 extends _i1.SmartFake implements _i6.SceneBuilder { ); } -class _FakePictureRecorder_37 extends _i1.SmartFake +class _FakePictureRecorder_36 extends _i1.SmartFake implements _i6.PictureRecorder { - _FakePictureRecorder_37( + _FakePictureRecorder_36( Object parent, Invocation parentInvocation, ) : super( @@ -458,8 +447,8 @@ class _FakePictureRecorder_37 extends _i1.SmartFake ); } -class _FakeCanvas_38 extends _i1.SmartFake implements _i6.Canvas { - _FakeCanvas_38( +class _FakeCanvas_37 extends _i1.SmartFake implements _i6.Canvas { + _FakeCanvas_37( Object parent, Invocation parentInvocation, ) : super( @@ -468,8 +457,8 @@ class _FakeCanvas_38 extends _i1.SmartFake implements _i6.Canvas { ); } -class _FakeWidget_39 extends _i1.SmartFake implements _i9.Widget { - _FakeWidget_39( +class _FakeWidget_38 extends _i1.SmartFake implements _i9.Widget { + _FakeWidget_38( Object parent, Invocation parentInvocation, ) : super( @@ -482,9 +471,9 @@ class _FakeWidget_39 extends _i1.SmartFake implements _i9.Widget { super.toString(); } -class _FakeSentryFlutterOptions_40 extends _i1.SmartFake +class _FakeSentryFlutterOptions_39 extends _i1.SmartFake implements _i2.SentryFlutterOptions { - _FakeSentryFlutterOptions_40( + _FakeSentryFlutterOptions_39( Object parent, Invocation parentInvocation, ) : super( @@ -493,8 +482,8 @@ class _FakeSentryFlutterOptions_40 extends _i1.SmartFake ); } -class _FakeSentryOptions_41 extends _i1.SmartFake implements _i2.SentryOptions { - _FakeSentryOptions_41( +class _FakeSentryOptions_40 extends _i1.SmartFake implements _i2.SentryOptions { + _FakeSentryOptions_40( Object parent, Invocation parentInvocation, ) : super( @@ -503,8 +492,8 @@ class _FakeSentryOptions_41 extends _i1.SmartFake implements _i2.SentryOptions { ); } -class _FakeScope_42 extends _i1.SmartFake implements _i2.Scope { - _FakeScope_42( +class _FakeScope_41 extends _i1.SmartFake implements _i2.Scope { + _FakeScope_41( Object parent, Invocation parentInvocation, ) : super( @@ -513,8 +502,8 @@ class _FakeScope_42 extends _i1.SmartFake implements _i2.Scope { ); } -class _FakeHub_43 extends _i1.SmartFake implements _i2.Hub { - _FakeHub_43( +class _FakeHub_42 extends _i1.SmartFake implements _i2.Hub { + _FakeHub_42( Object parent, Invocation parentInvocation, ) : super( @@ -1585,15 +1574,6 @@ class MockSentryClient extends _i1.Mock implements _i2.SentryClient { _i1.throwOnMissingStub(this); } - @override - _i2.SdkLifecycleRegistry get lifeCycleRegistry => (super.noSuchMethod( - Invocation.getter(#lifeCycleRegistry), - returnValue: _FakeSdkLifecycleRegistry_8( - this, - Invocation.getter(#lifeCycleRegistry), - ), - ) as _i2.SdkLifecycleRegistry); - @override _i11.Future<_i2.SentryId> captureEvent( _i2.SentryEvent? event, { @@ -1803,7 +1783,7 @@ class MockMethodChannel extends _i1.Mock implements _i4.MethodChannel { @override _i4.MethodCodec get codec => (super.noSuchMethod( Invocation.getter(#codec), - returnValue: _FakeMethodCodec_9( + returnValue: _FakeMethodCodec_8( this, Invocation.getter(#codec), ), @@ -1812,7 +1792,7 @@ class MockMethodChannel extends _i1.Mock implements _i4.MethodChannel { @override _i4.BinaryMessenger get binaryMessenger => (super.noSuchMethod( Invocation.getter(#binaryMessenger), - returnValue: _FakeBinaryMessenger_10( + returnValue: _FakeBinaryMessenger_9( this, Invocation.getter(#binaryMessenger), ), @@ -2163,7 +2143,7 @@ class MockBindingWrapper extends _i1.Mock implements _i2.BindingWrapper { #ensureInitialized, [], ), - returnValue: _FakeWidgetsBinding_11( + returnValue: _FakeWidgetsBinding_10( this, Invocation.method( #ensureInitialized, @@ -2185,7 +2165,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i6.SingletonFlutterWindow get window => (super.noSuchMethod( Invocation.getter(#window), - returnValue: _FakeSingletonFlutterWindow_12( + returnValue: _FakeSingletonFlutterWindow_11( this, Invocation.getter(#window), ), @@ -2194,7 +2174,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i6.PlatformDispatcher get platformDispatcher => (super.noSuchMethod( Invocation.getter(#platformDispatcher), - returnValue: _FakePlatformDispatcher_13( + returnValue: _FakePlatformDispatcher_12( this, Invocation.getter(#platformDispatcher), ), @@ -2209,7 +2189,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i7.PointerRouter get pointerRouter => (super.noSuchMethod( Invocation.getter(#pointerRouter), - returnValue: _FakePointerRouter_14( + returnValue: _FakePointerRouter_13( this, Invocation.getter(#pointerRouter), ), @@ -2218,7 +2198,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i7.GestureArenaManager get gestureArena => (super.noSuchMethod( Invocation.getter(#gestureArena), - returnValue: _FakeGestureArenaManager_15( + returnValue: _FakeGestureArenaManager_14( this, Invocation.getter(#gestureArena), ), @@ -2227,7 +2207,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i7.PointerSignalResolver get pointerSignalResolver => (super.noSuchMethod( Invocation.getter(#pointerSignalResolver), - returnValue: _FakePointerSignalResolver_16( + returnValue: _FakePointerSignalResolver_15( this, Invocation.getter(#pointerSignalResolver), ), @@ -2242,7 +2222,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override Duration get samplingOffset => (super.noSuchMethod( Invocation.getter(#samplingOffset), - returnValue: _FakeDuration_17( + returnValue: _FakeDuration_16( this, Invocation.getter(#samplingOffset), ), @@ -2251,7 +2231,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i7.SamplingClock get samplingClock => (super.noSuchMethod( Invocation.getter(#samplingClock), - returnValue: _FakeSamplingClock_18( + returnValue: _FakeSamplingClock_17( this, Invocation.getter(#samplingClock), ), @@ -2318,7 +2298,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override Duration get currentFrameTimeStamp => (super.noSuchMethod( Invocation.getter(#currentFrameTimeStamp), - returnValue: _FakeDuration_17( + returnValue: _FakeDuration_16( this, Invocation.getter(#currentFrameTimeStamp), ), @@ -2327,7 +2307,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override Duration get currentSystemFrameTimeStamp => (super.noSuchMethod( Invocation.getter(#currentSystemFrameTimeStamp), - returnValue: _FakeDuration_17( + returnValue: _FakeDuration_16( this, Invocation.getter(#currentSystemFrameTimeStamp), ), @@ -2346,7 +2326,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i8.ValueNotifier get accessibilityFocus => (super.noSuchMethod( Invocation.getter(#accessibilityFocus), - returnValue: _FakeValueNotifier_19( + returnValue: _FakeValueNotifier_18( this, Invocation.getter(#accessibilityFocus), ), @@ -2355,7 +2335,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i4.HardwareKeyboard get keyboard => (super.noSuchMethod( Invocation.getter(#keyboard), - returnValue: _FakeHardwareKeyboard_20( + returnValue: _FakeHardwareKeyboard_19( this, Invocation.getter(#keyboard), ), @@ -2364,7 +2344,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i4.KeyEventManager get keyEventManager => (super.noSuchMethod( Invocation.getter(#keyEventManager), - returnValue: _FakeKeyEventManager_21( + returnValue: _FakeKeyEventManager_20( this, Invocation.getter(#keyEventManager), ), @@ -2373,7 +2353,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i4.BinaryMessenger get defaultBinaryMessenger => (super.noSuchMethod( Invocation.getter(#defaultBinaryMessenger), - returnValue: _FakeBinaryMessenger_10( + returnValue: _FakeBinaryMessenger_9( this, Invocation.getter(#defaultBinaryMessenger), ), @@ -2382,7 +2362,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i6.ChannelBuffers get channelBuffers => (super.noSuchMethod( Invocation.getter(#channelBuffers), - returnValue: _FakeChannelBuffers_22( + returnValue: _FakeChannelBuffers_21( this, Invocation.getter(#channelBuffers), ), @@ -2391,7 +2371,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i4.RestorationManager get restorationManager => (super.noSuchMethod( Invocation.getter(#restorationManager), - returnValue: _FakeRestorationManager_23( + returnValue: _FakeRestorationManager_22( this, Invocation.getter(#restorationManager), ), @@ -2400,7 +2380,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i9.ImageCache get imageCache => (super.noSuchMethod( Invocation.getter(#imageCache), - returnValue: _FakeImageCache_24( + returnValue: _FakeImageCache_23( this, Invocation.getter(#imageCache), ), @@ -2409,7 +2389,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i8.Listenable get systemFonts => (super.noSuchMethod( Invocation.getter(#systemFonts), - returnValue: _FakeListenable_25( + returnValue: _FakeListenable_24( this, Invocation.getter(#systemFonts), ), @@ -2430,7 +2410,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i6.AccessibilityFeatures get accessibilityFeatures => (super.noSuchMethod( Invocation.getter(#accessibilityFeatures), - returnValue: _FakeAccessibilityFeatures_26( + returnValue: _FakeAccessibilityFeatures_25( this, Invocation.getter(#accessibilityFeatures), ), @@ -2454,7 +2434,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i10.RenderView get renderView => (super.noSuchMethod( Invocation.getter(#renderView), - returnValue: _FakeRenderView_27( + returnValue: _FakeRenderView_26( this, Invocation.getter(#renderView), ), @@ -2463,7 +2443,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i10.MouseTracker get mouseTracker => (super.noSuchMethod( Invocation.getter(#mouseTracker), - returnValue: _FakeMouseTracker_28( + returnValue: _FakeMouseTracker_27( this, Invocation.getter(#mouseTracker), ), @@ -2493,7 +2473,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i9.PlatformMenuDelegate get platformMenuDelegate => (super.noSuchMethod( Invocation.getter(#platformMenuDelegate), - returnValue: _FakePlatformMenuDelegate_29( + returnValue: _FakePlatformMenuDelegate_28( this, Invocation.getter(#platformMenuDelegate), ), @@ -2515,7 +2495,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock _i8.ValueNotifier get debugShowWidgetInspectorOverrideNotifier => (super.noSuchMethod( Invocation.getter(#debugShowWidgetInspectorOverrideNotifier), - returnValue: _FakeValueNotifier_19( + returnValue: _FakeValueNotifier_18( this, Invocation.getter(#debugShowWidgetInspectorOverrideNotifier), ), @@ -2525,7 +2505,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock _i8.ValueNotifier get debugWidgetInspectorSelectionOnTapEnabled => (super.noSuchMethod( Invocation.getter(#debugWidgetInspectorSelectionOnTapEnabled), - returnValue: _FakeValueNotifier_19( + returnValue: _FakeValueNotifier_18( this, Invocation.getter(#debugWidgetInspectorSelectionOnTapEnabled), ), @@ -2534,7 +2514,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock @override _i9.FocusManager get focusManager => (super.noSuchMethod( Invocation.getter(#focusManager), - returnValue: _FakeFocusManager_30( + returnValue: _FakeFocusManager_29( this, Invocation.getter(#focusManager), ), @@ -2934,7 +2914,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock ), (T v) => _i11.Future.value(v), ) ?? - _FakeFuture_31( + _FakeFuture_30( this, Invocation.method( #scheduleTask, @@ -3124,7 +3104,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #createBinaryMessenger, [], ), - returnValue: _FakeBinaryMessenger_10( + returnValue: _FakeBinaryMessenger_9( this, Invocation.method( #createBinaryMessenger, @@ -3223,7 +3203,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #createRestorationManager, [], ), - returnValue: _FakeRestorationManager_23( + returnValue: _FakeRestorationManager_22( this, Invocation.method( #createRestorationManager, @@ -3258,7 +3238,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #createImageCache, [], ), - returnValue: _FakeImageCache_24( + returnValue: _FakeImageCache_23( this, Invocation.method( #createImageCache, @@ -3284,7 +3264,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #allowUpscaling: allowUpscaling, }, ), - returnValue: _i11.Future<_i6.Codec>.value(_FakeCodec_32( + returnValue: _i11.Future<_i6.Codec>.value(_FakeCodec_31( this, Invocation.method( #instantiateImageCodecFromBuffer, @@ -3309,7 +3289,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock [buffer], {#getTargetSize: getTargetSize}, ), - returnValue: _i11.Future<_i6.Codec>.value(_FakeCodec_32( + returnValue: _i11.Future<_i6.Codec>.value(_FakeCodec_31( this, Invocation.method( #instantiateImageCodecWithSize, @@ -3367,7 +3347,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #ensureSemantics, [], ), - returnValue: _FakeSemanticsHandle_33( + returnValue: _FakeSemanticsHandle_32( this, Invocation.method( #ensureSemantics, @@ -3402,7 +3382,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #createSemanticsUpdateBuilder, [], ), - returnValue: _FakeSemanticsUpdateBuilder_34( + returnValue: _FakeSemanticsUpdateBuilder_33( this, Invocation.method( #createSemanticsUpdateBuilder, @@ -3452,7 +3432,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #createViewConfigurationFor, [renderView], ), - returnValue: _FakeViewConfiguration_35( + returnValue: _FakeViewConfiguration_34( this, Invocation.method( #createViewConfigurationFor, @@ -3467,7 +3447,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #createSceneBuilder, [], ), - returnValue: _FakeSceneBuilder_36( + returnValue: _FakeSceneBuilder_35( this, Invocation.method( #createSceneBuilder, @@ -3482,7 +3462,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #createPictureRecorder, [], ), - returnValue: _FakePictureRecorder_37( + returnValue: _FakePictureRecorder_36( this, Invocation.method( #createPictureRecorder, @@ -3497,7 +3477,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #createCanvas, [recorder], ), - returnValue: _FakeCanvas_38( + returnValue: _FakeCanvas_37( this, Invocation.method( #createCanvas, @@ -3648,7 +3628,7 @@ class MockWidgetsFlutterBinding extends _i1.Mock #wrapWithDefaultView, [rootWidget], ), - returnValue: _FakeWidget_39( + returnValue: _FakeWidget_38( this, Invocation.method( #wrapWithDefaultView, @@ -3776,7 +3756,7 @@ class MockTimeToDisplayTracker extends _i1.Mock @override _i2.SentryFlutterOptions get options => (super.noSuchMethod( Invocation.getter(#options), - returnValue: _FakeSentryFlutterOptions_40( + returnValue: _FakeSentryFlutterOptions_39( this, Invocation.getter(#options), ), @@ -3954,7 +3934,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { @override _i2.SentryOptions get options => (super.noSuchMethod( Invocation.getter(#options), - returnValue: _FakeSentryOptions_41( + returnValue: _FakeSentryOptions_40( this, Invocation.getter(#options), ), @@ -3978,18 +3958,12 @@ class MockHub extends _i1.Mock implements _i2.Hub { @override _i2.Scope get scope => (super.noSuchMethod( Invocation.getter(#scope), - returnValue: _FakeScope_42( + returnValue: _FakeScope_41( this, Invocation.getter(#scope), ), ) as _i2.Scope); - @override - Map> get lifecycleCallbacks => (super.noSuchMethod( - Invocation.getter(#lifecycleCallbacks), - returnValue: >{}, - ) as Map>); - @override set profilerFactory(_i15.SentryProfilerFactory? value) => super.noSuchMethod( Invocation.setter( @@ -4166,7 +4140,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { #clone, [], ), - returnValue: _FakeHub_43( + returnValue: _FakeHub_42( this, Invocation.method( #clone, @@ -4334,26 +4308,4 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), returnValueForMissingStub: null, ); - - @override - void registerSdkLifecycleCallback( - _i2.SdkLifecycleCallback? callback) => - super.noSuchMethod( - Invocation.method( - #registerSdkLifecycleCallback, - [callback], - ), - returnValueForMissingStub: null, - ); - - @override - void removeSdkLifecycleCallback( - _i2.SdkLifecycleCallback? callback) => - super.noSuchMethod( - Invocation.method( - #removeSdkLifecycleCallback, - [callback], - ), - returnValueForMissingStub: null, - ); } diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index 152e3e0d8d..17f3c8c1e9 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -704,6 +704,29 @@ void main() { ); SentryFlutter.native = null; }); + + test('ThreadInfoIntegration is added', () async { + final sentryFlutterOptions = + defaultTestOptions(checker: MockRuntimeChecker()) + ..platform = MockPlatform.android() + ..methodChannel = native.channel; + + SentryFlutter.native = mockNativeBinding(); + await SentryFlutter.init( + (options) { + expect( + options.integrations.any((integration) => + integration.runtimeType.toString() == 'ThreadInfoIntegration'), + true, + reason: + 'ThreadInfoIntegration should be added when tracing is enabled', + ); + }, + appRunner: appRunner, + options: sentryFlutterOptions, + ); + SentryFlutter.native = null; + }); }); test('resumeAppHangTracking calls native method when available', () async {