// 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_core/src/util/io.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;

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

void main() {
  setUpAll(precompileTestExecutable);

  group('on_platform', () {
    test(
      'applies platform-specific configuration to matching tests',
      () async {
        await d
            .file(
              'dart_test.yaml',
              jsonEncode({
                'on_platform': {
                  'chrome': {'timeout': '0s'},
                },
              }),
            )
            .create();

        await d.file('test.dart', '''
        import 'dart:async';

        import 'package:test/test.dart';

        void main() {
          test("test", () => Future.delayed(Duration.zero));
        }
      ''').create();

        var test = await runTest(['-p', 'chrome,vm', 'test.dart']);
        expect(
          test.stdout,
          containsInOrder([
            '-1: [Chrome, Dart2Js] test [E]',
            '+1 -1: Some tests failed.',
          ]),
        );
        await test.shouldExit(1);
      },
      tags: ['chrome'],
    );

    test('supports platform selectors', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'on_platform': {
                'chrome || vm': {'timeout': '0s'},
              },
            }),
          )
          .create();

      await d.file('test.dart', '''
        import 'dart:async';

        import 'package:test/test.dart';

        void main() {
          test("test", () => Future.delayed(Duration.zero));
        }
      ''').create();

      var test = await runTest(['-p', 'chrome,vm', 'test.dart']);
      expect(
        test.stdout,
        containsInOrder([
          '-1: [Chrome, Dart2Js] test [E]',
          '-2: [VM, Kernel] test [E]',
          '-2: Some tests failed.',
        ]),
      );
      await test.shouldExit(1);
    }, tags: ['chrome']);

    group('errors', () {
      test('rejects an invalid selector type', () async {
        await d.file('dart_test.yaml', '{"on_platform": {12: null}}').create();

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

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

        var test = await runTest([]);
        expect(
          test.stderr,
          containsInOrder([
            'Invalid on_platform key: Expected end of input.',
            '^^^^^^^^^',
          ]),
        );
        await test.shouldExit(exit_codes.data);
      });

      test('rejects a selector with an undefined variable', () async {
        await d
            .file(
              'dart_test.yaml',
              jsonEncode({
                'on_platform': {'foo': null},
              }),
            )
            .create();

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

        var test = await runTest([]);
        expect(test.stderr, containsInOrder(['Undefined variable.', '^^^^^']));
        await test.shouldExit(exit_codes.data);
      });

      test('rejects an invalid map', () async {
        await d
            .file(
              'dart_test.yaml',
              jsonEncode({
                'on_platform': {'linux': 12},
              }),
            )
            .create();

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

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

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

      test('rejects runner configuration', () async {
        await d
            .file(
              'dart_test.yaml',
              jsonEncode({
                'on_platform': {
                  'linux': {'filename': '*_blorp'},
                },
              }),
            )
            .create();

        var test = await runTest([]);
        expect(
          test.stderr,
          containsInOrder(["filename isn't supported here.", '^^^^^^^^^']),
        );
        await test.shouldExit(exit_codes.data);
      });
    });
  });

  group('on_os', () {
    test('applies OS-specific configuration on a matching OS', () async {
      await d
          .file(
            'dart_test.yaml',
            jsonEncode({
              'on_os': {
                currentOS.identifier: {'filename': 'test_*.dart'},
              },
            }),
          )
          .create();

      await d.file('foo_test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("foo_test", () {});
        }
      ''').create();

      await d.file('test_foo.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("test_foo", () {});
        }
      ''').create();

      var test = await runTest(['.']);
      expect(
        test.stdout,
        containsInOrder([
          '+0: ./test_foo.dart: test_foo',
          '+1: All tests passed!',
        ]),
      );
      await test.shouldExit(0);
    });

    test(
      "doesn't apply OS-specific configuration on a non-matching OS",
      () async {
        await d
            .file(
              'dart_test.yaml',
              jsonEncode({
                'on_os': {
                  otherOS: {'filename': 'test_*.dart'},
                },
              }),
            )
            .create();

        await d.file('foo_test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("foo_test", () {});
        }
      ''').create();

        await d.file('test_foo.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("test_foo", () {});
        }
      ''').create();

        var test = await runTest(['.']);
        expect(
          test.stdout,
          containsInOrder([
            '+0: ./foo_test.dart: foo_test',
            '+1: All tests passed!',
          ]),
        );
        await test.shouldExit(0);
      },
    );

    group('errors', () {
      test('rejects an invalid OS type', () async {
        await d.file('dart_test.yaml', '{"on_os": {12: null}}').create();

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

      test('rejects an unknown OS name', () async {
        await d
            .file(
              'dart_test.yaml',
              jsonEncode({
                'on_os': {'foo': null},
              }),
            )
            .create();

        var test = await runTest([]);
        expect(
          test.stderr,
          containsInOrder([
            'Invalid on_os key: No such operating system.',
            '^^^^^',
          ]),
        );
        await test.shouldExit(exit_codes.data);
      });

      test('rejects an invalid map', () async {
        await d
            .file(
              'dart_test.yaml',
              jsonEncode({
                'on_os': {'linux': 12},
              }),
            )
            .create();

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

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

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