blob: 7caef56670aeb871ff586baa52c444eeed4eb1a6 [file] [log] [blame]
// 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;
List<String> rawArgs, {
required this.usage,
required this.fxEnv,
}) {
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']) {
return false;
if (testsConfig.testArguments.parsedArgs['printtests']) {
processLauncher ??= ProcessLauncher();
ProcessResult result = await'cat', ['tests.json'],
workingDirectory: fxEnv.outputDir);
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.
// 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.
// Register a listener for when the `stdout` closes.
try {
await Future.wait(
_cmd! f) => f.stdOutClosedFuture),
eagerError: true,
} on Exception {
if (exitCode == 0) {
} else {
throw OutputClosedException(exitCode);
FuchsiaTestCommand createCommand() => FuchsiaTestCommand.fromConfig(
directoryBuilder: directoryBuilder,
testRunnerBuilder: (TestsConfig testsConfig) => SymbolizingTestRunner(
fx: testsConfig.fxEnv.fx,
Future<void> terminateEarly() async {
Future<void> cleanUp() async {
await _cmd?.cleanUp();