// Copyright 2013 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:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; import 'package:flutter_plugin_tools/src/dart_test_command.dart'; import 'package:platform/platform.dart'; import 'package:test/test.dart'; import 'mocks.dart'; import 'util.dart'; void main() { group('TestCommand', () { late FileSystem fileSystem; late Platform mockPlatform; late Directory packagesDir; late CommandRunner runner; late RecordingProcessRunner processRunner; setUp(() { fileSystem = MemoryFileSystem(); mockPlatform = MockPlatform(); packagesDir = createPackagesDirectory(fileSystem: fileSystem); processRunner = RecordingProcessRunner(); final DartTestCommand command = DartTestCommand( packagesDir, processRunner: processRunner, platform: mockPlatform, ); runner = CommandRunner('test_test', 'Test for $DartTestCommand'); runner.addCommand(command); }); test('legacy "test" name still works', () async { final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, extraFiles: ['test/a_test.dart']); await runCapturingPrint(runner, ['test']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), const ['test', '--color'], plugin.path), ]), ); }); test('runs flutter test on each plugin', () async { final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, extraFiles: ['test/empty_test.dart']); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint(runner, ['dart-test']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), const ['test', '--color'], plugin1.path), ProcessCall(getFlutterCommand(mockPlatform), const ['test', '--color'], plugin2.path), ]), ); }); test('runs flutter test on Flutter package example tests', () async { final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, extraFiles: [ 'test/empty_test.dart', 'example/test/an_example_test.dart' ]); await runCapturingPrint(runner, ['dart-test']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), const ['test', '--color'], plugin.path), ProcessCall(getFlutterCommand(mockPlatform), const ['test', '--color'], getExampleDir(plugin).path), ]), ); }); test('fails when Flutter tests fail', () async { createFakePlugin('plugin1', packagesDir, extraFiles: ['test/empty_test.dart']); createFakePlugin('plugin2', packagesDir, extraFiles: ['test/empty_test.dart']); processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ FakeProcessInfo( MockProcess(exitCode: 1), ['dart-test']), // plugin 1 test FakeProcessInfo(MockProcess(), ['dart-test']), // plugin 2 test ]; Error? commandError; final List output = await runCapturingPrint( runner, ['dart-test'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('The following packages had errors:'), contains(' plugin1'), ])); }); test('skips testing plugins without test directory', () async { createFakePlugin('plugin1', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint(runner, ['dart-test']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), const ['test', '--color'], plugin2.path), ]), ); }); test('runs dart run test on non-Flutter packages', () async { final RepositoryPackage plugin = createFakePlugin('a', packagesDir, extraFiles: ['test/empty_test.dart']); final RepositoryPackage package = createFakePackage('b', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint( runner, ['dart-test', '--enable-experiment=exp1']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const ['test', '--color', '--enable-experiment=exp1'], plugin.path), ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall( 'dart', const ['run', '--enable-experiment=exp1', 'test'], package.path), ]), ); }); test('runs dart run test on non-Flutter package examples', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: [ 'test/empty_test.dart', 'example/test/an_example_test.dart' ]); await runCapturingPrint(runner, ['dart-test']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall('dart', const ['run', 'test'], package.path), ProcessCall('dart', const ['pub', 'get'], getExampleDir(package).path), ProcessCall('dart', const ['run', 'test'], getExampleDir(package).path), ]), ); }); test('fails when getting non-Flutter package dependencies fails', () async { createFakePackage('a_package', packagesDir, extraFiles: ['test/empty_test.dart']); processRunner.mockProcessesForExecutable['dart'] = [ FakeProcessInfo(MockProcess(exitCode: 1), ['pub', 'get']) ]; Error? commandError; final List output = await runCapturingPrint( runner, ['dart-test'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('Unable to fetch dependencies'), contains('The following packages had errors:'), contains(' a_package'), ])); }); test('fails when non-Flutter tests fail', () async { createFakePackage('a_package', packagesDir, extraFiles: ['test/empty_test.dart']); processRunner.mockProcessesForExecutable['dart'] = [ FakeProcessInfo(MockProcess(), ['pub', 'get']), FakeProcessInfo(MockProcess(exitCode: 1), ['run']), // run test ]; Error? commandError; final List output = await runCapturingPrint( runner, ['dart-test'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('The following packages had errors:'), contains(' a_package'), ])); }); test('converts --platform=vm to no argument for flutter test', () async { final RepositoryPackage plugin = createFakePlugin( 'some_plugin', packagesDir, extraFiles: ['test/empty_test.dart'], ); await runCapturingPrint(runner, ['dart-test', '--platform=vm']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), const ['test', '--color'], plugin.path), ]), ); }); test('throws for an unrecognized test_on type', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['test/empty_test.dart'], ); package.directory.childFile('dart_test.yaml').writeAsStringSync(''' test_on: unknown '''); Error? commandError; final List output = await runCapturingPrint( runner, ['dart-test', '--platform=vm'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder( [ contains('Unknown "test_on" value: "unknown"\n' "If this value needs to be supported for this package's " 'tests, please update the repository tooling to support more ' 'test_on modes.'), ], )); }); test('throws for an valid but complex test_on directive', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['test/empty_test.dart'], ); package.directory.childFile('dart_test.yaml').writeAsStringSync(''' test_on: vm && browser '''); Error? commandError; final List output = await runCapturingPrint( runner, ['dart-test', '--platform=vm'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder( [ contains('Unknown "test_on" value: "vm && browser"\n' "If this value needs to be supported for this package's " 'tests, please update the repository tooling to support more ' 'test_on modes.'), ], )); }); test('runs in Chrome when requested for Flutter package', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, isFlutter: true, extraFiles: ['test/empty_test.dart'], ); await runCapturingPrint( runner, ['dart-test', '--platform=chrome']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const [ 'test', '--color', '--platform=chrome', ], package.path), ]), ); }); test('runs in Chrome (wasm) when requested for Flutter package', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, isFlutter: true, extraFiles: ['test/empty_test.dart'], ); await runCapturingPrint( runner, ['dart-test', '--platform=chrome', '--wasm']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const [ 'test', '--color', '--platform=chrome', '--wasm', ], package.path), ]), ); }); test('runs in Chrome by default for Flutter plugins that implement web', () async { final RepositoryPackage plugin = createFakePlugin( 'some_plugin_web', packagesDir, extraFiles: ['test/empty_test.dart'], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); await runCapturingPrint(runner, ['dart-test']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const [ 'test', '--color', '--platform=chrome', ], plugin.path), ]), ); }); test('runs in Chrome when requested for Flutter plugins that implement web', () async { final RepositoryPackage plugin = createFakePlugin( 'some_plugin_web', packagesDir, extraFiles: ['test/empty_test.dart'], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); await runCapturingPrint( runner, ['dart-test', '--platform=chrome']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const [ 'test', '--color', '--platform=chrome', ], plugin.path), ]), ); }); test('runs in Chrome when requested for Flutter plugin that endorse web', () async { final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: ['test/empty_test.dart'], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.federated), }, ); await runCapturingPrint( runner, ['dart-test', '--platform=chrome']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const [ 'test', '--color', '--platform=chrome', ], plugin.path), ]), ); }); test('skips running non-web plugins in browser mode', () async { createFakePlugin( 'non_web_plugin', packagesDir, extraFiles: ['test/empty_test.dart'], ); final List output = await runCapturingPrint( runner, ['dart-test', '--platform=chrome']); expect( output, containsAllInOrder([ contains("Non-web plugin tests don't need web testing."), ])); expect( processRunner.recordedCalls, orderedEquals([]), ); }); test('skips running web plugins in explicit vm mode', () async { createFakePlugin( 'some_plugin_web', packagesDir, extraFiles: ['test/empty_test.dart'], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); final List output = await runCapturingPrint( runner, ['dart-test', '--platform=vm']); expect( output, containsAllInOrder([ contains("Web plugin tests don't need vm testing."), ])); expect( processRunner.recordedCalls, orderedEquals([]), ); }); test('does not skip for plugins that endorse web', () async { final RepositoryPackage plugin = createFakePlugin( 'some_plugin', packagesDir, extraFiles: ['test/empty_test.dart'], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.federated), platformAndroid: const PlatformDetails(PlatformSupport.federated), }, ); await runCapturingPrint( runner, ['dart-test', '--platform=chrome']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const [ 'test', '--color', '--platform=chrome', ], plugin.path), ]), ); }); test('runs in Chrome when requested for Dart package', () async { final RepositoryPackage package = createFakePackage( 'package', packagesDir, extraFiles: ['test/empty_test.dart'], ); await runCapturingPrint( runner, ['dart-test', '--platform=chrome']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall('dart', const ['run', 'test', '--platform=chrome'], package.path), ]), ); }); test('runs in Chrome (wasm) when requested for Dart package', () async { final RepositoryPackage package = createFakePackage( 'package', packagesDir, extraFiles: ['test/empty_test.dart'], ); await runCapturingPrint( runner, ['dart-test', '--platform=chrome', '--wasm']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall( 'dart', const [ 'run', 'test', '--platform=chrome', '--compiler=dart2wasm', ], package.path), ]), ); }); test('skips running in browser mode if package opts out', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['test/empty_test.dart'], ); package.directory.childFile('dart_test.yaml').writeAsStringSync(''' test_on: vm '''); final List output = await runCapturingPrint( runner, ['dart-test', '--platform=chrome']); expect( output, containsAllInOrder([ contains('Package has opted out of non-vm testing.'), ])); expect( processRunner.recordedCalls, orderedEquals([]), ); }); test('does not skip running vm in vm mode', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['test/empty_test.dart'], ); package.directory.childFile('dart_test.yaml').writeAsStringSync(''' test_on: vm '''); final List output = await runCapturingPrint( runner, ['dart-test', '--platform=vm']); expect( output, isNot(containsAllInOrder([ contains('Package has opted out'), ]))); expect( processRunner.recordedCalls, isNotEmpty, ); }); test('skips running in vm mode if package opts out', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['test/empty_test.dart'], ); package.directory.childFile('dart_test.yaml').writeAsStringSync(''' test_on: browser '''); final List output = await runCapturingPrint( runner, ['dart-test', '--platform=vm']); expect( output, containsAllInOrder([ contains('Package has opted out of vm testing.'), ])); expect( processRunner.recordedCalls, orderedEquals([]), ); }); test('does not skip running browser in browser mode', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['test/empty_test.dart'], ); package.directory.childFile('dart_test.yaml').writeAsStringSync(''' test_on: browser '''); final List output = await runCapturingPrint( runner, ['dart-test', '--platform=browser']); expect( output, isNot(containsAllInOrder([ contains('Package has opted out'), ]))); expect( processRunner.recordedCalls, isNotEmpty, ); }); test('tries to run for a test_on that the tool does not recognize', () async { final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['test/empty_test.dart'], ); package.directory.childFile('dart_test.yaml').writeAsStringSync(''' test_on: !vm && firefox '''); await runCapturingPrint(runner, ['dart-test']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall('dart', const ['run', 'test'], package.path), ]), ); }); test('enable-experiment flag', () async { final RepositoryPackage plugin = createFakePlugin('a', packagesDir, extraFiles: ['test/empty_test.dart']); final RepositoryPackage package = createFakePackage('b', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint( runner, ['dart-test', '--enable-experiment=exp1']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const ['test', '--color', '--enable-experiment=exp1'], plugin.path), ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall( 'dart', const ['run', '--enable-experiment=exp1', 'test'], package.path), ]), ); }); }); }