1// Copyright 2015 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import 'dart:async'; 6import 'dart:convert' show json; 7import 'dart:math' as math; 8 9import 'package:args/args.dart'; 10import 'package:flutter_tools/src/base/common.dart'; 11import 'package:flutter_tools/src/base/context.dart'; 12import 'package:flutter_tools/src/base/file_system.dart'; 13import 'package:flutter_tools/src/base/io.dart'; 14import 'package:flutter_tools/src/base/platform.dart'; 15import 'package:flutter_tools/src/cache.dart'; 16import 'package:flutter_tools/src/context_runner.dart'; 17import 'package:flutter_tools/src/dart/package_map.dart'; 18import 'package:flutter_tools/src/artifacts.dart'; 19import 'package:flutter_tools/src/globals.dart'; 20import 'package:flutter_tools/src/project.dart'; 21import 'package:flutter_tools/src/reporting/reporting.dart'; 22import 'package:flutter_tools/src/test/coverage_collector.dart'; 23import 'package:flutter_tools/src/test/runner.dart'; 24 25// This was largely inspired by lib/src/commands/test.dart. 26 27const String _kOptionPackages = 'packages'; 28const String _kOptionShell = 'shell'; 29const String _kOptionTestDirectory = 'test-directory'; 30const String _kOptionSdkRoot = 'sdk-root'; 31const String _kOptionIcudtl = 'icudtl'; 32const String _kOptionTests = 'tests'; 33const String _kOptionCoverageDirectory = 'coverage-directory'; 34const List<String> _kRequiredOptions = <String>[ 35 _kOptionPackages, 36 _kOptionShell, 37 _kOptionSdkRoot, 38 _kOptionIcudtl, 39 _kOptionTests, 40]; 41const String _kOptionCoverage = 'coverage'; 42const String _kOptionCoveragePath = 'coverage-path'; 43 44void main(List<String> args) { 45 runInContext<void>(() => run(args), overrides: <Type, Generator>{ 46 Usage: () => DisabledUsage(), 47 }); 48} 49 50Future<void> run(List<String> args) async { 51 final ArgParser parser = ArgParser() 52 ..addOption(_kOptionPackages, help: 'The .packages file') 53 ..addOption(_kOptionShell, help: 'The Flutter shell binary') 54 ..addOption(_kOptionTestDirectory, help: 'Directory containing the tests') 55 ..addOption(_kOptionSdkRoot, help: 'Path to the SDK platform files') 56 ..addOption(_kOptionIcudtl, help: 'Path to the ICU data file') 57 ..addOption(_kOptionTests, help: 'Path to json file that maps Dart test files to precompiled dill files') 58 ..addOption(_kOptionCoverageDirectory, help: 'The path to the directory that will have coverage collected') 59 ..addFlag(_kOptionCoverage, 60 defaultsTo: false, 61 negatable: false, 62 help: 'Whether to collect coverage information.', 63 ) 64 ..addOption(_kOptionCoveragePath, 65 defaultsTo: 'coverage/lcov.info', 66 help: 'Where to store coverage information (if coverage is enabled).', 67 ); 68 final ArgResults argResults = parser.parse(args); 69 if (_kRequiredOptions 70 .any((String option) => !argResults.options.contains(option))) { 71 throwToolExit('Missing option! All options must be specified.'); 72 } 73 final Directory tempDir = 74 fs.systemTempDirectory.createTempSync('flutter_fuchsia_tester.'); 75 try { 76 Cache.flutterRoot = tempDir.path; 77 78 final String shellPath = fs.file(argResults[_kOptionShell]).resolveSymbolicLinksSync(); 79 if (!fs.isFileSync(shellPath)) { 80 throwToolExit('Cannot find Flutter shell at $shellPath'); 81 } 82 83 final Directory sdkRootSrc = fs.directory(argResults[_kOptionSdkRoot]); 84 if (!fs.isDirectorySync(sdkRootSrc.path)) { 85 throwToolExit('Cannot find SDK files at ${sdkRootSrc.path}'); 86 } 87 Directory coverageDirectory; 88 final String coverageDirectoryPath = argResults[_kOptionCoverageDirectory]; 89 if (coverageDirectoryPath != null) { 90 if (!fs.isDirectorySync(coverageDirectoryPath)) { 91 throwToolExit('Cannot find coverage directory at $coverageDirectoryPath'); 92 } 93 coverageDirectory = fs.directory(coverageDirectoryPath); 94 } 95 96 // Put the tester shell where runTests expects it. 97 // TODO(garymm): Switch to a Fuchsia-specific Artifacts impl. 98 final Link testerDestLink = 99 fs.link(artifacts.getArtifactPath(Artifact.flutterTester)); 100 testerDestLink.parent.createSync(recursive: true); 101 testerDestLink.createSync(fs.path.absolute(shellPath)); 102 103 final Directory sdkRootDest = 104 fs.directory(artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath)); 105 sdkRootDest.createSync(recursive: true); 106 for (FileSystemEntity artifact in sdkRootSrc.listSync()) { 107 fs.link(sdkRootDest.childFile(artifact.basename).path).createSync(artifact.path); 108 } 109 // TODO(tvolkert): Remove once flutter_tester no longer looks for this. 110 fs.link(sdkRootDest.childFile('platform.dill').path).createSync('platform_strong.dill'); 111 112 PackageMap.globalPackagesPath = 113 fs.path.normalize(fs.path.absolute(argResults[_kOptionPackages])); 114 115 Directory testDirectory; 116 CoverageCollector collector; 117 if (argResults['coverage']) { 118 collector = CoverageCollector( 119 libraryPredicate: (String libraryName) { 120 // If we have a specified coverage directory then accept all libraries. 121 if (coverageDirectory != null) { 122 return true; 123 } 124 final String projectName = FlutterProject.current().manifest.appName; 125 return libraryName.contains(projectName); 126 }); 127 if (!argResults.options.contains(_kOptionTestDirectory)) { 128 throwToolExit('Use of --coverage requires setting --test-directory'); 129 } 130 testDirectory = fs.directory(argResults[_kOptionTestDirectory]); 131 } 132 133 134 final Map<String, String> tests = <String, String>{}; 135 final List<Map<String, dynamic>> jsonList = List<Map<String, dynamic>>.from( 136 json.decode(fs.file(argResults[_kOptionTests]).readAsStringSync())); 137 for (Map<String, dynamic> map in jsonList) { 138 final String source = fs.file(map['source']).resolveSymbolicLinksSync(); 139 final String dill = fs.file(map['dill']).resolveSymbolicLinksSync(); 140 tests[source] = dill; 141 } 142 143 exitCode = await runTests( 144 tests.keys.toList(), 145 workDir: testDirectory, 146 watcher: collector, 147 ipv6: false, 148 enableObservatory: collector != null, 149 precompiledDillFiles: tests, 150 concurrency: math.max(1, platform.numberOfProcessors - 2), 151 icudtlPath: fs.path.absolute(argResults[_kOptionIcudtl]), 152 coverageDirectory: coverageDirectory, 153 ); 154 155 if (collector != null) { 156 // collector expects currentDirectory to be the root of the dart 157 // package (i.e. contains lib/ and test/ sub-dirs). In some cases, 158 // test files may appear to be in the root directory. 159 if (coverageDirectory == null) { 160 fs.currentDirectory = testDirectory.parent; 161 } else { 162 fs.currentDirectory = testDirectory; 163 } 164 if (!await collector.collectCoverageData(argResults[_kOptionCoveragePath], coverageDirectory: coverageDirectory)) 165 throwToolExit('Failed to collect coverage data'); 166 } 167 } finally { 168 tempDir.deleteSync(recursive: true); 169 } 170 // TODO(ianh): There's apparently some sort of lost async task keeping the 171 // process open. Remove the next line once that's been resolved. 172 exit(exitCode); 173} 174