diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fafbc1ca9..d09937f81c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased +* Feat: Automatically create transactions when navigating between screens (#643) # 6.2.2 * Fix: ConcurrentModificationError in when finishing span (#664) diff --git a/dart/lib/src/hub.dart b/dart/lib/src/hub.dart index e533ef3501..b239e83bb7 100644 --- a/dart/lib/src/hub.dart +++ b/dart/lib/src/hub.dart @@ -3,7 +3,6 @@ import 'dart:collection'; import 'package:meta/meta.dart'; -import 'noop_sentry_span.dart'; import 'protocol.dart'; import 'scope.dart'; import 'sentry_client.dart'; @@ -340,6 +339,8 @@ class Hub { String operation, { String? description, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, Map? customSamplingContext, }) => startTransactionWithContext( @@ -349,6 +350,8 @@ class Hub { description: description, ), bindToScope: bindToScope, + waitForChildren: waitForChildren, + autoFinishAfter: autoFinishAfter, customSamplingContext: customSamplingContext, ); @@ -357,6 +360,8 @@ class Hub { SentryTransactionContext transactionContext, { Map? customSamplingContext, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, }) { if (!_isEnabled) { _options.logger( @@ -380,8 +385,12 @@ class Hub { transactionContext = transactionContext.copyWith(sampled: sampled); } - final tracer = SentryTracer(transactionContext, this); - + final tracer = SentryTracer( + transactionContext, + this, + waitForChildren: waitForChildren ?? false, + autoFinishAfter: autoFinishAfter, + ); if (bindToScope ?? false) { item.scope.span = tracer; } diff --git a/dart/lib/src/hub_adapter.dart b/dart/lib/src/hub_adapter.dart index 11eb8044bd..3f7803fc29 100644 --- a/dart/lib/src/hub_adapter.dart +++ b/dart/lib/src/hub_adapter.dart @@ -102,11 +102,15 @@ class HubAdapter implements Hub { SentryTransactionContext transactionContext, { Map? customSamplingContext, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, }) => Sentry.startTransactionWithContext( transactionContext, customSamplingContext: customSamplingContext, bindToScope: bindToScope, + waitForChildren: waitForChildren, + autoFinishAfter: autoFinishAfter, ); @override @@ -115,6 +119,8 @@ class HubAdapter implements Hub { String operation, { String? description, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, Map? customSamplingContext, }) => Sentry.startTransaction( @@ -122,6 +128,8 @@ class HubAdapter implements Hub { operation, description: description, bindToScope: bindToScope, + waitForChildren: waitForChildren, + autoFinishAfter: autoFinishAfter, customSamplingContext: customSamplingContext, ); diff --git a/dart/lib/src/noop_hub.dart b/dart/lib/src/noop_hub.dart index 06163bac20..9963771496 100644 --- a/dart/lib/src/noop_hub.dart +++ b/dart/lib/src/noop_hub.dart @@ -4,7 +4,6 @@ import 'protocol.dart'; import 'sentry_client.dart'; import 'sentry_user_feedback.dart'; import 'tracing.dart'; -import 'noop_sentry_span.dart'; class NoOpHub implements Hub { NoOpHub._(); @@ -79,6 +78,8 @@ class NoOpHub implements Hub { String operation, { String? description, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, Map? customSamplingContext, }) => NoOpSentrySpan(); @@ -88,6 +89,8 @@ class NoOpHub implements Hub { SentryTransactionContext transactionContext, { Map? customSamplingContext, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, }) => NoOpSentrySpan(); diff --git a/dart/lib/src/protocol/breadcrumb.dart b/dart/lib/src/protocol/breadcrumb.dart index e1ac8cd4fc..198942c2e4 100644 --- a/dart/lib/src/protocol/breadcrumb.dart +++ b/dart/lib/src/protocol/breadcrumb.dart @@ -1,7 +1,6 @@ import 'package:meta/meta.dart'; import '../utils.dart'; -import 'sentry_level.dart'; import '../protocol.dart'; /// Structed data to describe more information pior to the event captured. diff --git a/dart/lib/src/protocol/sentry_span.dart b/dart/lib/src/protocol/sentry_span.dart index abb0ed18a4..d262e85f30 100644 --- a/dart/lib/src/protocol/sentry_span.dart +++ b/dart/lib/src/protocol/sentry_span.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import '../hub.dart'; import '../protocol.dart'; @@ -17,6 +19,7 @@ class SentrySpan extends ISentrySpan { SpanStatus? _status; final Map _tags = {}; + void Function()? _finishedCallback; @override bool? sampled; @@ -26,8 +29,10 @@ class SentrySpan extends ISentrySpan { this._context, this._hub, { bool? sampled, + Function()? finishedCallback, }) { this.sampled = sampled; + _finishedCallback = finishedCallback; } @override @@ -45,6 +50,8 @@ class SentrySpan extends ISentrySpan { if (_throwable != null) { _hub.setSpanContext(_throwable, this, _tracer.name); } + _finishedCallback?.call(); + await super.finish(status: status); } @override diff --git a/dart/lib/src/protocol/sentry_transaction.dart b/dart/lib/src/protocol/sentry_transaction.dart index 2d1496198e..2bbdbfc7bb 100644 --- a/dart/lib/src/protocol/sentry_transaction.dart +++ b/dart/lib/src/protocol/sentry_transaction.dart @@ -2,7 +2,6 @@ import 'package:meta/meta.dart'; import '../protocol.dart'; import '../sentry_tracer.dart'; -import 'sentry_span.dart'; import '../utils.dart'; @immutable diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index 4ded2d49ce..3d1a74f02b 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -229,6 +229,8 @@ class Sentry { String operation, { String? description, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, Map? customSamplingContext, }) => _hub.startTransaction( @@ -236,6 +238,8 @@ class Sentry { operation, description: description, bindToScope: bindToScope, + waitForChildren: waitForChildren, + autoFinishAfter: autoFinishAfter, customSamplingContext: customSamplingContext, ); @@ -244,11 +248,15 @@ class Sentry { SentryTransactionContext transactionContext, { Map? customSamplingContext, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, }) => _hub.startTransactionWithContext( transactionContext, customSamplingContext: customSamplingContext, bindToScope: bindToScope, + waitForChildren: waitForChildren, + autoFinishAfter: autoFinishAfter, ); /// Gets the current active transaction or span. diff --git a/dart/lib/src/sentry_envelope.dart b/dart/lib/src/sentry_envelope.dart index ac93c54b07..7c38fd63f8 100644 --- a/dart/lib/src/sentry_envelope.dart +++ b/dart/lib/src/sentry_envelope.dart @@ -6,8 +6,6 @@ import 'utils.dart'; import 'sentry_attachment/sentry_attachment.dart'; import 'sentry_envelope_header.dart'; import 'sentry_envelope_item.dart'; -import 'protocol/sentry_event.dart'; -import 'protocol/sdk_version.dart'; import 'sentry_user_feedback.dart'; /// Class representation of `Envelope` file. diff --git a/dart/lib/src/sentry_envelope_item.dart b/dart/lib/src/sentry_envelope_item.dart index 69778046c9..b274334127 100644 --- a/dart/lib/src/sentry_envelope_item.dart +++ b/dart/lib/src/sentry_envelope_item.dart @@ -3,7 +3,6 @@ import 'protocol.dart'; import 'utils.dart'; import 'sentry_attachment/sentry_attachment.dart'; import 'sentry_item_type.dart'; -import 'protocol/sentry_event.dart'; import 'sentry_envelope_item_header.dart'; import 'sentry_user_feedback.dart'; diff --git a/dart/lib/src/sentry_span_interface.dart b/dart/lib/src/sentry_span_interface.dart index 4942fb736f..d59de3c0ed 100644 --- a/dart/lib/src/sentry_span_interface.dart +++ b/dart/lib/src/sentry_span_interface.dart @@ -24,9 +24,7 @@ abstract class ISentrySpan { void removeData(String key); /// Sets span timestamp marking this span as finished. - Future finish({ - SpanStatus? status, - }); + Future finish({SpanStatus? status}) async {} /// Gets span status. SpanStatus? get status; diff --git a/dart/lib/src/sentry_tracer.dart b/dart/lib/src/sentry_tracer.dart index 72880a4964..88988875e6 100644 --- a/dart/lib/src/sentry_tracer.dart +++ b/dart/lib/src/sentry_tracer.dart @@ -1,52 +1,65 @@ +import 'dart:async'; + import 'package:meta/meta.dart'; import '../sentry.dart'; +import 'sentry_tracer_finish_status.dart'; @internal class SentryTracer extends ISentrySpan { final Hub _hub; + late bool _waitForChildren; late String name; - // missing waitForChildren - late final SentrySpan _rootSpan; final List _children = []; final Map _extra = {}; + Timer? _autoFinishAfterTimer; + var _finishStatus = SentryTracerFinishStatus.notFinishing(); - SentryTracer(SentryTransactionContext transactionContext, this._hub) { + SentryTracer(SentryTransactionContext transactionContext, this._hub, + {bool waitForChildren = false, Duration? autoFinishAfter}) { _rootSpan = SentrySpan( this, transactionContext, _hub, sampled: transactionContext.sampled, ); + _waitForChildren = waitForChildren; + if (autoFinishAfter != null) { + _autoFinishAfterTimer = Timer(autoFinishAfter, () async { + await finish(status: status ?? SpanStatus.ok()); + }); + } name = transactionContext.name; } @override Future finish({SpanStatus? status}) async { - if (finished) { - return; - } - - await _rootSpan.finish(status: status); - - // finish unfinished spans otherwise transaction gets dropped - for (final span in _children) { - if (!span.finished) { - await span.finish(status: SpanStatus.deadlineExceeded()); + _autoFinishAfterTimer?.cancel(); + _finishStatus = SentryTracerFinishStatus.finishing(status); + if (!_rootSpan.finished && + (!_waitForChildren || _haveAllChildrenFinished())) { + _rootSpan.status ??= status; + await _rootSpan.finish(); + + // finish unfinished spans otherwise transaction gets dropped + for (final span in _children) { + if (!span.finished) { + await span.finish(status: SpanStatus.deadlineExceeded()); + } } - } - // remove from scope - _hub.configureScope((scope) { - if (scope.span == this) { - scope.span = null; - } - }); + // remove from scope + _hub.configureScope((scope) { + if (scope.span == this) { + scope.span = null; + } + }); - final transaction = SentryTransaction(this); - await _hub.captureTransaction(transaction); + final transaction = SentryTransaction(this); + await _hub.captureTransaction(transaction); + } } @override @@ -110,18 +123,18 @@ class SentryTracer extends ISentrySpan { } final context = SentrySpanContext( - traceId: _rootSpan.context.traceId, - parentSpanId: parentSpanId, - operation: operation, - description: description, - ); - - final child = SentrySpan( - this, - context, - _hub, - sampled: _rootSpan.sampled, - ); + traceId: _rootSpan.context.traceId, + parentSpanId: parentSpanId, + operation: operation, + description: description); + + final child = SentrySpan(this, context, _hub, sampled: _rootSpan.sampled, + finishedCallback: () { + final finishStatus = _finishStatus; + if (finishStatus.finishing) { + finish(status: finishStatus.status); + } + }); _children.add(child); @@ -163,4 +176,13 @@ class SentryTracer extends ISentrySpan { @override SentryTraceHeader toSentryTrace() => _rootSpan.toSentryTrace(); + + bool _haveAllChildrenFinished() { + for (final child in children) { + if (!child.finished) { + return false; + } + } + return true; + } } diff --git a/dart/lib/src/sentry_tracer_finish_status.dart b/dart/lib/src/sentry_tracer_finish_status.dart new file mode 100644 index 0000000000..a5eca97948 --- /dev/null +++ b/dart/lib/src/sentry_tracer_finish_status.dart @@ -0,0 +1,17 @@ +import 'package:meta/meta.dart'; + +import '../sentry.dart'; + +@internal +class SentryTracerFinishStatus { + final bool finishing; + final SpanStatus? status; + + SentryTracerFinishStatus.finishing(SpanStatus? status) + : finishing = true, + status = status; + + SentryTracerFinishStatus.notFinishing() + : finishing = false, + status = null; +} diff --git a/dart/lib/src/sentry_traces_sampler.dart b/dart/lib/src/sentry_traces_sampler.dart index 3501ff64cc..9d981cca04 100644 --- a/dart/lib/src/sentry_traces_sampler.dart +++ b/dart/lib/src/sentry_traces_sampler.dart @@ -3,7 +3,6 @@ import 'dart:math'; import 'package:meta/meta.dart'; import '../sentry.dart'; -import 'tracing.dart'; @internal class SentryTracesSampler { diff --git a/dart/test/contexts_test.dart b/dart/test/contexts_test.dart index 5bd97189d0..137795c716 100644 --- a/dart/test/contexts_test.dart +++ b/dart/test/contexts_test.dart @@ -6,7 +6,6 @@ import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry/src/protocol/sentry_gpu.dart'; import 'package:test/test.dart'; void main() { diff --git a/dart/test/default_integrations_test.dart b/dart/test/default_integrations_test.dart index 6ca1bb64ec..026025cb5e 100644 --- a/dart/test/default_integrations_test.dart +++ b/dart/test/default_integrations_test.dart @@ -1,5 +1,4 @@ import 'package:sentry/sentry.dart'; -import 'package:sentry/src/noop_sentry_span.dart'; import 'package:test/test.dart'; import 'mocks/mock_hub.dart'; @@ -186,9 +185,12 @@ class PrintRecursionMockHub extends MockHub { @override ISentrySpan startTransactionWithContext( - SentryTransactionContext transactionContext, - {Map? customSamplingContext, - bool? bindToScope}) { + SentryTransactionContext transactionContext, { + Map? customSamplingContext, + bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, + }) { return NoOpSentrySpan(); } } diff --git a/dart/test/http_client/failed_request_client_test.dart b/dart/test/http_client/failed_request_client_test.dart index 80fce80fc1..342a7f00f1 100644 --- a/dart/test/http_client/failed_request_client_test.dart +++ b/dart/test/http_client/failed_request_client_test.dart @@ -3,7 +3,6 @@ import 'package:http/testing.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry/sentry.dart'; import 'package:sentry/src/http_client/failed_request_client.dart'; -import 'package:sentry/src/http_client/sentry_http_client.dart'; import 'package:test/test.dart'; import '../mocks.dart'; diff --git a/dart/test/http_client/sentry_http_client_test.dart b/dart/test/http_client/sentry_http_client_test.dart index 390cc98561..697803b89c 100644 --- a/dart/test/http_client/sentry_http_client_test.dart +++ b/dart/test/http_client/sentry_http_client_test.dart @@ -3,7 +3,6 @@ import 'package:http/testing.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry/sentry.dart'; import 'package:sentry/src/http_client/failed_request_client.dart'; -import 'package:sentry/src/http_client/sentry_http_client.dart'; import 'package:test/test.dart'; import '../mocks/mock_hub.dart'; diff --git a/dart/test/hub_test.dart b/dart/test/hub_test.dart index 32bf3ab1d9..2da153bf04 100644 --- a/dart/test/hub_test.dart +++ b/dart/test/hub_test.dart @@ -1,7 +1,5 @@ import 'package:collection/collection.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry/src/hub.dart'; -import 'package:sentry/src/noop_sentry_span.dart'; import 'package:sentry/src/sentry_tracer.dart'; import 'package:test/test.dart'; diff --git a/dart/test/mocks.dart b/dart/test/mocks.dart index c9bd199512..cb36172d06 100644 --- a/dart/test/mocks.dart +++ b/dart/test/mocks.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:sentry/sentry.dart'; -import 'package:sentry/src/protocol.dart'; import 'package:sentry/src/transport/rate_limiter.dart'; final fakeDsn = 'https://abc@def.ingest.sentry.io/1234567'; diff --git a/dart/test/mocks/mock_hub.dart b/dart/test/mocks/mock_hub.dart index b0e672abbd..5fddcff7f7 100644 --- a/dart/test/mocks/mock_hub.dart +++ b/dart/test/mocks/mock_hub.dart @@ -1,7 +1,5 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/noop_hub.dart'; -import 'package:sentry/src/noop_sentry_span.dart'; -import 'package:sentry/src/sentry_user_feedback.dart'; class MockHub implements Hub { List captureEventCalls = []; @@ -127,6 +125,8 @@ class MockHub implements Hub { String operation, { String? description, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, Map? customSamplingContext, }) { return NoOpSentrySpan(); @@ -137,6 +137,8 @@ class MockHub implements Hub { SentryTransactionContext transactionContext, { Map? customSamplingContext, bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, }) { return NoOpSentrySpan(); } diff --git a/dart/test/mocks/mock_sentry_client.dart b/dart/test/mocks/mock_sentry_client.dart index f61e0f2360..8a9dbec36d 100644 --- a/dart/test/mocks/mock_sentry_client.dart +++ b/dart/test/mocks/mock_sentry_client.dart @@ -1,5 +1,4 @@ import 'package:sentry/sentry.dart'; -import 'package:sentry/src/sentry_envelope.dart'; class MockSentryClient implements SentryClient { List captureEventCalls = []; diff --git a/dart/test/scope_test.dart b/dart/test/scope_test.dart index a647ac0a1f..f01b4cf763 100644 --- a/dart/test/scope_test.dart +++ b/dart/test/scope_test.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry/src/noop_sentry_span.dart'; import 'package:sentry/src/sentry_tracer.dart'; import 'package:test/test.dart'; diff --git a/dart/test/sentry_envelope_item_test.dart b/dart/test/sentry_envelope_item_test.dart index 65b7f915f1..35c553c468 100644 --- a/dart/test/sentry_envelope_item_test.dart +++ b/dart/test/sentry_envelope_item_test.dart @@ -4,7 +4,6 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/sentry_envelope_item_header.dart'; import 'package:sentry/src/sentry_envelope_item.dart'; import 'package:sentry/src/sentry_item_type.dart'; -import 'package:sentry/src/protocol/sentry_id.dart'; import 'package:sentry/src/sentry_tracer.dart'; import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; diff --git a/dart/test/sentry_envelope_test.dart b/dart/test/sentry_envelope_test.dart index 5a090e398f..bc28c508de 100644 --- a/dart/test/sentry_envelope_test.dart +++ b/dart/test/sentry_envelope_test.dart @@ -2,12 +2,10 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:sentry/sentry.dart'; -import 'package:sentry/src/sentry_envelope.dart'; import 'package:sentry/src/sentry_envelope_header.dart'; import 'package:sentry/src/sentry_envelope_item_header.dart'; import 'package:sentry/src/sentry_envelope_item.dart'; import 'package:sentry/src/sentry_item_type.dart'; -import 'package:sentry/src/protocol/sentry_id.dart'; import 'package:sentry/src/sentry_tracer.dart'; import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; diff --git a/dart/test/sentry_envelope_vm_test.dart b/dart/test/sentry_envelope_vm_test.dart index d3395a1d8a..b1bd880f91 100644 --- a/dart/test/sentry_envelope_vm_test.dart +++ b/dart/test/sentry_envelope_vm_test.dart @@ -1,13 +1,10 @@ @TestOn('vm') import 'dart:io'; -import 'package:sentry/sentry.dart'; import 'package:sentry/sentry_io.dart'; -import 'package:sentry/src/sentry_envelope.dart'; import 'package:sentry/src/sentry_envelope_header.dart'; import 'package:sentry/src/sentry_envelope_item_header.dart'; import 'package:sentry/src/sentry_envelope_item.dart'; -import 'package:sentry/src/protocol/sentry_id.dart'; import 'package:test/test.dart'; void main() { diff --git a/dart/test/sentry_event_test.dart b/dart/test/sentry_event_test.dart index 2d57771254..2417ca5296 100644 --- a/dart/test/sentry_event_test.dart +++ b/dart/test/sentry_event_test.dart @@ -4,8 +4,6 @@ import 'package:collection/collection.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry/src/protocol/sentry_request.dart'; -import 'package:sentry/src/protocol/sentry_thread.dart'; import 'package:sentry/src/version.dart'; import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; diff --git a/dart/test/sentry_span_test.dart b/dart/test/sentry_span_test.dart index 90d9efe4b6..0a1b0f811a 100644 --- a/dart/test/sentry_span_test.dart +++ b/dart/test/sentry_span_test.dart @@ -1,6 +1,5 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/sentry_tracer.dart'; -import 'package:sentry/src/protocol/sentry_span.dart'; import 'package:test/test.dart'; import 'mocks/mock_hub.dart'; @@ -171,6 +170,17 @@ void main() { expect(NoOpSentrySpan(), span); }); + + test('callback called on finish', () async { + var numberOfCallbackCalls = 0; + final sut = fixture.getSut(finishedCallback: () { + numberOfCallbackCalls += 1; + }); + + await sut.finish(); + + expect(numberOfCallbackCalls, 1); + }); } class Fixture { @@ -181,7 +191,7 @@ class Fixture { late SentryTracer tracer; final hub = MockHub(); - SentrySpan getSut({bool? sampled = true}) { + SentrySpan getSut({bool? sampled = true, Function()? finishedCallback}) { tracer = SentryTracer(context, hub); return SentrySpan( @@ -189,6 +199,7 @@ class Fixture { context, hub, sampled: sampled, + finishedCallback: finishedCallback, ); } } diff --git a/dart/test/sentry_tracer_test.dart b/dart/test/sentry_tracer_test.dart index f58c84a1b9..39812f559c 100644 --- a/dart/test/sentry_tracer_test.dart +++ b/dart/test/sentry_tracer_test.dart @@ -170,6 +170,60 @@ void main() { expect(NoOpSentrySpan(), span); }); + + test('tracer finishes after auto finish duration', () async { + final sut = fixture.getSut(autoFinishAfter: Duration(milliseconds: 200)); + + expect(sut.finished, false); + await Future.delayed(Duration(milliseconds: 210)); + expect(sut.status, SpanStatus.ok()); + expect(sut.finished, true); + }); + + test('tracer finish needs child to finish', () async { + final sut = fixture.getSut(waitForChildren: true); + + final child = sut.startChild('operation', description: 'description'); + + await sut.finish(); + expect(sut.finished, false); + + await child.finish(); + expect(sut.finished, true); + }); + + test('tracer finish needs all children to finish', () async { + final sut = fixture.getSut(waitForChildren: true); + + final childA = sut.startChild('operation-a', description: 'description'); + final childB = sut.startChild('operation-b', description: 'description'); + + await sut.finish(); + expect(sut.finished, false); + + await childA.finish(); + expect(sut.finished, false); + + await childB.finish(); + expect(sut.finished, true); + }); + + test('tracer without finish will not be finished when children are finished', + () async { + final sut = fixture.getSut(waitForChildren: true); + + final childA = sut.startChild('operation-a', description: 'description'); + final childB = sut.startChild('operation-b', description: 'description'); + + await childA.finish(); + expect(sut.finished, false); + + await childB.finish(); + expect(sut.finished, false); + + await sut.finish(); + expect(sut.finished, true); + }); } class Fixture { @@ -177,6 +231,8 @@ class Fixture { SentryTracer getSut({ bool? sampled = true, + bool waitForChildren = false, + Duration? autoFinishAfter, }) { final context = SentryTransactionContext( 'name', @@ -186,6 +242,8 @@ class Fixture { return SentryTracer( context, hub, + waitForChildren: waitForChildren, + autoFinishAfter: autoFinishAfter, ); } } diff --git a/flutter/example/android/app/src/main/AndroidManifest.xml b/flutter/example/android/app/src/main/AndroidManifest.xml index da3c097241..c2029920e9 100644 --- a/flutter/example/android/app/src/main/AndroidManifest.xml +++ b/flutter/example/android/app/src/main/AndroidManifest.xml @@ -12,7 +12,8 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" - android:windowSoftInputMode="adjustResize"> + android:windowSoftInputMode="adjustResize" + android:exported="true">