blob: f5220af0506bf2adc0915711822ca8c34e1be008 [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:io/ansi.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;
final FuchsiaLocator _fuchsiaLocator;
FuchsiaTestCommandCli(
List<String> rawArgs, {
@required this.usage,
FuchsiaLocator fuchsiaLocator,
}) : _fuchsiaLocator = fuchsiaLocator ?? FuchsiaLocator.shared {
_testsConfig = TestsConfig.fromRawArgs(
rawArgs: rawArgs,
fuchsiaLocator: _fuchsiaLocator,
);
}
Future<bool> preRunChecks(
String fxLocation,
Function(Object) stdoutWriter,
) async {
if (_testsConfig.testArguments.parsedArgs['help']) {
usage(fxTestArgParser);
return false;
}
if (_testsConfig.testArguments.parsedArgs['printtests']) {
ProcessResult result = await Process.run('cat', ['tests.json'],
workingDirectory: FuchsiaLocator.shared.buildDir);
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
var fxFile = File(fxLocation);
if (!fxFile.existsSync()) {
throw MissingFxException();
}
return true;
}
Future<void> run() async {
_cmd = createCommand(_testsConfig);
if (_testsConfig.flags.shouldRebuild) {
_cmd.outputFormatter.update(
TestInfo(wrapWith('> fx build', [green, styleBold])),
);
await fxCommandRun(FuchsiaLocator.shared.fx, 'build');
}
// 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.outputFormatter.close();
}),
);
// Register a listener for when the `stdout` closes.
await _cmd.outputFormatter.stdOutClosedFuture.catchError((err) {
// If we have not yet determined a failing error code, then go with
// whatever is baked into this error.
if (exitCode == 0) {
throw err;
} else {
// However, if we do already have an `exitCode` (from a failing test),
// use that
throw OutputClosedException(exitCode);
}
});
}
FuchsiaTestCommand createCommand(TestsConfig testsConfig) =>
FuchsiaTestCommand.fromConfig(
testsConfig,
testRunnerBuilder: (TestsConfig testsConfig) => SymbolizingTestRunner(
fx: testsConfig.fuchsiaLocator.fx,
),
);
Future<void> terminateEarly() async {
_cmd?.emitEvent(AllTestsCompleted());
}
Future<void> cleanUp() async {
await _cmd?.cleanUp();
}
}