// 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 'package:flutter_test/flutter_test.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( exception: NullThrownError(), // ignore: deprecated_member_use library: 'LIBRARY', context: ErrorDescription('CONTEXTING'), informationCollector: () sync* { yield ErrorDescription('INFO'); }, ).toString(), '══╡ EXCEPTION CAUGHT BY LIBRARY ╞════════════════════════════════\n' 'The null value was thrown CONTEXTING.\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( // ignore: deprecated_member_use FlutterErrorDetails(exception: NullThrownError()).toString(), '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' 'The null value was thrown.\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', () { { final 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', ); } { final 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=2_bug.md\n' '═════════════════════════════════════════════════════════════════\n', ); } { final 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=2_bug.md\n' '═════════════════════════════════════════════════════════════════\n', ); } { final 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=2_bug.md\n' '═════════════════════════════════════════════════════════════════\n', ); } }); test('User-thrown exceptions have ErrorSummary properties', () { { final 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'])); } { final 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=2_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?>.filled(2, null); 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?>.filled(2, null)); }); }