Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

* Fix: Fix `SentryAssetBundle` on Flutter >= 3.1 (#877)
* Feat: Add Android thread to platform stacktraces (#853)
* Fix: Rename auto initialize property (#857)
* Bump: Sentry-Android to 6.0.0-beta.4 (#871)
Expand Down
47 changes: 47 additions & 0 deletions flutter/lib/src/sentry_asset_bundle.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';

import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -143,6 +145,8 @@ class SentryAssetBundle implements AssetBundle {
byteLength = data.length;
} else if (data is ByteData) {
byteLength = data.lengthInBytes;
} else if (data is ImmutableBuffer) {
byteLength = data.length;
}
if (byteLength != null) {
span?.setData('file.size', byteLength);
Expand Down Expand Up @@ -177,6 +181,49 @@ class SentryAssetBundle implements AssetBundle {
}
}

@override
// This is an override on Flutter 3.1 (I guess) and later
// ignore: override_on_non_overriding_member
Future<ImmutableBuffer> loadBuffer(String key) async {
final span = _hub.getSpan()?.startChild(
'file.read',
description: 'AssetBundle.loadBuffer: ${_fileName(key)}',
);

span?.setData('file.path', key);

ImmutableBuffer data;
try {
data = await _loadBuffer(key);
_setDataLength(data, span);
span?.status = SpanStatus.ok();
} catch (exception) {
span?.throwable = exception;
span?.status = SpanStatus.internalError();
rethrow;
} finally {
await span?.finish();
}
return data;
}

Future<ImmutableBuffer> _loadBuffer(String key) async {
try {
return (_bundle as dynamic).loadBuffer(key);
} on NoSuchMethodError catch (_) {
// The loadBuffer method exists as of Flutter 3.1
// Previous versions don't have it, but later versions do.
// We can't use `extends` in order to provide this method because this is
// a wrapper and thus the method call must be forwarded.
// On Flutter versions <=3.1 we can't forward this call and
// just catch the error which is thrown. On later version the call gets
// correctly forwarded.
//
// In case of a NoSuchMethodError we just return an empty list
return ImmutableBuffer.fromUint8List(Uint8List.fromList([]));
}
}

static Future<T> _wrapParsing<T>(
_Parser<T> parser,
String value,
Expand Down
62 changes: 62 additions & 0 deletions flutter/test/sentry_asset_bundle_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The lint above is okay, because we're using another Sentry package
import 'dart:convert';
import 'dart:typed_data';
import 'dart:ui';

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

test('loadBuffer: creates a span if transaction is bound to scope',
() async {
final sut = fixture.getSut();
final tr = fixture._hub.startTransaction(
'name',
'op',
bindToScope: true,
);

await sut.loadBuffer(_testFileName);

await tr.finish();

final tracer = (tr as SentryTracer);
final span = tracer.children.first;

expect(span.status, SpanStatus.ok());
expect(span.finished, true);
expect(span.context.operation, 'file.read');
expect(span.data['file.path'], 'resources/test.txt');
expect(span.data['file.size'], 12);
expect(span.context.description, 'AssetBundle.loadBuffer: test.txt');
});

test('loadBuffer: end span with error if exception is thrown', () async {
final sut = fixture.getSut(throwException: true);
final tr = fixture._hub.startTransaction(
'name',
'op',
bindToScope: true,
);

try {
await sut.loadBuffer(_testFileName);
} catch (_) {}

await tr.finish();

final tracer = (tr as SentryTracer);
final span = tracer.children.first;

expect(span.status, SpanStatus.internalError());
expect(span.finished, true);
expect(span.context.operation, 'file.read');
expect(span.context.description, 'AssetBundle.loadBuffer: test.txt');
});

test(
'loadStructuredData: does not create any spans and just forwords the call to the underlying assetbundle if disabled',
() async {
Expand Down Expand Up @@ -359,4 +407,18 @@ class TestAssetBundle extends CachingAssetBundle {
super.evict(key);
evictKey = key;
}

@override
// This is an override on Flutter 3.1 (I guess) and later
// ignore: override_on_non_overriding_member
Future<ImmutableBuffer> loadBuffer(String key) async {
if (throwException) {
throw Exception('exception thrown for testing purposes');
}
if (key == _testFileName) {
return ImmutableBuffer.fromUint8List(
Uint8List.fromList(utf8.encode('Hello World!')));
}
return ImmutableBuffer.fromUint8List(Uint8List.fromList([]));
}
}