// 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 '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);

  group('with test.dart?name="name" query', () {
    test('selects tests with matching names', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("nope", () => throw TestFailure("oh no"));
          test("selected 2", () {});
        }
      ''').create();

      var test = await runTest(['test.dart?name=selected']);

      expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));

      await test.shouldExit(0);
    });

    test('supports RegExp syntax', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("test 1", () {});
          test("test 2", () => throw TestFailure("oh no"));
          test("test 3", () {});
        }
      ''').create();

      var test = await runTest(['test.dart?name=test [13]']);

      expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));

      await test.shouldExit(0);
    });

    test('applies only to the associated file', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("selected 2", () => throw TestFailure("oh no"));
        }
      ''').create();

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

        void main() {
          test("selected 1", () => throw TestFailure("oh no"));
          test("selected 2", () {});
        }
      ''').create();

      var test = await runTest([
        'test.dart?name=selected 1',
        'test2.dart?name=selected 2',
      ]);

      expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));
      await test.shouldExit(0);
    });

    test('selects more narrowly when passed multiple times', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("nope", () => throw TestFailure("oh no"));
          test("selected 2", () {});
        }
      ''').create();

      var test = await runTest(['test.dart?name=selected&name=1']);

      expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
      await test.shouldExit(0);
    });

    test('applies to directories', () async {
      await d.dir('dir', [
        d.file('first_test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("selected 2", () => throw TestFailure("oh no"));
        }
      '''),
        d.file('second_test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("selected 2", () => throw TestFailure("oh no"));
        }
      '''),
      ]).create();

      var test = await runTest(['dir?name=selected 1']);

      expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));
      await test.shouldExit(0);
    });

    test('produces an error when no tests match', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

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

      var test = await runTest(['test.dart?name=no']);

      expect(test.stderr, emitsThrough(contains('No tests were found.')));

      await test.shouldExit(exit_codes.noTestsRan);
    });

    test("doesn't filter out load exceptions", () async {
      var test = await runTest(['file?name=name']);
      expect(
        test.stdout,
        containsInOrder([
          '-1: loading file [E]',
          '  Failed to load "file": Does not exist.',
        ]),
      );

      await test.shouldExit(1);
    });
  });

  group('with test.dart?full-name query,', () {
    test('matches with the complete test name', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected", () {});
          test("nope", () => throw TestFailure("oh no"));
          test("selected nope", () => throw TestFailure("oh no"));
        }
      ''').create();

      var test = await runTest(['test.dart?full-name=selected']);

      expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
      await test.shouldExit(0);
    });

    test("doesn't support RegExp syntax", () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("test 1", () => throw TestFailure("oh no"));
          test("test 2", () => throw TestFailure("oh no"));
          test("test [12]", () {});
        }
      ''').create();

      var test = await runTest(['test.dart?full-name=test [12]']);

      expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
      await test.shouldExit(0);
    });

    test('applies only to the associated file', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("selected 2", () => throw TestFailure("oh no"));
        }
      ''').create();

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

        void main() {
          test("selected 1", () => throw TestFailure("oh no"));
          test("selected 2", () {});
        }
      ''').create();

      var test = await runTest([
        'test.dart?full-name=selected 1',
        'test2.dart?full-name=selected 2',
      ]);

      expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));
      await test.shouldExit(0);
    });

    test('produces an error when no tests match', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

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

      var test = await runTest(['test.dart?full-name=no match']);

      expect(test.stderr, emitsThrough(contains('No tests were found.')));
      await test.shouldExit(exit_codes.noTestsRan);
    });
  });

  test('test?name="name" and --name narrow the selection', () async {
    await d.file('test.dart', '''
      import 'package:test/test.dart';

      void main() {
        test("selected 1", () {});
        test("nope 1", () => throw TestFailure("oh no"));
        test("selected 2", () => throw TestFailure("oh no"));
        test("nope 2", () => throw TestFailure("oh no"));
      }
    ''').create();

    var test = await runTest(['--name', '1', 'test.dart?name=selected']);

    expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
    await test.shouldExit(0);
  });

  test('test?name="name" and test?full-name="name" throws', () async {
    await d.file('test.dart', '''
      import 'package:test/test.dart';

      void main() {
        test("selected 1", () {});
        test("nope 1", () => throw TestFailure("oh no"));
        test("selected 2", () => throw TestFailure("oh no"));
        test("nope 2", () => throw TestFailure("oh no"));
      }
    ''').create();

    var test = await runTest(['test.dart?name=selected&full-name=selected 1']);

    await test.shouldExit(64);
  });

  group('with the --name flag,', () {
    test('selects tests with matching names', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("nope", () => throw TestFailure("oh no"));
          test("selected 2", () {});
        }
      ''').create();

      var test = await runTest(['--name', 'selected', 'test.dart']);
      expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));
      await test.shouldExit(0);
    });

    test('supports RegExp syntax', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("test 1", () {});
          test("test 2", () => throw TestFailure("oh no"));
          test("test 3", () {});
        }
      ''').create();

      var test = await runTest(['--name', 'test [13]', 'test.dart']);
      expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));
      await test.shouldExit(0);
    });

    test('selects more narrowly when passed multiple times', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("nope", () => throw TestFailure("oh no"));
          test("selected 2", () {});
        }
      ''').create();

      var test = await runTest([
        '--name',
        'selected',
        '--name',
        '1',
        'test.dart',
      ]);
      expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
      await test.shouldExit(0);
    });

    test('produces an error when no tests match', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

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

      var test = await runTest(['--name', 'no match', 'test.dart']);
      expect(
        test.stderr,
        emitsThrough(contains('No tests match regular expression "no match".')),
      );
      await test.shouldExit(exit_codes.noTestsRan);
    });

    test("doesn't filter out load exceptions", () async {
      var test = await runTest(['--name', 'name', 'file']);
      expect(
        test.stdout,
        containsInOrder([
          '-1: loading file [E]',
          '  Failed to load "file": Does not exist.',
        ]),
      );
      await test.shouldExit(1);
    });
  });

  group('with the --plain-name flag,', () {
    test('selects tests with matching names', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("nope", () => throw TestFailure("oh no"));
          test("selected 2", () {});
        }
      ''').create();

      var test = await runTest(['--plain-name', 'selected', 'test.dart']);
      expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));
      await test.shouldExit(0);
    });

    test("doesn't support RegExp syntax", () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("test 1", () => throw TestFailure("oh no"));
          test("test 2", () => throw TestFailure("oh no"));
          test("test [12]", () {});
        }
      ''').create();

      var test = await runTest(['--plain-name', 'test [12]', 'test.dart']);
      expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
      await test.shouldExit(0);
    });

    test('selects more narrowly when passed multiple times', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

        void main() {
          test("selected 1", () {});
          test("nope", () => throw TestFailure("oh no"));
          test("selected 2", () {});
        }
      ''').create();

      var test = await runTest([
        '--plain-name',
        'selected',
        '--plain-name',
        '1',
        'test.dart',
      ]);
      expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
      await test.shouldExit(0);
    });

    test('produces an error when no tests match', () async {
      await d.file('test.dart', '''
        import 'package:test/test.dart';

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

      var test = await runTest(['--plain-name', 'no match', 'test.dart']);
      expect(test.stderr, emitsThrough(contains('No tests match "no match".')));
      await test.shouldExit(exit_codes.noTestsRan);
    });
  });

  test('--name and --plain-name together narrow the selection', () async {
    await d.file('test.dart', '''
      import 'package:test/test.dart';

      void main() {
        test("selected 1", () {});
        test("nope", () => throw TestFailure("oh no"));
        test("selected 2", () {});
      }
    ''').create();

    var test = await runTest([
      '--name',
      '.....',
      '--plain-name',
      'e',
      'test.dart',
    ]);
    expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));
    await test.shouldExit(0);
  });
}
