Skip to content

Commit 7a748b3

Browse files
committed
Update tests
1 parent f535386 commit 7a748b3

16 files changed

+267
-167
lines changed

dart/lib/src/sentry_span_operations.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ class SentrySpanOperations {
55
static const String uiLoad = 'ui.load';
66
static const String uiTimeToInitialDisplay = 'ui.load.initial_display';
77
static const String uiTimeToFullDisplay = 'ui.load.full_display';
8-
}
8+
}

dart/lib/src/sentry_tracer.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ class SentryTracer extends ISentrySpan {
117117
for (var child in children) {
118118
final childEndTimestamp = child.endTimestamp;
119119
if (childEndTimestamp != null) {
120-
if (latestEndTime == null || childEndTimestamp.isAfter(latestEndTime)) {
120+
if (latestEndTime == null ||
121+
childEndTimestamp.isAfter(latestEndTime)) {
121122
latestEndTime = child.endTimestamp;
122123
}
123124
}

flutter/lib/src/frame_callback_handler.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import 'package:flutter/scheduler.dart';
22
import 'package:flutter/widgets.dart';
33

4-
abstract class IFrameCallbackHandler {
4+
abstract class FrameCallbackHandler {
55
void addPostFrameCallback(FrameCallback callback, {String debugLabel});
66
}
77

8-
class DefaultFrameCallbackHandler implements IFrameCallbackHandler {
8+
class DefaultFrameCallbackHandler implements FrameCallbackHandler {
99
@override
1010
void addPostFrameCallback(FrameCallback callback,
1111
{String debugLabel = 'callback'}) {

flutter/lib/src/integrations/app_start/app_start_tracker.dart

-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class AppStartTracker {
2727
void setAppStartInfo(AppStartInfo? appStartInfo) {
2828
_appStartInfo = appStartInfo;
2929
if (!_appStartCompleter.isCompleted) {
30-
3130
// Complete the completer with the app start info when it becomes available
3231
_appStartCompleter.complete(appStartInfo);
3332
} else {

flutter/lib/src/navigation/sentry_display_widget.dart

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import '../frame_callback_handler.dart';
66

77
class SentryDisplayWidget extends StatefulWidget {
88
final Widget child;
9-
final IFrameCallbackHandler _frameCallbackHandler;
9+
final FrameCallbackHandler _frameCallbackHandler;
1010

1111
SentryDisplayWidget({
1212
super.key,
1313
required this.child,
14-
IFrameCallbackHandler? frameCallbackHandler,
15-
}) : _frameCallbackHandler = frameCallbackHandler ?? DefaultFrameCallbackHandler();
14+
FrameCallbackHandler? frameCallbackHandler,
15+
}) : _frameCallbackHandler =
16+
frameCallbackHandler ?? DefaultFrameCallbackHandler();
1617

1718
@override
1819
_SentryDisplayWidgetState createState() => _SentryDisplayWidgetState();

flutter/lib/src/navigation/time_to_display_tracker.dart

+4-4
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,8 @@ class TimeToDisplayTracker {
6161

6262
if (appStartInfo == null || name == null) return;
6363

64-
final transaction = await _ttdTransactionHandler.startTransaction(
65-
name, arguments,
66-
startTimestamp: appStartInfo.start);
64+
final transaction = await _ttdTransactionHandler
65+
.startTransaction(name, arguments, startTimestamp: appStartInfo.start);
6766
if (transaction == null) return;
6867

6968
await _ttidTracker.trackAppStart(transaction, appStartInfo, name);
@@ -81,7 +80,8 @@ class TimeToDisplayTracker {
8180

8281
if (transaction == null || routeName == null) return;
8382

84-
await _ttidTracker.trackRegularRoute(transaction, startTimestamp, routeName);
83+
await _ttidTracker.trackRegularRoute(
84+
transaction, startTimestamp, routeName);
8585

8686
if (_enableTimeToFullDisplayTracing) {
8787
_ttfdTracker.startTracking(transaction, startTimestamp, routeName);

flutter/lib/src/navigation/time_to_full_display_tracker.dart

+41-24
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,28 @@ import '../../sentry_flutter.dart';
44
import '../sentry_flutter_measurement.dart';
55
import 'time_to_initial_display_tracker.dart';
66

7+
abstract class EndTimestampProvider {
8+
DateTime? get endTimestamp;
9+
}
10+
11+
class TTIDEndTimestampProvider implements EndTimestampProvider {
12+
@override
13+
DateTime? get endTimestamp => TimeToInitialDisplayTracker().endTimestamp;
14+
}
15+
716
class TimeToFullDisplayTracker {
8-
static final TimeToFullDisplayTracker _singleton =
17+
static final TimeToFullDisplayTracker _instance =
918
TimeToFullDisplayTracker._internal();
1019

11-
factory TimeToFullDisplayTracker() {
12-
return _singleton;
20+
factory TimeToFullDisplayTracker(
21+
{EndTimestampProvider? endTimestampProvider, Duration? autoFinishAfter}) {
22+
if (autoFinishAfter != null) {
23+
_instance._autoFinishAfter = autoFinishAfter;
24+
}
25+
if (endTimestampProvider != null) {
26+
_instance._endTimestampProvider = endTimestampProvider;
27+
}
28+
return _instance;
1329
}
1430

1531
TimeToFullDisplayTracker._internal();
@@ -18,23 +34,8 @@ class TimeToFullDisplayTracker {
1834
ISentrySpan? _ttfdSpan;
1935
Timer? _ttfdTimer;
2036
ISentrySpan? _transaction;
21-
Duration ttfdAutoFinishAfter = const Duration(seconds: 30);
22-
23-
Future<void> reportFullyDisplayed() async {
24-
_ttfdTimer?.cancel();
25-
final endTimestamp = DateTime.now();
26-
final startTimestamp = _startTimestamp;
27-
final ttfdSpan = _ttfdSpan;
28-
29-
if (ttfdSpan == null ||
30-
ttfdSpan.finished == true ||
31-
startTimestamp == null) {
32-
return;
33-
}
34-
35-
_setTTFDMeasurement(startTimestamp, endTimestamp);
36-
return ttfdSpan.finish(endTimestamp: endTimestamp);
37-
}
37+
Duration _autoFinishAfter = const Duration(seconds: 30);
38+
EndTimestampProvider _endTimestampProvider = TTIDEndTimestampProvider();
3839

3940
void startTracking(
4041
ISentrySpan transaction, DateTime startTimestamp, String routeName) {
@@ -43,7 +44,7 @@ class TimeToFullDisplayTracker {
4344
_ttfdSpan = transaction.startChild(SentrySpanOperations.uiTimeToFullDisplay,
4445
description: '$routeName full display', startTimestamp: startTimestamp);
4546
_ttfdSpan?.origin = SentryTraceOrigins.manualUiTimeToDisplay;
46-
_ttfdTimer = Timer(ttfdAutoFinishAfter, handleTimeToFullDisplayTimeout);
47+
_ttfdTimer = Timer(_autoFinishAfter, handleTimeToFullDisplayTimeout);
4748
}
4849

4950
void handleTimeToFullDisplayTimeout() {
@@ -56,15 +57,31 @@ class TimeToFullDisplayTracker {
5657
}
5758

5859
// If for some reason we can't get the ttid end timestamp
59-
// we'll use the start timestamp + autoFinishTime as a fallback
60-
final endTimestamp = TimeToInitialDisplayTracker().endTimestamp ??
61-
startTimestamp.add(ttfdAutoFinishAfter);
60+
// we'll use the start timestamp + autoFinishAfter as a fallback
61+
final endTimestamp = _endTimestampProvider.endTimestamp ??
62+
startTimestamp.add(_autoFinishAfter);
6263

6364
_setTTFDMeasurement(startTimestamp, endTimestamp);
6465
ttfdSpan.finish(
6566
status: SpanStatus.deadlineExceeded(), endTimestamp: endTimestamp);
6667
}
6768

69+
Future<void> reportFullyDisplayed() async {
70+
_ttfdTimer?.cancel();
71+
final endTimestamp = DateTime.now();
72+
final startTimestamp = _startTimestamp;
73+
final ttfdSpan = _ttfdSpan;
74+
75+
if (ttfdSpan == null ||
76+
ttfdSpan.finished == true ||
77+
startTimestamp == null) {
78+
return;
79+
}
80+
81+
_setTTFDMeasurement(startTimestamp, endTimestamp);
82+
return ttfdSpan.finish(endTimestamp: endTimestamp);
83+
}
84+
6885
void _setTTFDMeasurement(DateTime startTimestamp, DateTime endTimestamp) {
6986
final duration = endTimestamp.difference(startTimestamp);
7087
final measurement = SentryFlutterMeasurement.timeToFullDisplay(duration);

flutter/lib/src/navigation/time_to_initial_display_tracker.dart

+34-11
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,18 @@ import '../sentry_flutter_measurement.dart';
1414
class TimeToInitialDisplayTracker {
1515
static final TimeToInitialDisplayTracker _instance =
1616
TimeToInitialDisplayTracker._internal();
17-
factory TimeToInitialDisplayTracker() => _instance;
17+
18+
factory TimeToInitialDisplayTracker(
19+
{FrameCallbackHandler? frameCallbackHandler}) {
20+
if (frameCallbackHandler != null) {
21+
_instance._frameCallbackHandler = frameCallbackHandler;
22+
}
23+
return _instance;
24+
}
25+
1826
TimeToInitialDisplayTracker._internal();
1927

20-
IFrameCallbackHandler frameCallbackHandler = DefaultFrameCallbackHandler();
28+
FrameCallbackHandler _frameCallbackHandler = DefaultFrameCallbackHandler();
2129
bool _isManual = false;
2230
Completer<DateTime>? _trackingCompleter;
2331
DateTime? _endTimestamp;
@@ -26,7 +34,8 @@ class TimeToInitialDisplayTracker {
2634
@internal
2735
DateTime? get endTimestamp => _endTimestamp;
2836

29-
Future<void> trackRegularRoute(ISentrySpan transaction, DateTime startTimestamp, String routeName) async {
37+
Future<void> trackRegularRoute(ISentrySpan transaction,
38+
DateTime startTimestamp, String routeName) async {
3039
final endTimestamp = await determineEndTime();
3140
if (endTimestamp == null) return;
3241

@@ -41,19 +50,20 @@ class TimeToInitialDisplayTracker {
4150
ttidSpan.origin = SentryTraceOrigins.autoUiTimeToDisplay;
4251
}
4352

44-
// Reset after completion
45-
_isManual = false;
46-
4753
final ttidMeasurement = SentryFlutterMeasurement.timeToInitialDisplay(
4854
Duration(
4955
milliseconds:
50-
endTimestamp.difference(startTimestamp).inMilliseconds));
56+
endTimestamp.difference(startTimestamp).inMilliseconds));
5157
transaction.setMeasurement(ttidMeasurement.name, ttidMeasurement.value,
5258
unit: ttidMeasurement.unit);
53-
return ttidSpan.finish(endTimestamp: endTimestamp);
59+
await ttidSpan.finish(endTimestamp: endTimestamp);
60+
61+
// We can clear the state after creating and finishing the ttid span has finished
62+
clear();
5463
}
5564

56-
Future<void> trackAppStart(ISentrySpan transaction, AppStartInfo appStartInfo, String routeName) async {
65+
Future<void> trackAppStart(ISentrySpan transaction, AppStartInfo appStartInfo,
66+
String routeName) async {
5767
final ttidSpan = transaction.startChild(
5868
SentrySpanOperations.uiTimeToInitialDisplay,
5969
description: '$routeName initial display',
@@ -68,7 +78,8 @@ class TimeToInitialDisplayTracker {
6878
final ttidMeasurement = SentryFlutterMeasurement.timeToInitialDisplay(
6979
Duration(milliseconds: appStartInfo.measurement.value.toInt()),
7080
);
71-
transaction.setMeasurement(ttidMeasurement.name, ttidMeasurement.value, unit: ttidMeasurement.unit);
81+
transaction.setMeasurement(ttidMeasurement.name, ttidMeasurement.value,
82+
unit: ttidMeasurement.unit);
7283

7384
// Since app start measurement is immediate, finish the TTID span with the app start's end timestamp
7485
await ttidSpan.finish(endTimestamp: appStartInfo.end);
@@ -80,9 +91,14 @@ class TimeToInitialDisplayTracker {
8091
Future<DateTime>? determineEndTime() {
8192
_trackingCompleter = Completer<DateTime>();
8293

94+
// If we already know it's manual we can return the future immediately
95+
if (_isManual) {
96+
return _trackingCompleter?.future;
97+
}
98+
8399
// Schedules a check at the end of the frame to determine if the tracking
84100
// should be completed immediately (approximation mode) or deferred (manual mode).
85-
frameCallbackHandler.addPostFrameCallback((_) {
101+
_frameCallbackHandler.addPostFrameCallback((_) {
86102
if (!_isManual) {
87103
completeTracking();
88104
}
@@ -103,4 +119,11 @@ class TimeToInitialDisplayTracker {
103119
_trackingCompleter?.complete(endTimestamp);
104120
}
105121
}
122+
123+
void clear() {
124+
_isManual = false;
125+
_trackingCompleter = null;
126+
// We can't clear the ttid end time stamp here, because it might be needed
127+
// in the [TimeToFullDisplayTracker] class
128+
}
106129
}

flutter/lib/src/sentry_flutter.dart

+13-8
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ typedef FlutterOptionsConfiguration = FutureOr<void> Function(
3434
mixin SentryFlutter {
3535
static const _channel = MethodChannel('sentry_flutter');
3636

37-
static Future<void> init(FlutterOptionsConfiguration optionsConfiguration, {
37+
static Future<void> init(
38+
FlutterOptionsConfiguration optionsConfiguration, {
3839
AppRunner? appRunner,
3940
@internal MethodChannel channel = _channel,
4041
@internal PlatformChecker? platformChecker,
@@ -82,7 +83,7 @@ mixin SentryFlutter {
8283
await _initDefaultValues(flutterOptions, channel);
8384

8485
await Sentry.init(
85-
(options) => optionsConfiguration(options as SentryFlutterOptions),
86+
(options) => optionsConfiguration(options as SentryFlutterOptions),
8687
appRunner: appRunner,
8788
// ignore: invalid_use_of_internal_member
8889
options: flutterOptions,
@@ -98,8 +99,10 @@ mixin SentryFlutter {
9899
}
99100
}
100101

101-
static Future<void> _initDefaultValues(SentryFlutterOptions options,
102-
MethodChannel channel,) async {
102+
static Future<void> _initDefaultValues(
103+
SentryFlutterOptions options,
104+
MethodChannel channel,
105+
) async {
103106
options.addEventProcessor(FlutterExceptionEventProcessor());
104107

105108
// Not all platforms have a native integration.
@@ -123,9 +126,11 @@ mixin SentryFlutter {
123126

124127
/// Install default integrations
125128
/// https://medium.com/flutter-community/error-handling-in-flutter-98fce88a34f0
126-
static List<Integration> _createDefaultIntegrations(MethodChannel channel,
127-
SentryFlutterOptions options,
128-
bool isOnErrorSupported,) {
129+
static List<Integration> _createDefaultIntegrations(
130+
MethodChannel channel,
131+
SentryFlutterOptions options,
132+
bool isOnErrorSupported,
133+
) {
129134
final integrations = <Integration>[];
130135
final platformChecker = options.platformChecker;
131136
final platform = platformChecker.platform;
@@ -185,7 +190,7 @@ mixin SentryFlutter {
185190
if (_native != null) {
186191
integrations.add(NativeAppStartIntegration(
187192
_native!,
188-
() {
193+
() {
189194
try {
190195
/// Flutter >= 2.12 throws if SchedulerBinding.instance isn't initialized.
191196
return SchedulerBinding.instance;

flutter/test/fake_frame_callback_handler.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:flutter/scheduler.dart';
22
import 'package:sentry_flutter/src/frame_callback_handler.dart';
33

4-
class FakeFrameCallbackHandler implements IFrameCallbackHandler {
4+
class FakeFrameCallbackHandler implements FrameCallbackHandler {
55
FrameCallback? storedCallback;
66

77
final Duration _finishAfterDuration;

flutter/test/mocks.dart

-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import 'package:sentry/src/sentry_tracer.dart';
99

1010
import 'package:meta/meta.dart';
1111
import 'package:sentry_flutter/sentry_flutter.dart';
12-
import 'package:sentry_flutter/src/frame_callback_handler.dart';
1312
import 'package:sentry_flutter/src/renderer/renderer.dart';
1413
import 'package:sentry_flutter/src/native/sentry_native.dart';
1514
import 'package:sentry_flutter/src/native/sentry_native_binding.dart';
@@ -48,7 +47,6 @@ ISentrySpan startTransactionShim(
4847
SentryTracer,
4948
SentryTransaction,
5049
MethodChannel,
51-
IFrameCallbackHandler,
5250
], customMocks: [
5351
MockSpec<Hub>(fallbackGenerators: {#startTransaction: startTransactionShim})
5452
])

0 commit comments

Comments
 (0)