| // 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 'package:flutter/foundation.dart'; |
| import '../flutter_test_alternative.dart'; |
| |
| import 'capture_output.dart'; |
| |
| void main() { |
| test('debugPrintStack', () { |
| final List<String> log = captureOutput(() { |
| debugPrintStack(label: 'Example label', maxFrames: 7); |
| }); |
| expect(log[0], contains('Example label')); |
| expect(log[1], contains('debugPrintStack')); |
| }); |
| |
| test('debugPrintStack', () { |
| final List<String> log = captureOutput(() { |
| final FlutterErrorDetails details = FlutterErrorDetails( |
| exception: 'Example exception', |
| stack: StackTrace.current, |
| library: 'Example library', |
| context: ErrorDescription('Example context'), |
| informationCollector: () sync* { |
| yield ErrorDescription('Example information'); |
| }, |
| ); |
| |
| FlutterError.dumpErrorToConsole(details); |
| }); |
| |
| expect(log[0], contains('EXAMPLE LIBRARY')); |
| expect(log[1], contains('Example context')); |
| expect(log[2], contains('Example exception')); |
| |
| final String joined = log.join('\n'); |
| |
| expect(joined, contains('captureOutput')); |
| expect(joined, contains('\nExample information\n')); |
| }); |
| |
| test('FlutterErrorDetails.toString', () { |
| expect( |
| FlutterErrorDetails( |
| exception: 'MESSAGE', |
| library: 'LIBRARY', |
| context: ErrorDescription('CONTEXTING'), |
| informationCollector: () sync* { |
| yield ErrorDescription('INFO'); |
| }, |
| ).toString(), |
| '══╡ EXCEPTION CAUGHT BY LIBRARY ╞════════════════════════════════\n' |
| 'The following message was thrown CONTEXTING:\n' |
| 'MESSAGE\n' |
| '\n' |
| 'INFO\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| |
| ); |
| expect( |
| FlutterErrorDetails( |
| library: 'LIBRARY', |
| context: ErrorDescription('CONTEXTING'), |
| informationCollector: () sync* { |
| yield ErrorDescription('INFO'); |
| }, |
| ).toString(), |
| '══╡ EXCEPTION CAUGHT BY LIBRARY ╞════════════════════════════════\n' |
| 'The following Null object was thrown CONTEXTING:\n' |
| ' null\n' |
| '\n' |
| 'INFO\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| expect( |
| FlutterErrorDetails( |
| exception: 'MESSAGE', |
| context: ErrorDescription('CONTEXTING'), |
| informationCollector: () sync* { |
| yield ErrorDescription('INFO'); |
| }, |
| ).toString(), |
| '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' |
| 'The following message was thrown CONTEXTING:\n' |
| 'MESSAGE\n' |
| '\n' |
| 'INFO\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| expect( |
| FlutterErrorDetails( |
| exception: 'MESSAGE', |
| context: ErrorDescription('CONTEXTING ${'SomeContext(BlaBla)'}'), |
| informationCollector: () sync* { |
| yield ErrorDescription('INFO'); |
| }, |
| ).toString(), |
| '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' |
| 'The following message was thrown CONTEXTING SomeContext(BlaBla):\n' |
| 'MESSAGE\n' |
| '\n' |
| 'INFO\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| expect( |
| const FlutterErrorDetails( |
| exception: 'MESSAGE', |
| ).toString(), |
| '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' |
| 'The following message was thrown:\n' |
| 'MESSAGE\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| expect( |
| const FlutterErrorDetails().toString(), |
| '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' |
| 'The following Null object was thrown:\n' |
| ' null\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| }); |
| |
| test('FlutterErrorDetails.toStringShort', () { |
| expect( |
| FlutterErrorDetails( |
| exception: 'MESSAGE', |
| library: 'library', |
| context: ErrorDescription('CONTEXTING'), |
| informationCollector: () sync* { |
| yield ErrorDescription('INFO'); |
| }, |
| ).toStringShort(), |
| 'Exception caught by library', |
| ); |
| }); |
| |
| test('FlutterError default constructor', () { |
| FlutterError error = FlutterError( |
| 'My Error Summary.\n' |
| 'My first description.\n' |
| 'My second description.' |
| ); |
| expect(error.diagnostics.length, equals(3)); |
| expect(error.diagnostics[0].level, DiagnosticLevel.summary); |
| expect(error.diagnostics[1].level, DiagnosticLevel.info); |
| expect(error.diagnostics[2].level, DiagnosticLevel.info); |
| expect(error.diagnostics[0].toString(), 'My Error Summary.'); |
| expect(error.diagnostics[1].toString(), 'My first description.'); |
| expect(error.diagnostics[2].toString(), 'My second description.'); |
| expect( |
| error.toStringDeep(), |
| 'FlutterError\n' |
| ' My Error Summary.\n' |
| ' My first description.\n' |
| ' My second description.\n', |
| ); |
| |
| error = FlutterError( |
| 'My Error Summary.\n' |
| 'My first description.\n' |
| 'My second description.\n' |
| '\n' |
| ); |
| |
| expect(error.diagnostics.length, equals(5)); |
| expect(error.diagnostics[0].level, DiagnosticLevel.summary); |
| expect(error.diagnostics[1].level, DiagnosticLevel.info); |
| expect(error.diagnostics[2].level, DiagnosticLevel.info); |
| expect(error.diagnostics[0].toString(), 'My Error Summary.'); |
| expect(error.diagnostics[1].toString(), 'My first description.'); |
| expect(error.diagnostics[2].toString(), 'My second description.'); |
| expect(error.diagnostics[3].toString(), ''); |
| expect(error.diagnostics[4].toString(), ''); |
| expect( |
| error.toStringDeep(), |
| 'FlutterError\n' |
| ' My Error Summary.\n' |
| ' My first description.\n' |
| ' My second description.\n' |
| '\n' |
| '\n', |
| ); |
| |
| error = FlutterError( |
| 'My Error Summary.\n' |
| 'My first description.\n' |
| '\n' |
| 'My second description.' |
| ); |
| expect(error.diagnostics.length, equals(4)); |
| expect(error.diagnostics[0].level, DiagnosticLevel.summary); |
| expect(error.diagnostics[1].level, DiagnosticLevel.info); |
| expect(error.diagnostics[2].level, DiagnosticLevel.info); |
| expect(error.diagnostics[3].level, DiagnosticLevel.info); |
| expect(error.diagnostics[0].toString(), 'My Error Summary.'); |
| expect(error.diagnostics[1].toString(), 'My first description.'); |
| expect(error.diagnostics[2].toString(), ''); |
| expect(error.diagnostics[3].toString(), 'My second description.'); |
| |
| expect( |
| error.toStringDeep(), |
| 'FlutterError\n' |
| ' My Error Summary.\n' |
| ' My first description.\n' |
| '\n' |
| ' My second description.\n', |
| ); |
| error = FlutterError('My Error Summary.'); |
| expect(error.diagnostics.length, 1); |
| expect(error.diagnostics.first.level, DiagnosticLevel.summary); |
| expect(error.diagnostics.first.toString(), 'My Error Summary.'); |
| |
| expect( |
| error.toStringDeep(), |
| 'FlutterError\n' |
| ' My Error Summary.\n', |
| ); |
| }); |
| |
| test('Malformed FlutterError objects', () { |
| { |
| AssertionError error; |
| try { |
| throw FlutterError.fromParts(<DiagnosticsNode>[]); |
| } on AssertionError catch (e) { |
| error = e; |
| } |
| expect( |
| FlutterErrorDetails(exception: error).toString(), |
| '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' |
| 'The following assertion was thrown:\n' |
| 'Empty FlutterError\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| } |
| |
| { |
| AssertionError error; |
| try { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| (ErrorDescription('Error description without a summary'))]); |
| } on AssertionError catch (e) { |
| error = e; |
| } |
| expect( |
| FlutterErrorDetails(exception: error).toString(), |
| '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' |
| 'The following assertion was thrown:\n' |
| 'FlutterError is missing a summary.\n' |
| 'All FlutterError objects should start with a short (one line)\n' |
| 'summary description of the problem that was detected.\n' |
| 'Malformed FlutterError:\n' |
| ' Error description without a summary\n' |
| '\n' |
| 'This error should still help you solve your problem, however\n' |
| 'please also report this malformed error in the framework by\n' |
| 'filing a bug on GitHub:\n' |
| ' https://github.com/flutter/flutter/issues/new?template=BUG.md\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| } |
| |
| { |
| AssertionError error; |
| try { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('Error Summary A'), |
| ErrorDescription('Some descriptionA'), |
| ErrorSummary('Error Summary B'), |
| ErrorDescription('Some descriptionB'), |
| ]); |
| } on AssertionError catch (e) { |
| error = e; |
| } |
| expect( |
| FlutterErrorDetails(exception: error).toString(), |
| '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' |
| 'The following assertion was thrown:\n' |
| 'FlutterError contained multiple error summaries.\n' |
| 'All FlutterError objects should have only a single short (one\n' |
| 'line) summary description of the problem that was detected.\n' |
| 'Malformed FlutterError:\n' |
| ' Error Summary A\n' |
| ' Some descriptionA\n' |
| ' Error Summary B\n' |
| ' Some descriptionB\n' |
| '\n' |
| 'The malformed error has 2 summaries.\n' |
| 'Summary 1: Error Summary A\n' |
| 'Summary 2: Error Summary B\n' |
| '\n' |
| 'This error should still help you solve your problem, however\n' |
| 'please also report this malformed error in the framework by\n' |
| 'filing a bug on GitHub:\n' |
| ' https://github.com/flutter/flutter/issues/new?template=BUG.md\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| } |
| |
| { |
| AssertionError error; |
| try { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorDescription('Some description'), |
| ErrorSummary('Error summary'), |
| ]); |
| } on AssertionError catch (e) { |
| error = e; |
| } |
| expect( |
| FlutterErrorDetails(exception: error).toString(), |
| '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' |
| 'The following assertion was thrown:\n' |
| 'FlutterError is missing a summary.\n' |
| 'All FlutterError objects should start with a short (one line)\n' |
| 'summary description of the problem that was detected.\n' |
| 'Malformed FlutterError:\n' |
| ' Some description\n' |
| ' Error summary\n' |
| '\n' |
| 'This error should still help you solve your problem, however\n' |
| 'please also report this malformed error in the framework by\n' |
| 'filing a bug on GitHub:\n' |
| ' https://github.com/flutter/flutter/issues/new?template=BUG.md\n' |
| '═════════════════════════════════════════════════════════════════\n', |
| ); |
| } |
| }); |
| |
| test('User-thrown exceptions have ErrorSummary properties', () { |
| { |
| DiagnosticsNode node; |
| try { |
| throw 'User thrown string'; |
| } catch (e) { |
| node = FlutterErrorDetails(exception: e).toDiagnosticsNode(); |
| } |
| final ErrorSummary summary = node.getProperties().whereType<ErrorSummary>().single; |
| expect(summary.value, equals(<String>['User thrown string'])); |
| } |
| |
| { |
| DiagnosticsNode node; |
| try { |
| throw ArgumentError.notNull('myArgument'); |
| } catch (e) { |
| node = FlutterErrorDetails(exception: e).toDiagnosticsNode(); |
| } |
| final ErrorSummary summary = node.getProperties().whereType<ErrorSummary>().single; |
| expect(summary.value, equals(<String>['Invalid argument(s) (myArgument): Must not be null'])); |
| } |
| }); |
| |
| test('Identifies user fault', () { |
| // User fault because they called `new Text(null)` from their own code. |
| final StackTrace stack = StackTrace.fromString(''' |
| #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:42:39) |
| #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:38:5) |
| #2 new Text (package:flutter/src/widgets/text.dart:287:10) |
| #3 _MyHomePageState.build (package:hello_flutter/main.dart:72:16) |
| #4 StatefulElement.build (package:flutter/src/widgets/framework.dart:4414:27) |
| #5 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4303:15) |
| #6 Element.rebuild (package:flutter/src/widgets/framework.dart:4027:5) |
| #7 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4286:5) |
| #8 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4461:11) |
| #9 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4281:5) |
| #10 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3276:14) |
| #11 Element.updateChild (package:flutter/src/widgets/framework.dart:3070:12) |
| #12 SingleChildRenderObjectElement.mount (package:flutter/blah.dart:999:9)'''); |
| |
| final FlutterErrorDetails details = FlutterErrorDetails( |
| exception: AssertionError('Test assertion'), |
| stack: stack, |
| ); |
| |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| details.debugFillProperties(builder); |
| |
| expect(builder.properties.length, 4); |
| expect(builder.properties[0].toString(), 'The following assertion was thrown:'); |
| expect(builder.properties[1].toString(), contains('Assertion failed')); |
| expect(builder.properties[2] is ErrorSpacer, true); |
| final DiagnosticsStackTrace trace = builder.properties[3] as DiagnosticsStackTrace; |
| expect(trace, isNotNull); |
| expect(trace.value, stack); |
| }); |
| |
| test('Identifies our fault', () { |
| // Our fault because we should either have an assertion in `text_helper.dart` |
| // or we should make sure not to pass bad values into new Text. |
| final StackTrace stack = StackTrace.fromString(''' |
| #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:42:39) |
| #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:38:5) |
| #2 new Text (package:flutter/src/widgets/text.dart:287:10) |
| #3 new SomeWidgetUsingText (package:flutter/src/widgets/text_helper.dart:287:10) |
| #4 _MyHomePageState.build (package:hello_flutter/main.dart:72:16) |
| #5 StatefulElement.build (package:flutter/src/widgets/framework.dart:4414:27) |
| #6 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4303:15) |
| #7 Element.rebuild (package:flutter/src/widgets/framework.dart:4027:5) |
| #8 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4286:5) |
| #9 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4461:11) |
| #10 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4281:5) |
| #11 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3276:14) |
| #12 Element.updateChild (package:flutter/src/widgets/framework.dart:3070:12) |
| #13 SingleChildRenderObjectElement.mount (package:flutter/blah.dart:999:9)'''); |
| |
| final FlutterErrorDetails details = FlutterErrorDetails( |
| exception: AssertionError('Test assertion'), |
| stack: stack, |
| ); |
| |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| details.debugFillProperties(builder); |
| expect(builder.properties.length, 6); |
| expect(builder.properties[0].toString(), 'The following assertion was thrown:'); |
| expect(builder.properties[1].toString(), contains('Assertion failed')); |
| expect(builder.properties[2] is ErrorSpacer, true); |
| expect( |
| builder.properties[3].toString(), |
| 'Either the assertion indicates an error in the framework itself, or we should ' |
| 'provide substantially more information in this error message to help you determine ' |
| 'and fix the underlying cause.\n' |
| 'In either case, please report this assertion by filing a bug on GitHub:\n' |
| ' https://github.com/flutter/flutter/issues/new?template=BUG.md', |
| ); |
| expect(builder.properties[4] is ErrorSpacer, true); |
| final DiagnosticsStackTrace trace = builder.properties[5] as DiagnosticsStackTrace; |
| expect(trace, isNotNull); |
| expect(trace.value, stack); |
| }); |
| |
| test('RepetitiveStackFrameFilter does not go out of range', () { |
| const RepetitiveStackFrameFilter filter = RepetitiveStackFrameFilter( |
| frames: <PartialStackFrame>[ |
| PartialStackFrame(className: 'TestClass', method: 'test1', package: 'package:test/blah.dart'), |
| PartialStackFrame(className: 'TestClass', method: 'test2', package: 'package:test/blah.dart'), |
| PartialStackFrame(className: 'TestClass', method: 'test3', package: 'package:test/blah.dart'), |
| ], |
| replacement: 'test', |
| ); |
| final List<String> reasons = List<String>(2); |
| filter.filter( |
| const <StackFrame>[ |
| StackFrame(className: 'TestClass', method: 'test1', packageScheme: 'package', package: 'test', packagePath: 'blah.dart', line: 1, column: 1, number: 0, source: ''), |
| StackFrame(className: 'TestClass', method: 'test2', packageScheme: 'package', package: 'test', packagePath: 'blah.dart', line: 1, column: 1, number: 0, source: ''), |
| ], |
| reasons, |
| ); |
| expect(reasons, List<String>(2)); |
| }); |
| } |