// Copyright (c) 2023, 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')
@Timeout.factor(2)
library;

import 'dart:io';

import 'package:test/test.dart';
import 'package:test_api/backend.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;

import '../io.dart';

void main() {
  setUpAll(() async {
    await precompileTestExecutable();
  });

  for (var runtime in Runtime.builtIn) {
    for (var compiler in runtime.supportedCompilers) {
      // Ignore the platforms we can't run on this OS.
      if ((runtime == Runtime.edge && !Platform.isWindows) ||
          (runtime == Runtime.safari && !Platform.isMacOS)) {
        continue;
      }
      String? skipReason;
      if (runtime == Runtime.safari) {
        skipReason = 'https://github.com/dart-lang/test/issues/1253';
      } else if (compiler == Compiler.dart2wasm) {
        skipReason = 'Wasm tests are experimental and require special setup';
      } else if ([Runtime.firefox, Runtime.nodeJS].contains(runtime) &&
          Platform.isWindows) {
        skipReason = 'https://github.com/dart-lang/test/issues/1942';
      } else if (runtime == Runtime.firefox && Platform.isMacOS) {
        skipReason = 'https://github.com/dart-lang/test/pull/2276';
      }
      group('--runtime ${runtime.identifier} --compiler ${compiler.identifier}',
          skip: skipReason, () {
        final testArgs = [
          'test.dart',
          '-p',
          runtime.identifier,
          '-c',
          compiler.identifier
        ];

        test('can run passing tests', () async {
          await d.file('test.dart', _goodTest).create();
          var test = await runTest(testArgs);

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

        test('fails gracefully for invalid code', () async {
          await d.file('test.dart', _compileErrorTest).create();
          var test = await runTest(testArgs);

          expect(
              test.stdout,
              containsInOrder([
                "Error: A value of type 'String' can't be assigned to a variable of type 'int'.",
                "int x = 'hello';",
              ]));

          await test.shouldExit(1);
        });

        test('fails gracefully for test failures', () async {
          await d.file('test.dart', _failingTest).create();
          var test = await runTest(testArgs);

          expect(
              test.stdout,
              containsInOrder([
                'Expected: <2>',
                'Actual: <1>',
                'test.dart 5',
                '+0 -1: Some tests failed.',
              ]));

          await test.shouldExit(1);
        });

        test('fails gracefully if a test file throws in main', () async {
          await d.file('test.dart', _throwingTest).create();
          var test = await runTest(testArgs);
          expect(
              test.stdout,
              containsInOrder([
                '-1: [${runtime.name}, ${compiler.name}] loading test.dart [E]',
                'Failed to load "test.dart": oh no'
              ]));
          await test.shouldExit(1);
        });

        test('captures prints', () async {
          await d.file('test.dart', _testWithPrints).create();
          var test = await runTest([...testArgs, '-r', 'json']);

          expect(
              test.stdout,
              containsInOrder([
                '"messageType":"print","message":"hello","type":"print"',
              ]));

          await test.shouldExit(0);
        });

        if (runtime.isDartVM) {
          test('forwards stdout/stderr', () async {
            await d.file('test.dart', _testWithStdOutAndErr).create();
            var test = await runTest(testArgs, reporter: 'silent');

            expect(test.stdout, emitsThrough('hello'));
            expect(test.stderr, emits('world'));
            await test.shouldExit(0);
          },
              skip: Platform.isWindows && compiler == Compiler.exe
                  ? 'https://github.com/dart-lang/test/issues/2150'
                  : null);
        }
      });
    }
  }
}

final _goodTest = '''
  import 'package:test/test.dart';

  void main() {
    test("success", () {});
  }
''';

final _failingTest = '''
  import 'package:test/test.dart';

  void main() {
    test("failure", () {
      expect(1, 2);
    });
  }
''';

final _compileErrorTest = '''
int x = 'hello';

void main() {}
''';

final _throwingTest = "void main() => throw 'oh no';";

final _testWithPrints = '''
import 'package:test/test.dart';

void main() {
  print('hello');
  test('success', () {});
}''';

final _testWithStdOutAndErr = '''
import 'dart:io';
import 'package:test/test.dart';

void main() async {
  stdout.writeln('hello');
  await stdout.flush();
  stderr.writeln('world');
  test('success', () {});
}''';
