// Copyright 2015 The Chromium 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 'dart:async'; import 'dart:io'; import 'package:path/path.dart' as path; import 'package:test/src/executable.dart' as executable; import '../artifacts.dart'; import '../base/context.dart'; import '../build_configuration.dart'; import '../runner/flutter_command.dart'; import '../test/loader.dart' as loader; class TestCommand extends FlutterCommand { String get name => 'test'; String get description => 'Runs Flutter unit tests for the current project.'; bool get requiresProjectRoot => false; String get projectRootValidationErrorMessage { return 'Error: No pubspec.yaml file found.\n' 'If you wish to run the tests in the Flutter repository\'s \'flutter\' package,\n' 'pass --flutter-repo before any test paths. Otherwise, run this command from the\n' 'root of your project. Test files must be called *_test.dart and must reside in\n' 'the package\'s \'test\' directory (or one of its subdirectories).'; } Future<String> _getShellPath(BuildConfiguration config) async { if (config.type == BuildType.prebuilt) { Artifact artifact = ArtifactStore.getArtifact( type: ArtifactType.shell, targetPlatform: config.targetPlatform); return await ArtifactStore.getPath(artifact); } else { switch (config.targetPlatform) { case TargetPlatform.linux: return path.join(config.buildDir, 'sky_shell'); case TargetPlatform.mac: return path.join(config.buildDir, 'SkyShell.app', 'Contents', 'MacOS', 'SkyShell'); default: throw new Exception('Unsupported platform.'); } } } TestCommand() { argParser.addFlag('flutter-repo', help: 'Run tests from the \'flutter\' package in the Flutter repository instead of the current directory.', defaultsTo: false); } Iterable<String> _findTests(Directory directory) { return directory.listSync(recursive: true, followLinks: false) .where((FileSystemEntity entity) => entity.path.endsWith('_test.dart') && FileSystemEntity.isFileSync(entity.path)) .map((FileSystemEntity entity) => path.absolute(entity.path)); } Directory get _flutterUnitTestDir { return new Directory(path.join(ArtifactStore.flutterRoot, 'packages', 'flutter', 'test')); } Directory get _currentPackageTestDir { // We don't scan the entire package, only the test/ subdirectory, so that // files with names like like "hit_test.dart" don't get run. return new Directory('test'); } Future<int> _runTests(List<String> testArgs, Directory testDirectory) async { Directory currentDirectory = Directory.current; try { Directory.current = testDirectory; return await executable.main(testArgs); } finally { Directory.current = currentDirectory; } } @override Future<int> runInProject() async { List<String> testArgs = argResults.rest.map((String testPath) => path.absolute(testPath)).toList(); final bool runFlutterTests = argResults['flutter-repo']; if (!runFlutterTests && !validateProjectRoot()) return 1; // If we're running the flutter tests, we want to use the packages directory // from the flutter package in order to find the proper shell binary. if (runFlutterTests && ArtifactStore.packageRoot == 'packages') ArtifactStore.packageRoot = path.join(ArtifactStore.flutterRoot, 'packages', 'flutter', 'packages'); Directory testDir = runFlutterTests ? _flutterUnitTestDir : _currentPackageTestDir; if (testArgs.isEmpty) { if (!testDir.existsSync()) { printError("Test directory '${testDir.path}' not found."); return 1; } testArgs.addAll(_findTests(testDir)); } testArgs.insert(0, '--'); if (Platform.environment['TERM'] == 'dumb') testArgs.insert(0, '--no-color'); List<BuildConfiguration> configs = buildConfigurations; bool foundOne = false; loader.installHook(); for (BuildConfiguration config in configs) { if (!config.testable) continue; foundOne = true; loader.shellPath = path.absolute(await _getShellPath(config)); if (!FileSystemEntity.isFileSync(loader.shellPath)) { printError('Cannot find Flutter shell at ${loader.shellPath}'); return 1; } await _runTests(testArgs, testDir); if (exitCode != 0) return exitCode; } if (!foundOne) { printError('At least one of --debug or --release must be set, to specify the local build products to test.'); return 1; } return 0; } }