| // 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); | |
| } | |
| 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; | |
| } | |
| } |