Load assets in flutter_test without turning event loop. (#115123)

* Load assets in flutter_test without turning event loop.

This makes it possible to load an asset without actually turning
the event loop. This is importnat because our FakeAsync zones
may cause people to sprinkle in extra pumpAndSettles in when
running tests that load assets, which is undesirable.

* fix null checking
This commit is contained in:
Dan Field 2022-11-10 20:17:12 -08:00 committed by GitHub
parent 6e89042d9b
commit 88e49ed274
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 13 deletions

View File

@ -38,11 +38,11 @@ class SynchronousFuture<T> implements Future<T> {
@override
Future<R> then<R>(FutureOr<R> Function(T value) onValue, { Function? onError }) {
final dynamic result = onValue(_value);
final FutureOr<R> result = onValue(_value);
if (result is Future<R>) {
return result;
}
return SynchronousFuture<R>(result as R);
return SynchronousFuture<R>(result);
}
@override

View File

@ -247,17 +247,24 @@ abstract class CachingAssetBundle extends AssetBundle {
/// An [AssetBundle] that loads resources using platform messages.
class PlatformAssetBundle extends CachingAssetBundle {
@override
Future<ByteData> load(String key) async {
Future<ByteData> load(String key) {
final Uint8List encoded = utf8.encoder.convert(Uri(path: Uri.encodeFull(key)).path);
final ByteData? asset =
await ServicesBinding.instance.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData());
if (asset == null) {
final Future<ByteData>? future = ServicesBinding.instance.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData())?.then((ByteData? asset) {
if (asset == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
_errorSummaryWithKey(key),
ErrorDescription('The asset does not exist or has empty data.'),
]);
}
return asset;
});
if (future == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
_errorSummaryWithKey(key),
ErrorDescription('The asset does not exist or has empty data.'),
]);
_errorSummaryWithKey(key),
ErrorDescription('The asset does not exist or has empty data.'),
]);
}
return asset;
return future;
}
@override

View File

@ -6,8 +6,8 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart' as path;
// ignore: deprecated_member_use
import 'package:test_api/test_api.dart' as test_package;
@ -42,7 +42,7 @@ void mockFlutterAssets() {
/// platform messages.
SystemChannels.navigation.setMockMethodCallHandler((MethodCall methodCall) async {});
ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async {
ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) {
assert(message != null);
String key = utf8.decode(message!.buffer.asUint8List());
File asset = File(path.join(assetFolderPath, key));
@ -62,7 +62,7 @@ void mockFlutterAssets() {
}
final Uint8List encoded = Uint8List.fromList(asset.readAsBytesSync());
return Future<ByteData>.value(encoded.buffer.asByteData());
return SynchronousFuture<ByteData>(encoded.buffer.asByteData());
});
}

View File

@ -10,6 +10,7 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
@ -90,4 +91,13 @@ void main() {
binding.idle();
});
});
testWidgets('Assets in the tester can be loaded without turning event loop', (WidgetTester tester) async {
bool responded = false;
// The particular asset does not matter, as long as it exists.
rootBundle.load('AssetManifest.json').then((ByteData data) {
responded = true;
});
expect(responded, true);
});
}