Unverified Commit 9e314ff7 authored by Dan Field's avatar Dan Field Committed by GitHub

Make FutureBuilder handle SynchronousFuture correctly, reland...

Make FutureBuilder handle SynchronousFuture correctly, reland SynchronousFuture usage in test assets (#115173)

* Make FutureBuilder handle SynchronousFuture correctly

* Reland  "Load assets in flutter_test without turning event loop. (#115123)" (#115156)"

This reverts commit 3895786f.
parent 0e9ee367
...@@ -38,11 +38,11 @@ class SynchronousFuture<T> implements Future<T> { ...@@ -38,11 +38,11 @@ class SynchronousFuture<T> implements Future<T> {
@override @override
Future<R> then<R>(FutureOr<R> Function(T value) onValue, { Function? onError }) { 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>) { if (result is Future<R>) {
return result; return result;
} }
return SynchronousFuture<R>(result as R); return SynchronousFuture<R>(result);
} }
@override @override
......
...@@ -247,17 +247,24 @@ abstract class CachingAssetBundle extends AssetBundle { ...@@ -247,17 +247,24 @@ abstract class CachingAssetBundle extends AssetBundle {
/// An [AssetBundle] that loads resources using platform messages. /// An [AssetBundle] that loads resources using platform messages.
class PlatformAssetBundle extends CachingAssetBundle { class PlatformAssetBundle extends CachingAssetBundle {
@override @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 Uint8List encoded = utf8.encoder.convert(Uri(path: Uri.encodeFull(key)).path);
final ByteData? asset = final Future<ByteData>? future = ServicesBinding.instance.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData())?.then((ByteData? asset) {
await ServicesBinding.instance.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData()); if (asset == null) {
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>[ throw FlutterError.fromParts(<DiagnosticsNode>[
_errorSummaryWithKey(key), _errorSummaryWithKey(key),
ErrorDescription('The asset does not exist or has empty data.'), ErrorDescription('The asset does not exist or has empty data.'),
]); ]);
} }
return asset; return future;
} }
@override @override
......
...@@ -645,7 +645,11 @@ class _FutureBuilderState<T> extends State<FutureBuilder<T>> { ...@@ -645,7 +645,11 @@ class _FutureBuilderState<T> extends State<FutureBuilder<T>> {
}()); }());
}); });
_snapshot = _snapshot.inState(ConnectionState.waiting); // An implementation like `SynchronousFuture` may have already called the
// .then closure. Do not overwrite it in that case.
if (_snapshot.connectionState != ConnectionState.done) {
_snapshot = _snapshot.inState(ConnectionState.waiting);
}
} }
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -87,6 +88,20 @@ void main() { ...@@ -87,6 +88,20 @@ void main() {
}); });
}); });
group('FutureBuilder', () { group('FutureBuilder', () {
testWidgets('gives expected snapshot with SynchronousFuture', (WidgetTester tester) async {
final SynchronousFuture<String> future = SynchronousFuture<String>('flutter');
await tester.pumpWidget(FutureBuilder<String>(
future: future,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
expect(snapshot.connectionState, ConnectionState.done);
expect(snapshot.data, 'flutter');
expect(snapshot.error, null);
expect(snapshot.stackTrace, null);
return const Placeholder();
},
));
});
testWidgets('gracefully handles transition from null future', (WidgetTester tester) async { testWidgets('gracefully handles transition from null future', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey key = GlobalKey();
await tester.pumpWidget(FutureBuilder<String>( await tester.pumpWidget(FutureBuilder<String>(
......
...@@ -6,8 +6,8 @@ import 'dart:async'; ...@@ -6,8 +6,8 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
// ignore: deprecated_member_use // ignore: deprecated_member_use
import 'package:test_api/test_api.dart' as test_package; import 'package:test_api/test_api.dart' as test_package;
...@@ -42,7 +42,7 @@ void mockFlutterAssets() { ...@@ -42,7 +42,7 @@ void mockFlutterAssets() {
/// platform messages. /// platform messages.
SystemChannels.navigation.setMockMethodCallHandler((MethodCall methodCall) async {}); 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); assert(message != null);
String key = utf8.decode(message!.buffer.asUint8List()); String key = utf8.decode(message!.buffer.asUint8List());
File asset = File(path.join(assetFolderPath, key)); File asset = File(path.join(assetFolderPath, key));
...@@ -62,7 +62,7 @@ void mockFlutterAssets() { ...@@ -62,7 +62,7 @@ void mockFlutterAssets() {
} }
final Uint8List encoded = Uint8List.fromList(asset.readAsBytesSync()); final Uint8List encoded = Uint8List.fromList(asset.readAsBytesSync());
return Future<ByteData>.value(encoded.buffer.asByteData()); return SynchronousFuture<ByteData>(encoded.buffer.asByteData());
}); });
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -90,4 +91,13 @@ void main() { ...@@ -90,4 +91,13 @@ void main() {
binding.idle(); 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);
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment