Enable more lints (#91642)

This commit is contained in:
Ian Hickson 2021-10-14 22:03:03 -07:00 committed by GitHub
parent 9570d353b3
commit 299d484903
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 138 additions and 87 deletions

View file

@ -56,10 +56,10 @@ linter:
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly
# - avoid_catches_without_on_clauses # blocked on https://github.com/dart-lang/linter/issues/3023
# - avoid_catching_errors # blocked on https://github.com/dart-lang/linter/issues/3023
- avoid_classes_with_only_static_members
# - avoid_double_and_int_checks # only useful when targeting JS runtime
- avoid_double_and_int_checks
- avoid_dynamic_calls
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
@ -68,7 +68,7 @@ linter:
- avoid_function_literals_in_foreach_calls
- avoid_implementing_value_types
- avoid_init_to_null
# - avoid_js_rounded_ints # only useful when targeting JS runtime
- avoid_js_rounded_ints
# - avoid_multiple_declarations_per_line # seems to be a stylistic choice we don't subscribe to
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it
@ -81,7 +81,7 @@ linter:
# - avoid_returning_null # still violated by some pre-nnbd code that we haven't yet migrated
- avoid_returning_null_for_future
- avoid_returning_null_for_void
# - avoid_returning_this # there are plenty of valid reasons to return this
# - avoid_returning_this # there are enough valid reasons to return `this` that this lint ends up with too many false positives
- avoid_setters_without_getters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
@ -201,7 +201,7 @@ linter:
- tighten_type_of_initializing_formals
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # too many false positives
# - unawaited_futures # too many false positives, especially with the way AnimationController works
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
@ -216,24 +216,24 @@ linter:
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
# - unnecessary_raw_strings # not yet tested
# - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unrelated_type_equality_checks
# - unsafe_html # not yet tested
- unsafe_html
- use_build_context_synchronously
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
# - use_if_null_to_convert_nulls_to_bools # not yet tested
# - use_if_null_to_convert_nulls_to_bools # blocked on https://github.com/dart-lang/sdk/issues/47436
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested
- use_setters_to_change_properties
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
- use_test_throws_matchers
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review

View file

@ -2,5 +2,10 @@ include: ../analysis_options.yaml
linter:
rules:
avoid_print: false # We use prints as debugging tools here all the time.
only_throw_errors: false # Tests use a less... rigorous style.
avoid_print: false # We use prints as debugging tools here a lot.
# Tests try to throw and catch things in exciting ways all the time, so
# we disable these lints for the tests.
only_throw_errors: false
avoid_catching_errors: false
avoid_catches_without_on_clauses: false

View file

@ -0,0 +1,5 @@
include: ../analysis_options.yaml
linter:
rules:
avoid_js_rounded_ints: false # CLI code doesn't need to worry about JS issues

View file

@ -46,7 +46,7 @@ class Calculator {
final List<dynamic> result = decoder.convert(_data) as List<dynamic>;
final int n = result.length;
onResultListener('Decoded $n results');
} catch (e, stack) {
} on FormatException catch (e, stack) {
debugPrint('Invalid JSON file: $e');
debugPrint('$stack');
}

View file

@ -5,7 +5,7 @@
import 'bitfield.dart' as bitfield;
/// The dart:io implementation of [bitfield.kMaxUnsignedSMI].
const int kMaxUnsignedSMI = 0x3FFFFFFFFFFFFFFF;
const int kMaxUnsignedSMI = 0x3FFFFFFFFFFFFFFF; // ignore: avoid_js_rounded_ints, (VM-only code)
/// The dart:io implementation of [bitfield.Bitfield].
class BitField<T extends dynamic> implements bitfield.BitField<T> {

View file

@ -597,34 +597,27 @@ abstract class BindingBase {
return Future<void>.delayed(Duration.zero);
});
Object? caughtException;
StackTrace? caughtStack;
late Map<String, dynamic> result;
try {
result = await callback(parameters);
} catch (exception, stack) {
caughtException = exception;
caughtStack = stack;
}
if (caughtException == null) {
result['type'] = '_extensionType';
result['method'] = method;
return developer.ServiceExtensionResponse.result(json.encode(result));
} else {
FlutterError.reportError(FlutterErrorDetails(
exception: caughtException,
stack: caughtStack,
exception: exception,
stack: stack,
context: ErrorDescription('during a service extension callback for "$method"'),
));
return developer.ServiceExtensionResponse.error(
developer.ServiceExtensionResponse.extensionError,
json.encode(<String, String>{
'exception': caughtException.toString(),
'stack': caughtStack.toString(),
'exception': exception.toString(),
'stack': stack.toString(),
'method': method,
}),
);
}
result['type'] = '_extensionType';
result['method'] = method;
return developer.ServiceExtensionResponse.result(json.encode(result));
});
}

