Skip to content

Commit f55626a

Browse files
authored
Fix SentryAssetBundle on Flutter > 3.1 (#877)
1 parent b1f0ff6 commit f55626a

File tree

3 files changed

+110
-0
lines changed

3 files changed

+110
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44

5+
* Fix: Fix `SentryAssetBundle` on Flutter >= 3.1 (#877)
56
* Feat: Add Android thread to platform stacktraces (#853)
67
* Fix: Rename auto initialize property (#857)
78
* Bump: Sentry-Android to 6.0.0-beta.4 (#871)

flutter/lib/src/sentry_asset_bundle.dart

+47
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import 'dart:async';
2+
import 'dart:typed_data';
3+
import 'dart:ui';
24

35
import 'package:flutter/services.dart';
46
import 'package:flutter/material.dart';
@@ -143,6 +145,8 @@ class SentryAssetBundle implements AssetBundle {
143145
byteLength = data.length;
144146
} else if (data is ByteData) {
145147
byteLength = data.lengthInBytes;
148+
} else if (data is ImmutableBuffer) {
149+
byteLength = data.length;
146150
}
147151
if (byteLength != null) {
148152
span?.setData('file.size', byteLength);
@@ -177,6 +181,49 @@ class SentryAssetBundle implements AssetBundle {
177181
}
178182
}
179183

184+
@override
185+
// This is an override on Flutter greater than 3.1
186+
// ignore: override_on_non_overriding_member
187+
Future<ImmutableBuffer> loadBuffer(String key) async {
188+
final span = _hub.getSpan()?.startChild(
189+
'file.read',
190+
description: 'AssetBundle.loadBuffer: ${_fileName(key)}',
191+
);
192+
193+
span?.setData('file.path', key);
194+
195+
ImmutableBuffer data;
196+
try {
197+
data = await _loadBuffer(key);
198+
_setDataLength(data, span);
199+
span?.status = SpanStatus.ok();
200+
} catch (exception) {
201+
span?.throwable = exception;
202+
span?.status = SpanStatus.internalError();
203+
rethrow;
204+
} finally {
205+
await span?.finish();
206+
}
207+
return data;
208+
}
209+
210+
Future<ImmutableBuffer> _loadBuffer(String key) async {
211+
try {
212+
return (_bundle as dynamic).loadBuffer(key);
213+
} on NoSuchMethodError catch (_) {
214+
// The loadBuffer method exists as of Flutter greater than 3.1
215+
// Previous versions don't have it, but later versions do.
216+
// We can't use `extends` in order to provide this method because this is
217+
// a wrapper and thus the method call must be forwarded.
218+
// On Flutter versions <=3.1 we can't forward this call and
219+
// just catch the error which is thrown. On later version the call gets
220+
// correctly forwarded.
221+
//
222+
// In case of a NoSuchMethodError we just return an empty list
223+
return ImmutableBuffer.fromUint8List(Uint8List.fromList([]));
224+
}
225+
}
226+
180227
static Future<T> _wrapParsing<T>(
181228
_Parser<T> parser,
182229
String value,

flutter/test/sentry_asset_bundle_test.dart

+62
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The lint above is okay, because we're using another Sentry package
33
import 'dart:convert';
44
import 'dart:typed_data';
5+
import 'dart:ui';
56

67
import 'package:flutter/services.dart';
78
import 'package:flutter_test/flutter_test.dart';
@@ -137,6 +138,53 @@ void main() {
137138
expect(span.context.description, 'AssetBundle.loadString: test.txt');
138139
});
139140

141+
test('loadBuffer: creates a span if transaction is bound to scope',
142+
() async {
143+
final sut = fixture.getSut();
144+
final tr = fixture._hub.startTransaction(
145+
'name',
146+
'op',
147+
bindToScope: true,
148+
);
149+
150+
await sut.loadBuffer(_testFileName);
151+
152+
await tr.finish();
153+
154+
final tracer = (tr as SentryTracer);
155+
final span = tracer.children.first;
156+
157+
expect(span.status, SpanStatus.ok());
158+
expect(span.finished, true);
159+
expect(span.context.operation, 'file.read');
160+
expect(span.data['file.path'], 'resources/test.txt');
161+
expect(span.data['file.size'], 12);
162+
expect(span.context.description, 'AssetBundle.loadBuffer: test.txt');
163+
});
164+
165+
test('loadBuffer: end span with error if exception is thrown', () async {
166+
final sut = fixture.getSut(throwException: true);
167+
final tr = fixture._hub.startTransaction(
168+
'name',
169+
'op',
170+
bindToScope: true,
171+
);
172+
173+
try {
174+
await sut.loadBuffer(_testFileName);
175+
} catch (_) {}
176+
177+
await tr.finish();
178+
179+
final tracer = (tr as SentryTracer);
180+
final span = tracer.children.first;
181+
182+
expect(span.status, SpanStatus.internalError());
183+
expect(span.finished, true);
184+
expect(span.context.operation, 'file.read');
185+
expect(span.context.description, 'AssetBundle.loadBuffer: test.txt');
186+
});
187+
140188
test(
141189
'loadStructuredData: does not create any spans and just forwords the call to the underlying assetbundle if disabled',
142190
() async {
@@ -359,4 +407,18 @@ class TestAssetBundle extends CachingAssetBundle {
359407
super.evict(key);
360408
evictKey = key;
361409
}
410+
411+
@override
412+
// This is an override on Flutter greater than 3.1
413+
// ignore: override_on_non_overriding_member
414+
Future<ImmutableBuffer> loadBuffer(String key) async {
415+
if (throwException) {
416+
throw Exception('exception thrown for testing purposes');
417+
}
418+
if (key == _testFileName) {
419+
return ImmutableBuffer.fromUint8List(
420+
Uint8List.fromList(utf8.encode('Hello World!')));
421+
}
422+
return ImmutableBuffer.fromUint8List(Uint8List.fromList([]));
423+
}
362424
}

0 commit comments

Comments
 (0)