// 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'));
  }, skip: isBrowser);

  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'));
  }, skip: isBrowser);

  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']));
    }
  });
}