View file

@ -2832,6 +2832,8 @@ class DiagnosticsProperty<T> extends DiagnosticsNode {
try {
_value = _computeValue!();
} catch (exception) {
// The error is reported to inspector; rethrowing would destroy the
// debugging experience.
_exception = exception;
_value = null;
}

View file

@ -1091,6 +1091,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
/// The prompt rectangle will only be requested on non-web iOS applications.
///
/// When set to null, the currently displayed prompt rectangle (if any) will be dismissed.
// ignore: use_setters_to_change_properties, (API predates enforcing the lint)
void setPromptRectRange(TextRange? newRange) {
_autocorrectHighlightPainter.highlightedRange = newRange;
}

View file

@ -49,7 +49,9 @@ class RenderErrorBox extends RenderBox {
_paragraph = null;
}
} catch (error) {
// Intentionally left empty.
// If an error happens here we're in a terrible state, so we really should
// just forget about it and let the developer deal with the already-reported
// errors. It's unlikely that these errors are going to help with that.
}
}
@ -161,8 +163,10 @@ class RenderErrorBox extends RenderBox {
}
context.canvas.drawParagraph(_paragraph!, offset + Offset(left, top));
}
} catch (e) {
// Intentionally left empty.
} catch (error) {
// If an error happens here we're in a terrible state, so we really should
// just forget about it and let the developer deal with the already-reported
// errors. It's unlikely that these errors are going to help with that.
}
}
}

View file

@ -640,6 +640,7 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste
}
/// Update the last known ScrollDirection when scrolling began.
// ignore: use_setters_to_change_properties, (API predates enforcing the lint)
void updateScrollStartDirection(ScrollDirection direction) {
_lastStartedScrollDirection = direction;
}

View file

