Unverified Commit d35671c6 authored by chunhtai's avatar chunhtai Committed by GitHub

Fix FlutterError.onError in debug mode (#53843)

* Fix FlutterError.onError in debug mode

* update

* fix comments

* add license header

* fix analyzer

* update

* another attempt

* fix test

* fix comment
parent 900c7c16
...@@ -715,7 +715,7 @@ class FlutterError extends Error with DiagnosticableTreeMixin implements Asserti ...@@ -715,7 +715,7 @@ class FlutterError extends Error with DiagnosticableTreeMixin implements Asserti
/// Called whenever the Flutter framework catches an error. /// Called whenever the Flutter framework catches an error.
/// ///
/// The default behavior is to call [dumpErrorToConsole]. /// The default behavior is to call [presentError].
/// ///
/// You can set this to your own function to override this default behavior. /// You can set this to your own function to override this default behavior.
/// For example, you could report all errors to your server. /// For example, you could report all errors to your server.
...@@ -725,7 +725,18 @@ class FlutterError extends Error with DiagnosticableTreeMixin implements Asserti ...@@ -725,7 +725,18 @@ class FlutterError extends Error with DiagnosticableTreeMixin implements Asserti
/// ///
/// Set this to null to silently catch and ignore errors. This is not /// Set this to null to silently catch and ignore errors. This is not
/// recommended. /// recommended.
static FlutterExceptionHandler onError = dumpErrorToConsole; static FlutterExceptionHandler onError = (FlutterErrorDetails details) => presentError(details);
/// Called whenever the Flutter framework wants to present an error to the
/// users.
///
/// The default behavior is to call [dumpErrorToConsole].
///
/// Plugins can override how an error is to be presented to the user. For
/// example, the structured errors service extension sets its own method when
/// the extension is enabled. If you want to change how Flutter responds to an
/// error, use [onError] instead.
static FlutterExceptionHandler presentError = dumpErrorToConsole;
static int _errorCount = 0; static int _errorCount = 0;
......
...@@ -959,13 +959,13 @@ mixin WidgetInspectorService { ...@@ -959,13 +959,13 @@ mixin WidgetInspectorService {
SchedulerBinding.instance.addPersistentFrameCallback(_onFrameStart); SchedulerBinding.instance.addPersistentFrameCallback(_onFrameStart);
final FlutterExceptionHandler structuredExceptionHandler = _reportError; final FlutterExceptionHandler structuredExceptionHandler = _reportError;
final FlutterExceptionHandler defaultExceptionHandler = FlutterError.onError; final FlutterExceptionHandler defaultExceptionHandler = FlutterError.presentError;
_registerBoolServiceExtension( _registerBoolServiceExtension(
name: 'structuredErrors', name: 'structuredErrors',
getter: () async => FlutterError.onError == structuredExceptionHandler, getter: () async => FlutterError.presentError == structuredExceptionHandler,
setter: (bool value) { setter: (bool value) {
FlutterError.onError = value ? structuredExceptionHandler : defaultExceptionHandler; FlutterError.presentError = value ? structuredExceptionHandler : defaultExceptionHandler;
return Future<void>.value(); return Future<void>.value();
}, },
); );
......
...@@ -1840,7 +1840,7 @@ void main() { ...@@ -1840,7 +1840,7 @@ void main() {
final GlobalKey<ScaffoldState> key = GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> key = GlobalKey<ScaffoldState>();
const Key buttonKey = Key('button'); const Key buttonKey = Key('button');
final List<FlutterErrorDetails> errors = <FlutterErrorDetails>[]; final List<FlutterErrorDetails> errors = <FlutterErrorDetails>[];
FlutterError.onError = (FlutterErrorDetails error) => errors.add(error); FlutterError.presentError = (FlutterErrorDetails error) => errors.add(error);
int state = 0; int state = 0;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
......
...@@ -528,6 +528,7 @@ void main() { ...@@ -528,6 +528,7 @@ void main() {
final ImageListener listener = (ImageInfo info, bool synchronous) { final ImageListener listener = (ImageInfo info, bool synchronous) {
capturedImage = info; capturedImage = info;
}; };
final FlutterExceptionHandler oldHandler = FlutterError.onError;
FlutterError.onError = (FlutterErrorDetails flutterError) { FlutterError.onError = (FlutterErrorDetails flutterError) {
reportedException = flutterError.exception; reportedException = flutterError.exception;
reportedStackTrace = flutterError.stack; reportedStackTrace = flutterError.stack;
...@@ -564,6 +565,7 @@ void main() { ...@@ -564,6 +565,7 @@ void main() {
// The image stream error handler should have the original exception. // The image stream error handler should have the original exception.
expect(capturedException, testException); expect(capturedException, testException);
expect(capturedStackTrace, testStack); expect(capturedStackTrace, testStack);
FlutterError.onError = oldHandler;
}); });
testWidgets('Duplicate listener registration does not affect error listeners', (WidgetTester tester) async { testWidgets('Duplicate listener registration does not affect error listeners', (WidgetTester tester) async {
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
StructureErrorTestWidgetInspectorService.runTests();
}
typedef InspectorServiceExtensionCallback = FutureOr<Map<String, Object>> Function(Map<String, String> parameters);
class StructureErrorTestWidgetInspectorService extends Object with WidgetInspectorService {
final Map<String, InspectorServiceExtensionCallback> extensions = <String, InspectorServiceExtensionCallback>{};
final Map<String, List<Map<Object, Object>>> eventsDispatched = <String, List<Map<Object, Object>>>{};
@override
void registerServiceExtension({
@required String name,
@required FutureOr<Map<String, Object>> callback(Map<String, String> parameters),
}) {
assert(!extensions.containsKey(name));
extensions[name] = callback;
}
@override
void postEvent(String eventKind, Map<Object, Object> eventData) {
getEventsDispatched(eventKind).add(eventData);
}
List<Map<Object, Object>> getEventsDispatched(String eventKind) {
return eventsDispatched.putIfAbsent(eventKind, () => <Map<Object, Object>>[]);
}
Iterable<Map<Object, Object>> getServiceExtensionStateChangedEvents(String extensionName) {
return getEventsDispatched('Flutter.ServiceExtensionStateChanged')
.where((Map<Object, Object> event) => event['extension'] == extensionName);
}
Future<String> testBoolExtension(String name, Map<String, String> arguments) async {
expect(extensions, contains(name));
// Encode and decode to JSON to match behavior using a real service
// extension where only JSON is allowed.
return json.decode(json.encode(await extensions[name](arguments)))['enabled'] as String;
}
static void runTests() {
final StructureErrorTestWidgetInspectorService service = StructureErrorTestWidgetInspectorService();
WidgetInspectorService.instance = service;
test('ext.flutter.inspector.structuredErrors still report error to original on error', () async {
final FlutterExceptionHandler oldHandler = FlutterError.onError;
FlutterErrorDetails actualError;
// Creates a spy onError. This spy needs to be set before widgets binding
// initializes.
FlutterError.onError = (FlutterErrorDetails details) {
actualError = details;
};
WidgetsFlutterBinding.ensureInitialized();
try {
// Enables structured errors.
expect(await service.testBoolExtension(
'structuredErrors', <String, String>{'enabled': 'true'}),
equals('true'));
// Creates an error.
final FlutterErrorDetails expectedError = FlutterErrorDetailsForRendering(
library: 'rendering library',
context: ErrorDescription('during layout'),
exception: StackTrace.current,
);
FlutterError.reportError(expectedError);
// Validates the spy still received an error.
expect(actualError, expectedError);
} finally {
FlutterError.onError = oldHandler;
}
});
}
}
\ No newline at end of file
...@@ -2280,7 +2280,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { ...@@ -2280,7 +2280,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService {
List<Map<Object, Object>> flutterErrorEvents = service.getEventsDispatched('Flutter.Error'); List<Map<Object, Object>> flutterErrorEvents = service.getEventsDispatched('Flutter.Error');
expect(flutterErrorEvents, isEmpty); expect(flutterErrorEvents, isEmpty);
final FlutterExceptionHandler oldHandler = FlutterError.onError; final FlutterExceptionHandler oldHandler = FlutterError.presentError;
try { try {
// Enable structured errors. // Enable structured errors.
...@@ -2337,7 +2337,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { ...@@ -2337,7 +2337,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService {
error = flutterErrorEvents.last; error = flutterErrorEvents.last;
expect(error['errorsSinceReload'], 0); expect(error['errorsSinceReload'], 0);
} finally { } finally {
FlutterError.onError = oldHandler; FlutterError.presentError = oldHandler;
} }
}); });
......
...@@ -572,9 +572,9 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -572,9 +572,9 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
}) { }) {
assert(description != null); assert(description != null);
assert(inTest); assert(inTest);
_oldExceptionHandler = FlutterError.onError; _oldExceptionHandler = FlutterError.presentError;
int _exceptionCount = 0; // number of un-taken exceptions int _exceptionCount = 0; // number of un-taken exceptions
FlutterError.onError = (FlutterErrorDetails details) { FlutterError.presentError = (FlutterErrorDetails details) {
if (_pendingExceptionDetails != null) { if (_pendingExceptionDetails != null) {
debugPrint = debugPrintOverride; // just in case the test overrides it -- otherwise we won't see the errors! debugPrint = debugPrintOverride; // just in case the test overrides it -- otherwise we won't see the errors!
if (_exceptionCount == 0) { if (_exceptionCount == 0) {
...@@ -800,7 +800,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -800,7 +800,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
/// Called by the [testWidgets] function after a test is executed. /// Called by the [testWidgets] function after a test is executed.
void postTest() { void postTest() {
assert(inTest); assert(inTest);
FlutterError.onError = _oldExceptionHandler; FlutterError.presentError = _oldExceptionHandler;
_pendingExceptionDetails = null; _pendingExceptionDetails = null;
_parentZone = null; _parentZone = null;
buildOwner.focusManager = FocusManager(); buildOwner.focusManager = FocusManager();
......
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