| // 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: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. |
| late final 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(); |
| } |
| } |