@ -319,6 +319,7 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
///
/// * [SystemChrome.setEnabledSystemUIMode], which specifies the
/// [SystemUiMode] to have visible when the application is running.
// ignore: use_setters_to_change_properties, (API predates enforcing the lint)
void setSystemUiChangeCallback(SystemUiChangeCallback? callback) {
_systemUiChangeCallback = callback;
}
@ -387,7 +388,6 @@ class _DefaultBinaryMessenger extends BinaryMessenger {
try {
response = await handler(data);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,

View file

@ -376,7 +376,7 @@ class StandardMessageCodec implements MessageCodec<Object?> {
// decoding because we use tags to detect the type of value.
buffer.putUint8(_valueFloat64);
buffer.putFloat64(value);
} else if (value is int) {
} else if (value is int) { // ignore: avoid_double_and_int_checks, JS code always goes through the `double` path above
if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) {
buffer.putUint8(_valueInt32);
buffer.putInt32(value);

View file

@ -410,8 +410,8 @@ class MethodChannel {
);
} on MissingPluginException {
return null;
} catch (e) {
return codec.encodeErrorEnvelope(code: 'error', message: e.toString());
} catch (error) {
return codec.encodeErrorEnvelope(code: 'error', message: error.toString());
}
}

View file

@ -1006,7 +1006,9 @@ bool debugIsSerializableForRestoration(Object? object) {
try {
const StandardMessageCodec().encodeMessage(object);
result = true;
} catch (_) {
} catch (error) {
// This is only used in asserts, so reporting the exception isn't
// particularly useful, since the assert itself will likely fail.
result = false;
}
return true;

View file

@ -160,6 +160,7 @@ abstract class Action<T extends Intent> with Diagnosticable {
final ObserverList<ActionListenerCallback> _listeners = ObserverList<ActionListenerCallback>();
Action<T>? _currentCallingAction;
// ignore: use_setters_to_change_properties, (code predates enabling of this lint)
void _updateCallingAction(Action<T>? value) {
_currentCallingAction = value;
}

View file

@ -2342,6 +2342,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
Rect? _currentCaretRect;
// ignore: use_setters_to_change_properties, (this is used as a callback, can't be a setter)
void _handleCaretChanged(Rect caretRect) {
_currentCaretRect = caretRect;
}

View file

@ -421,6 +421,7 @@ class FormFieldState<T> extends State<FormField<T>> with RestorationMixin {
/// the value should be set by a call to [didChange], which ensures that
/// `setState` is called.
@protected
// ignore: use_setters_to_change_properties, (API predates enforcing the lint)
void setValue(T? value) {
_value = value;
}

View file

@ -4453,8 +4453,10 @@ class ErrorWidget extends LeafRenderObjectWidget {
static String _stringify(Object? exception) {
try {
return exception.toString();
} catch (e) {
// intentionally left empty.
} catch (error) {
// If we get here, it means things have really gone off the rails, and we're better
// off just returning a simple string and letting the developer find out what the
// root cause of all their problems are by looking at the console logs.
}
return 'Error';
}
@ -5426,6 +5428,9 @@ abstract class RenderObjectElement extends Element {
if (badAncestors.isNotEmpty) {
badAncestors.insert(0, result);
try {
// We explicitly throw here (even though we immediately redirect the
// exception elsewhere) so that debuggers will notice it when they
// have "break on exception" enabled.
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Incorrect use of ParentDataWidget.'),
ErrorDescription('The following ParentDataWidgets are providing parent data to the same RenderObject:'),
@ -5763,9 +5768,10 @@ abstract class RenderObjectElement extends Element {
]);
}
} on FlutterError catch (e) {
// Catching the exception directly to avoid activating the ErrorWidget.
// Since the tree is in a broken state, adding the ErrorWidget would
// cause more exceptions.
// We catch the exception directly to avoid activating the ErrorWidget,
// while still allowing debuggers to break on exception. Since the tree
// is in a broken state, adding the ErrorWidget would likely cause more
// exceptions, which is not good for the debugging experience.
_debugReportException(ErrorSummary('while applying parent data.'), e, e.stackTrace);
}
return true;
@ -6036,6 +6042,7 @@ abstract class RootRenderObjectElement extends RenderObjectElement {
/// to [runApp]. The binding is responsible for driving the build pipeline by
/// calling the build owner's [BuildOwner.buildScope] method. See
/// [WidgetsBinding.drawFrame].
// ignore: use_setters_to_change_properties, (API predates enforcing the lint)
void assignOwner(BuildOwner owner) {
_owner = owner;
}

View file

@ -175,6 +175,7 @@ abstract class Route<T> {
}
}
// ignore: use_setters_to_change_properties, (setters can't be private)
void _updateRestorationId(String? restorationId) {
_restorationScopeId.value = restorationId;
}

View file

@ -320,6 +320,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
/// middle of layout and applying the new position immediately.
/// * [animateTo], which is like [jumpTo] but animating to the
/// destination offset.
// ignore: use_setters_to_change_properties, (API is intended to discourage setting value)
void correctPixels(double value) {
_pixels = value;
}

View file

@ -1277,7 +1277,10 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
assert (() {
try {
scrollController!.position;
} catch (_) {
} catch (error) {
if (scrollController == null || scrollController.positions.length <= 1) {
rethrow;
}
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary(
'The $controllerForError is currently attached to more than one '

View file

@ -204,6 +204,7 @@ abstract class SliverChildDelegate {
if (children != null)
description.add('estimated child count: $children');
} catch (e) {
// The exception is forwarded to widget inspector.
description.add('estimated child count: EXCEPTION (${e.runtimeType})');
}
}

View file

@ -1587,7 +1587,13 @@ class ClipboardStatusNotifier extends ValueNotifier<ClipboardStatus> with Widget
final bool hasStrings;
try {
hasStrings = await Clipboard.hasStrings();
} catch (stacktrace) {
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widget library',
context: ErrorDescription('while checking if the clipboard has strings'),
));
// In the case of an error from the Clipboard API, set the value to
// unknown so that it will try to update again later.
if (_disposed || value == ClipboardStatus.unknown) {

View file

@ -1962,6 +1962,8 @@ mixin WidgetInspectorService {
FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widget inspector library',
context: ErrorDescription('while tracking widget repaints'),
),
);
}

View file

@ -2,4 +2,8 @@ include: ../analysis_options.yaml
linter:
rules:
only_throw_errors: false # We are more flexible for tests.
# Tests try to throw and catch things in exciting ways all the time, so
# we disable these lints for the tests.
only_throw_errors: false
avoid_catches_without_on_clauses: false
avoid_catching_errors: false

View file

@ -18,7 +18,7 @@ void main() {
checkEncodeDecode<dynamic>(json, -9223372036854775807);
});
test('should encode and decode list with a big number', () {
final List<dynamic> message = <dynamic>[-7000000000000000007];
final List<dynamic> message = <dynamic>[-7000000000000000007]; // ignore: avoid_js_rounded_ints, since we check for round-tripping, the actual value doesn't matter!
checkEncodeDecode<dynamic>(json, message);
});
});
@ -72,7 +72,7 @@ void main() {
});
test('should encode and decode a list containing big numbers', () {
final List<dynamic> message = <dynamic>[
-7000000000000000007,
-7000000000000000007, // ignore: avoid_js_rounded_ints, browsers are skipped below
Int64List.fromList(<int>[-0x7fffffffffffffff - 1, 0, 0x7fffffffffffffff]),
];
checkEncodeDecode<dynamic>(standard, message);

View file

@ -564,6 +564,8 @@ Future<vms.VmService> _waitAndConnect(String url, Map<String, dynamic>? headers)
await service.getVersion();
return service;
} catch (e) {
// We should not be catching all errors arbitrarily here, this might hide real errors.
// TODO(ianh): Determine which exceptions to catch here.
await socket?.close();
if (attempts > 5) {
_log('It is taking an unusually long time to connect to the VM...');

View file

@ -115,9 +115,10 @@ class WebFlutterDriver extends FlutterDriver {
response = data != null ? (json.decode(data as String) as Map<String, dynamic>?)! : <String, dynamic>{};
_logCommunication('<<< $response');
} catch (error, stackTrace) {
throw DriverError("Failed to respond to $command due to remote error\n : \$flutterDriver('${jsonEncode(serialized)}')",
error,
stackTrace
throw DriverError(
"Failed to respond to $command due to remote error\n : \$flutterDriver('${jsonEncode(serialized)}')",
error,
stackTrace
);
}
if (response['isError'] == true)
@ -261,8 +262,10 @@ class FlutterWebConnection {
dynamic result;
try {
await _driver.execute(script, <void>[]);
} catch (_) {
// In case there is an exception, do nothing
} catch (error) {
// We should not just arbitrarily throw all exceptions on the ground.
// This is probably hiding real errors.
// TODO(ianh): Determine what exceptions are expected here and handle those specifically.
}
try {
@ -271,7 +274,10 @@ class FlutterWebConnection {
matcher: isNotNull,
timeout: duration ?? const Duration(days: 30),
);
} catch (_) {
} catch (error) {
// We should not just arbitrarily throw all exceptions on the ground.
// This is probably hiding real errors.
// TODO(ianh): Determine what exceptions are expected here and handle those specifically.
// Returns null if exception thrown.
return null;
} finally {

View file

@ -329,15 +329,9 @@ class SkiaGoldClient {
final Uri requestForImage = Uri.parse(
'https://flutter-gold.skia.org/img/images/$imageHash.png',
);
try {
final io.HttpClientRequest request = await httpClient.getUrl(requestForImage);
final io.HttpClientResponse response = await request.close();
await response.forEach((List<int> bytes) => imageBytes.addAll(bytes));
} catch(e) {
rethrow;
}
final io.HttpClientRequest request = await httpClient.getUrl(requestForImage);
final io.HttpClientResponse response = await request.close();
await response.forEach((List<int> bytes) => imageBytes.addAll(bytes));
},
SkiaGoldHttpOverrides(),
);

View file

@ -743,8 +743,10 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
DiagnosticsNode treeDump;
try {
treeDump = renderViewElement?.toDiagnosticsNode() ?? DiagnosticsNode.message('<no tree>');
// TODO(jacobr): this is a hack to make sure the tree can safely be fully dumped.
// Potentially everything is good enough without this case.
// We try to stringify the tree dump here (though we immediately discard the result) because
// we want to make sure that if it can't be serialised, we replace it with a message that
// says the tree could not be serialised. Otherwise, the real exception might get obscured
// by side-effects of the underlying issues causing the tree dumping code to flail.
treeDump.toStringDeep();
} catch (exception) {
treeDump = DiagnosticsNode.message('<additional error caught while dumping tree: $exception>', level: DiagnosticLevel.error);

View file

@ -0,0 +1,7 @@
include: ../../analysis_options.yaml
linter:
rules:
# Tests try to catch errors all the time, so we don't worry about these lints for tests:
avoid_catches_without_on_clauses: false
avoid_catching_errors: false

View file

@ -3,6 +3,7 @@ include: ../analysis_options.yaml
linter:
rules:
avoid_catches_without_on_clauses: true
avoid_catching_errors: false # TODO(ianh): we should refactor the tool codebase to avoid relying on this so much
curly_braces_in_flow_control_structures: true
library_private_types_in_public_api: false # Tool does not have any public API.
no_runtimeType_toString: false # We use runtimeType for debugging in the tool.

View file

@ -70,12 +70,11 @@ Future<int> run(
// We already hit some error, so don't return success. The error path
// (which should be in progress) is responsible for calling _exit().
return 1;
// This catches all exceptions to send to crash logging, etc.
} catch (error, stackTrace) { // ignore: avoid_catches_without_on_clauses
} catch (error, stackTrace) { // ignore: avoid_catches_without_on_clauses
// This catches all exceptions to send to crash logging, etc.
firstError = error;
firstStackTrace = stackTrace;
return _handleToolError(
error, stackTrace, verbose, args, reportCrashes, getVersion);
return _handleToolError(error, stackTrace, verbose, args, reportCrashes, getVersion);
}
}, onError: (Object error, StackTrace stackTrace) async { // ignore: deprecated_member_use
// If sending a crash report throws an error into the zone, we don't want
@ -164,7 +163,8 @@ Future<int> _handleToolError(
} catch (error) { // ignore: avoid_catches_without_on_clauses
globals.stdio.stderrWrite(
'Unable to generate crash report due to secondary error: $error\n'
'${globals.userMessages.flutterToolBugInstructions}\n');
'${globals.userMessages.flutterToolBugInstructions}\n',
);
// Any exception thrown here (including one thrown by `_exit()`) will
// get caught by our zone's `onError` handler. In order to avoid an
// infinite error loop, we throw an error that is recognized above

View file

@ -114,7 +114,7 @@ Future<T> asyncGuard<T>(
}
// This catches all exceptions so that they can be propagated to the
// caller-supplied error handling or the completer.
} catch (e, s) { // ignore: avoid_catches_without_on_clauses
} catch (e, s) { // ignore: avoid_catches_without_on_clauses, forwards to Future
handleError(e, s);
}
}, onError: (Object e, StackTrace s) { // ignore: deprecated_member_use

View file

@ -348,7 +348,7 @@ class ErrorHandlingFile
sink.writeFromSync(buffer, 0, chunkLength);
bytes += chunkLength;
}
} catch (err) { // ignore: avoid_catches_without_on_clauses
} catch (err) { // ignore: avoid_catches_without_on_clauses, rethrows
ErrorHandlingFileSystem.deleteIfExists(resultFile, recursive: true);
rethrow;
} finally {

View file

@ -89,7 +89,7 @@ class _TaskQueueItem<T> {
Future<void> run() async {
try {
_completer.complete(await _closure());
} catch (e) { // ignore: avoid_catches_without_on_clauses
} catch (e) { // ignore: avoid_catches_without_on_clauses, forwards to Future
_completer.completeError(e);
} finally {
onComplete?.call();

View file

@ -11,7 +11,7 @@ final AnchorElement _urlParsingNode = AnchorElement();
/// Example: for the url `http://example.com/foo`, the extracted pathname will
/// be `/foo`.
String extractPathname(String url) {
_urlParsingNode.href = url;
_urlParsingNode.href = url; // ignore: unsafe_html, node is never exposed to the user
final String pathname = _urlParsingNode.pathname ?? '';
return (pathname.isEmpty || pathname[0] == '/') ? pathname : '/$pathname';
}

View file

@ -54,6 +54,8 @@ Future<vms.VmService> _waitAndConnect(
await service.getVersion();
return service;
} catch (e) {
// We should not be catching all errors arbitrarily here, this might hide real errors.
// TODO(ianh): Determine which exceptions to catch here.
await socket.close();
if (attempts > 5) {
_log.warning('It is taking an unusually long time to connect to the VM...');

View file

@ -679,14 +679,13 @@ class _SshPortForwarder implements PortForwarder {
/// If successful returns a valid [ServerSocket] (which must be disconnected
/// later).
static Future<ServerSocket?> _createLocalSocket() async {
ServerSocket s;
try {
s = await ServerSocket.bind(_ipv4Loopback, 0);
return await ServerSocket.bind(_ipv4Loopback, 0);
} catch (e) {
// Failures are signaled by a return value of 0 from this function.
// We should not be catching all errors arbitrarily here, this might hide real errors.
// TODO(ianh): Determine which exceptions to catch here.
_log.warning('_createLocalSocket failed: $e');
return null;
}
return s;
}
}

View file

@ -65,8 +65,7 @@ class WebCallbackManager implements CallbackManager {
'driver side');
}
} catch (exception) {
throw Exception('Web Driver Command failed: ${command.type} with '
'exception $exception');
throw Exception('Web Driver Command failed: ${command.type} with exception $exception');
} finally {
// Reset the completer.
_driverCommandComplete = Completer<bool>();

View file

@ -16,10 +16,8 @@ import 'dart:js_util' as js_util;
/// See also:
///
/// * `_extension_io.dart`, which has the dart:io implementation
void registerWebServiceExtension(
Future<Map<String, dynamic>> Function(Map<String, String>) callback) {
js_util.setProperty(html.window, r'$flutterDriver',
allowInterop((dynamic message) async {
void registerWebServiceExtension(Future<Map<String, dynamic>> Function(Map<String, String>) callback) {
js_util.setProperty(html.window, r'$flutterDriver', allowInterop((dynamic message) async {
try {
final Map<String, dynamic> messageJson = jsonDecode(message as String) as Map<String, dynamic>;
final Map<String, String> params = messageJson.cast<String, String>();
@ -27,9 +25,7 @@ void registerWebServiceExtension(
context[r'$flutterDriverResult'] = json.encode(result);
} catch (error, stackTrace) {
// Encode the error in the same format the FlutterDriver extension uses.
//
// See:
// * packages\flutter_driver\lib\src\extension\extension.dart
// See //packages/flutter_driver/lib/src/extension/extension.dart
context[r'$flutterDriverResult'] = json.encode(<String, dynamic>{
'isError': true,
'response': '$error\n$stackTrace',

View file

@ -106,8 +106,10 @@ Future<void> integrationDriver(
try {
ok = await onScreenshot(screenshotName, screenshotBytes.cast<int>());
} catch (exception) {
throw StateError('Screenshot failure:\n'
'onScreenshot("$screenshotName", <bytes>) threw an exception: $exception');
throw StateError(
'Screenshot failure:\n'
'onScreenshot("$screenshotName", <bytes>) threw an exception: $exception',
);
}
if (!ok) {
failures.add(screenshotName);