From c02d53fc0eaf23c44f0ebca5fcaae50604de676a Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Sat, 10 Dec 2022 21:17:19 -0800 Subject: [PATCH] More gracefully handle license loading failures (#87841) --- packages/flutter/lib/src/material/about.dart | 11 +++++++++++ packages/flutter/test/material/about_test.dart | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/packages/flutter/lib/src/material/about.dart b/packages/flutter/lib/src/material/about.dart index 1bab4e8a1a0..b6ea5f91020 100644 --- a/packages/flutter/lib/src/material/about.dart +++ b/packages/flutter/lib/src/material/about.dart @@ -571,6 +571,17 @@ class _PackagesViewState extends State<_PackagesView> { builder: (BuildContext context, BoxConstraints constraints) { switch (snapshot.connectionState) { case ConnectionState.done: + if (snapshot.hasError) { + assert(() { + FlutterError.reportError(FlutterErrorDetails( + exception: snapshot.error!, + stack: snapshot.stackTrace, + context: ErrorDescription('while decoding the license file'), + )); + return true; + }()); + return Center(child: Text(snapshot.error.toString())); + } _initDefaultDetailPage(snapshot.data!, context); return ValueListenableBuilder( valueListenable: widget.selectedId, diff --git a/packages/flutter/test/material/about_test.dart b/packages/flutter/test/material/about_test.dart index 698dbc4427f..20e20517ce2 100644 --- a/packages/flutter/test/material/about_test.dart +++ b/packages/flutter/test/material/about_test.dart @@ -997,6 +997,24 @@ void main() { final double appIconBottomPadding = tester.getTopLeft(appPowered).dy - tester.getBottomLeft(appIcon).dy; expect(appIconBottomPadding, 18.0); }); + + testWidgets('Error handling test', (WidgetTester tester) async { + LicenseRegistry.addLicense(() => Stream.error(Exception('Injected failure'))); + await tester.pumpWidget(const MaterialApp(home: Material(child: AboutListTile()))); + await tester.tap(find.byType(ListTile)); + await tester.pump(); + await tester.pump(const Duration(seconds: 2)); + await tester.tap(find.text('VIEW LICENSES')); + await tester.pump(); + await tester.pump(const Duration(seconds: 2)); + final Finder finder = find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_PackagesView'); + // force the stream to complete (has to be done in a runAsync block since it's areal async process) + await tester.runAsync(() => (tester.firstState(finder) as dynamic).licenses as Future); // ignore: avoid_dynamic_calls + expect(tester.takeException().toString(), 'Exception: Injected failure'); + await tester.pumpAndSettle(); + expect(tester.takeException().toString(), 'Exception: Injected failure'); + expect(find.text('Exception: Injected failure'), findsOneWidget); + }); } class FakeLicenseEntry extends LicenseEntry {