blob: b4deec6a9653b58f1d8208a08f210ee55bbce607 [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 'package:args/args.dart';
import 'package:meta/meta.dart';
class TestArguments {
final List<String> rawArgs;
/// Arguments hydrated into their respective data type representations
/// ([bool], [String], [int], etc).
final ArgResults parsedArgs;
/// Raw string arguments to be forwarded down to each executed test.
///
/// These tokens have no impact whatsoever on how `fx test` finds, filters,
/// and invokes tests. They are exclusively used by the underlying tests.
final List<String> passThroughArgs;
/// Optional strings to pretend were passed. Key-value pairs are merged into
/// a single string in [rawArgs] if the key is not present in [rawArgs].
/// For flags (as opposed to options), pass either `null` or an empty string
/// as the value.
final Map<String, String> defaultRawArgs;
/// Similar to [passThroughArgs], but unique to e2e tests.
final List<String> e2ePassThroughArgs;
TestArguments({
@required this.rawArgs,
@required ArgParser parser,
this.defaultRawArgs,
}) : e2ePassThroughArgs = <String>[],
passThroughArgs = TestArguments._extractPassThroughArgs(rawArgs),
parsedArgs = TestArguments._parseArgs(
rawArgs,
parser,
defaultRawArgs: defaultRawArgs,
);
/// Splits a list of command line arguments into the half intended for
/// local use and the half intended to be passed through to sub-commands.
static List<List<String>> splitArgs(List<String> rawArgs) {
var dashDashIndex = rawArgs.indexOf('--');
if (dashDashIndex == -1) {
dashDashIndex = rawArgs.length;
}
return [
rawArgs.take(dashDashIndex).toList(),
rawArgs.skip(dashDashIndex + 1).toList(),
];
}
static List<String> _extractPassThroughArgs(List<String> rawArgs) {
return TestArguments.splitArgs(rawArgs)[1];
}
static ArgResults _parseArgs(
List<String> rawArgs,
ArgParser parser, {
Map<String, String> defaultRawArgs,
}) {
var localArgs = TestArguments.splitArgs(rawArgs)[0];
localArgs = TestArguments.addDefaults(localArgs, defaultRawArgs, parser);
return parser.parse(localArgs);
}
// ignore: prefer_constructors_over_static_methods
static List<String> addDefaults(
List<String> rawArgs,
Map<String, String> defaults,
ArgParser parser,
) {
if (defaults == null || defaults.isEmpty) return rawArgs;
var copy = List<String>.from(rawArgs);
const String invertToken = '--no-';
for (var key in defaults.keys) {
// For a default of "--flag", we only want to add if there is no "--flag"
// and no "--no-flag". The inverse is also true, for a default of
// "--no-flag", we have to check for both "--no-flag" and "--flag".
final oppositeKey = key.startsWith(invertToken)
// Opposite of an invert is to drop the invert and re-apply "--".
? '--${key.substring(invertToken.length)}'
// Opposite of a positive is to drop the leading "--" then add the
// inversion token.
: '$invertToken${key.substring(2)}';
final keysToCheckFor = <String>{key, oppositeKey};
// Grab the shorter key, which will be the one without a leading `--no-`.
// We need this to check for an abbreviation.
var primaryKey = key.length < oppositeKey.length ? key : oppositeKey;
// Trim leading "--"
primaryKey = primaryKey.substring(2);
if (parser.options[primaryKey]?.abbr != null) {
keysToCheckFor.add('-${parser.options[primaryKey].abbr}');
}
if (Set.from(rawArgs).intersection(keysToCheckFor).isEmpty) {
copy.add(key);
if (defaults[key] != null && defaults[key] != '') {
copy.add(defaults[key]);
}
}
}
return copy;
}
}