|  | // Copyright 2020 The Fuchsia 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:io'; | 
|  |  | 
|  | import 'package:args/args.dart'; | 
|  | import 'package:fxtest/fxtest.dart'; | 
|  | import 'package:fxutils/fxutils.dart'; | 
|  | import 'package:meta/meta.dart'; | 
|  | import 'package:pedantic/pedantic.dart'; | 
|  |  | 
|  | /// Translator for command line arguments into [FuchsiaTestCommand] primitives. | 
|  | class FuchsiaTestCommandCli { | 
|  | /// Callable that prints help/usage information for when the user passes | 
|  | /// invalid arguments or "--help". | 
|  | final Function(ArgParser) usage; | 
|  |  | 
|  | /// Fully-hydrated object containing answers to every runtime question. | 
|  | /// Derivable from the set of raw arguments passed in by the user. | 
|  | TestsConfig testsConfig; | 
|  |  | 
|  | /// The underlying class which does all the work. | 
|  | FuchsiaTestCommand _cmd; | 
|  |  | 
|  | /// Used to create any new directories needed to house test output / artifacts. | 
|  | final DirectoryBuilder directoryBuilder; | 
|  |  | 
|  | final IFxEnv fxEnv; | 
|  |  | 
|  | FuchsiaTestCommandCli( | 
|  | List<String> rawArgs, { | 
|  | @required this.usage, | 
|  | @required this.fxEnv, | 
|  | this.directoryBuilder, | 
|  | }) { | 
|  | testsConfig = TestsConfig.fromRawArgs( | 
|  | rawArgs: rawArgs, | 
|  | // When running real tests, turn on logging. Passing `--no-log` explicitly | 
|  | // will still override this. | 
|  | // The `null` value is not a falsy indicator - it is because `--log` is a | 
|  | // flag and thus does not accept a value. | 
|  | defaultRawArgs: {'--log': null}, | 
|  | fxEnv: fxEnv, | 
|  | ); | 
|  | } | 
|  |  | 
|  | Future<bool> preRunChecks( | 
|  | Function(Object) stdoutWriter, { | 
|  | ProcessLauncher processLauncher, | 
|  | }) async { | 
|  | if (testsConfig.testArguments.parsedArgs['help']) { | 
|  | usage(fxTestArgParser); | 
|  | return false; | 
|  | } | 
|  | if (testsConfig.testArguments.parsedArgs['printtests']) { | 
|  | processLauncher ??= ProcessLauncher(); | 
|  | ProcessResult result = await processLauncher.run('cat', ['tests.json'], | 
|  | workingDirectory: fxEnv.outputDir); | 
|  | stdoutWriter(result.stdout); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // This command uses extensive fx re-entry, so it's good to make sure fx | 
|  | // is actually located where we expect | 
|  | final fxFile = File(testsConfig.fxEnv.fx); | 
|  | if (!fxFile.existsSync()) { | 
|  | throw MissingFxException(); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | Future<void> run() async { | 
|  | _cmd = createCommand(); | 
|  |  | 
|  | // Without waiting, start the command. | 
|  | unawaited( | 
|  | // But register a listener for when it completes, which resolves the | 
|  | // stdout future. | 
|  | _cmd.runTestSuite(TestsManifestReader()).then((_) { | 
|  | // Once the actual command finishes without problems, close the stdout. | 
|  | _cmd.dispose(); | 
|  | }), | 
|  | ); | 
|  |  | 
|  | // Register a listener for when the `stdout` closes. | 
|  | try { | 
|  | await Future.wait( | 
|  | _cmd.outputFormatters.map((var f) => f.stdOutClosedFuture), | 
|  | eagerError: true, | 
|  | ); | 
|  | } on Exception { | 
|  | if (exitCode == 0) { | 
|  | rethrow; | 
|  | } else { | 
|  | throw OutputClosedException(exitCode); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | FuchsiaTestCommand createCommand() => FuchsiaTestCommand.fromConfig( | 
|  | testsConfig, | 
|  | directoryBuilder: directoryBuilder, | 
|  | testRunnerBuilder: (TestsConfig testsConfig) => SymbolizingTestRunner( | 
|  | fx: testsConfig.fxEnv.fx, | 
|  | ), | 
|  | ); | 
|  |  | 
|  | Future<void> terminateEarly() async { | 
|  | _cmd?.emitEvent(AllTestsCompleted()); | 
|  | } | 
|  |  | 
|  | Future<void> cleanUp() async { | 
|  | await _cmd?.cleanUp(); | 
|  | } | 
|  | } |