// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

@TestOn('vm')
library;

import 'dart:convert';

import 'package:test/test.dart';
import 'package:test_core/src/util/exit_codes.dart' as exit_codes;
import 'package:test_descriptor/test_descriptor.dart' as d;

import '../../io.dart';

void main() {
  setUpAll(precompileTestExecutable);

  test('rejects an invalid fold_stack_frames', () async {
    await d
        .file('dart_test.yaml', jsonEncode({'fold_stack_frames': 'flup'}))
        .create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['fold_stack_frames must be a map', '^^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects multiple fold_stack_frames keys', () async {
    await d
        .file(
          'dart_test.yaml',
          jsonEncode({
            'fold_stack_frames': {
              'except': ['blah'],
              'only': ['blah'],
            },
          }),
        )
        .create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder([
        'Can only contain one of "only" or "except".',
        '^^^^^^',
      ]),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects invalid fold_stack_frames keys', () async {
    await d
        .file(
          'dart_test.yaml',
          jsonEncode({
            'fold_stack_frames': {'invalid': 'blah'},
          }),
        )
        .create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['Must be "only" or "except".', '^^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects invalid fold_stack_frames values', () async {
    await d
        .file(
          'dart_test.yaml',
          jsonEncode({
            'fold_stack_frames': {'only': 'blah'},
          }),
        )
        .create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['Folded packages must be strings', '^^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects an invalid pause_after_load', () async {
    await d
        .file('dart_test.yaml', jsonEncode({'pause_after_load': 'flup'}))
        .create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['pause_after_load must be a boolean', '^^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects an invalid verbose_trace', () async {
    await d
        .file('dart_test.yaml', jsonEncode({'verbose_trace': 'flup'}))
        .create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['verbose_trace must be a boolean', '^^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects an invalid chain_stack_traces', () async {
    await d
        .file('dart_test.yaml', jsonEncode({'chain_stack_traces': 'flup'}))
        .create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['chain_stack_traces must be a boolean', '^^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects an invalid retry', () async {
    await d.file('dart_test.yaml', jsonEncode({'retry': 'flup'})).create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['retry must be a non-negative int', '^^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects an negative retry values', () async {
    await d.file('dart_test.yaml', jsonEncode({'retry': -1})).create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['retry must be a non-negative int', '^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  test('rejects an invalid js_trace', () async {
    await d.file('dart_test.yaml', jsonEncode({'js_trace': 'flup'})).create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['js_trace must be a boolean', '^^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  group('reporter', () {
    test('rejects an invalid type', () async {
      await d.file('dart_test.yaml', jsonEncode({'reporter': 12})).create();

      var test = await runTest(['test.dart']);
      expect(test.stderr, containsInOrder(['reporter must be a string', '^^']));
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid name', () async {
      await d
          .file('dart_test.yaml', jsonEncode({'reporter': 'non-existent'}))
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['Unknown reporter "non-existent"', '^^^^^^^^^^^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });
  });

  group('file_reporters', () {
    test('rejects an invalid type', () async {
      await d
          .file('dart_test.yaml', jsonEncode({'file_reporters': 12}))
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['file_reporters must be a map', '^^']),
      );
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid value type', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'file_reporters': {'json': 12},
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['file_reporters value must be a string', '^^']),
      );
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid name', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'file_reporters': {'non-existent': 'out'},
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['Unknown reporter "non-existent"', '^^^^^^^^^^^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });
  });

  test('rejects an invalid concurrency', () async {
    await d.file('dart_test.yaml', jsonEncode({'concurrency': 'foo'})).create();

    var test = await runTest(['test.dart']);
    expect(
      test.stderr,
      containsInOrder(['concurrency must be an int', '^^^^^']),
    );
    await test.shouldExit(exit_codes.data);
  });

  group('timeout', () {
    test('rejects an invalid type', () async {
      await d.file('dart_test.yaml', jsonEncode({'timeout': 12})).create();

      var test = await runTest(['test.dart']);
      expect(test.stderr, containsInOrder(['timeout must be a string', '^^']));
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid format', () async {
      await d.file('dart_test.yaml', jsonEncode({'timeout': '12p'})).create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['Invalid timeout: expected unit', '^^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });
  });

  group('names', () {
    test('rejects an invalid list type', () async {
      await d.file('dart_test.yaml', jsonEncode({'names': 'vm'})).create();

      var test = await runTest(['test.dart']);
      expect(test.stderr, containsInOrder(['names must be a list', '^^^^']));
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid member type', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'names': [12],
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(test.stderr, containsInOrder(['Names must be strings', '^^']));
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid RegExp', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'names': ['(foo'],
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['Invalid name: Unterminated group', '^^^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });
  });

  group('plain_names', () {
    test('rejects an invalid list type', () async {
      await d
          .file('dart_test.yaml', jsonEncode({'plain_names': 'vm'}))
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['plain_names must be a list', '^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid member type', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'plain_names': [12],
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(test.stderr, containsInOrder(['Names must be strings', '^^']));
      await test.shouldExit(exit_codes.data);
    });
  });

  group('platforms', () {
    test('rejects an invalid list type', () async {
      await d.file('dart_test.yaml', jsonEncode({'platforms': 'vm'})).create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['platforms must be a list', '^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid member type', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'platforms': [12],
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['Platform name must be a string', '^^']),
      );
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid member name', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'platforms': ['foo'],
            }),
          )
          .create();

      await d.dir('test').create();

      var test = await runTest([]);
      expect(test.stderr, containsInOrder(['Unknown platform "foo"', '^^^^^']));
      await test.shouldExit(exit_codes.data);
    });
  });

  group('paths', () {
    test('rejects an invalid list type', () async {
      await d.file('dart_test.yaml', jsonEncode({'paths': 'test'})).create();

      var test = await runTest(['test.dart']);
      expect(test.stderr, containsInOrder(['paths must be a list', '^^^^^^']));
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid member type', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'paths': [12],
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(test.stderr, containsInOrder(['Paths must be strings', '^^']));
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an absolute path', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'paths': ['/foo'],
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['Paths must be relative.', '^^^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid URI', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'paths': [':invalid'],
            }),
          )
          .create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['Invalid path: Invalid empty scheme', '^^^^^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });
  });

  group('filename', () {
    test('rejects an invalid type', () async {
      await d.file('dart_test.yaml', jsonEncode({'filename': 12})).create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['filename must be a string.', '^^']),
      );
      await test.shouldExit(exit_codes.data);
    });

    test('rejects an invalid format', () async {
      await d.file('dart_test.yaml', jsonEncode({'filename': '{foo'})).create();

      var test = await runTest(['test.dart']);
      expect(
        test.stderr,
        containsInOrder(['Invalid filename: expected ",".', '^^^^^^']),
      );
      await test.shouldExit(exit_codes.data);
    });
  });
}
