Cleaned up results formatter options hand-off.

* --results-formatter-options renamed to --results-formatter-option,
  with short version of -O

* Multiple --results-formatter-option=OPTION can be specified.  The
  comma-separating mechanism has been removed.

* XunitFormatter options modified: -n and -r are now short forms of
  --ignore-skip-name and --ignore-skip-reason.  Those long option
  names were tweaked lightly.  They also can be specified multiple
  times on the command line.  The comma-separating, multiple-pattern-
  per-option mechanism has been removed.

One can now specify:

  dotest.py --results-file stdout -O-ndsym -O-nlldb-mi

for example, to ignore reporting skips for dsym-related or lldb-mi-related
tests in the xUnit report.

llvm-svn: 248384
This commit is contained in:
Todd Fiala 2015-09-23 15:21:28 +00:00
parent 0d427986f5
commit ea73624e5f
4 changed files with 111 additions and 60 deletions

View File

@ -1068,43 +1068,87 @@ def get_test_runner_strategies(num_threads):
} }
def _remove_option(args, option_name, removal_count): def _remove_option(
args, long_option_name, short_option_name, takes_arg):
"""Removes option and related option arguments from args array. """Removes option and related option arguments from args array.
This method removes all short/long options that match the given
arguments.
@param args the array of command line arguments (in/out) @param args the array of command line arguments (in/out)
@param option_name the full command line representation of the
option that will be removed (including '--' or '-'). @param long_option_name the full command line representation of the
@param the count of elements to remove. A value of 1 will remove long-form option that will be removed (including '--').
just the found option, while 2 will remove the option and its first
argument. @param short_option_name the short version of the command line option
that will be removed (including '-').
@param takes_arg True if the option takes an argument.
""" """
try: if long_option_name is not None:
index = args.index(option_name) regex_string = "^" + long_option_name + "="
# Handle the exact match case. long_regex = re.compile(regex_string)
del args[index:index+removal_count] if short_option_name is not None:
return # Short options we only match the -X and assume
except ValueError: # any arg is one command line argument jammed together.
# Thanks to argparse not handling options with known arguments # i.e. -O--abc=1 is a single argument in the args list.
# like other options parsing libraries (see # We don't handle -O --abc=1, as argparse doesn't handle
# https://bugs.python.org/issue9334), we need to support the # it, either.
# --results-formatter-options={second-level-arguments} (note regex_string = "^" + short_option_name
# the equal sign to fool the first-level arguments parser into short_regex = re.compile(regex_string)
# not treating the second-level arguments as first-level
# options). We're certainly at risk of getting this wrong def remove_long_internal():
# since now we're forced into the business of trying to figure """Removes one matching long option from args.
# out what is an argument (although I think this @returns True if one was found and removed; False otherwise.
# implementation will suffice). """
regex_string = "^" + option_name + "=" try:
regex = re.compile(regex_string) index = args.index(long_option_name)
# Handle the exact match case.
if takes_arg:
removal_count = 2
else:
removal_count = 1
del args[index:index+removal_count]
return True
except ValueError:
# Thanks to argparse not handling options with known arguments
# like other options parsing libraries (see
# https://bugs.python.org/issue9334), we need to support the
# --results-formatter-options={second-level-arguments} (note
# the equal sign to fool the first-level arguments parser into
# not treating the second-level arguments as first-level
# options). We're certainly at risk of getting this wrong
# since now we're forced into the business of trying to figure
# out what is an argument (although I think this
# implementation will suffice).
for index in range(len(args)):
match = long_regex.search(args[index])
if match:
del args[index]
return True
return False
def remove_short_internal():
"""Removes one matching short option from args.
@returns True if one was found and removed; False otherwise.
"""
for index in range(len(args)): for index in range(len(args)):
match = regex.match(args[index]) match = short_regex.search(args[index])
if match: if match:
del args[index] del args[index]
return return True
print "failed to find regex '{}'".format(regex_string) return False
# We didn't find the option but we should have. removal_count = 0
raise Exception("failed to find option '{}' in args '{}'".format( while long_option_name is not None and remove_long_internal():
option_name, args)) removal_count += 1
while short_option_name is not None and remove_short_internal():
removal_count += 1
if removal_count == 0:
raise Exception(
"failed to find at least one of '{}', '{}' in options".format(
long_option_name, short_option_name))
def adjust_inferior_options(dotest_argv): def adjust_inferior_options(dotest_argv):
@ -1132,17 +1176,18 @@ def adjust_inferior_options(dotest_argv):
# we'll have inferiors spawn with the --results-port option and # we'll have inferiors spawn with the --results-port option and
# strip the original test runner options. # strip the original test runner options.
if dotest_options.results_file is not None: if dotest_options.results_file is not None:
_remove_option(dotest_argv, "--results-file", 2) _remove_option(dotest_argv, "--results-file", None, True)
if dotest_options.results_port is not None: if dotest_options.results_port is not None:
_remove_option(dotest_argv, "--results-port", 2) _remove_option(dotest_argv, "--results-port", None, True)
if dotest_options.results_formatter is not None: if dotest_options.results_formatter is not None:
_remove_option(dotest_argv, "--results-formatter", 2) _remove_option(dotest_argv, "--results-formatter", None, True)
if dotest_options.results_formatter_options is not None: if dotest_options.results_formatter_options is not None:
_remove_option(dotest_argv, "--results-formatter-options", 2) _remove_option(dotest_argv, "--results-formatter-option", "-O",
True)
# Remove test runner name if present. # Remove test runner name if present.
if dotest_options.test_runner_name is not None: if dotest_options.test_runner_name is not None:
_remove_option(dotest_argv, "--test-runner-name", 2) _remove_option(dotest_argv, "--test-runner-name", None, True)
def is_darwin_version_lower_than(target_version): def is_darwin_version_lower_than(target_version):

View File

@ -1000,12 +1000,10 @@ def setupTestResults():
# Handle formatter options for the results formatter class. # Handle formatter options for the results formatter class.
formatter_arg_parser = clazz.arg_parser() formatter_arg_parser = clazz.arg_parser()
if results_formatter_options and len(results_formatter_options) > 0: if results_formatter_options and len(results_formatter_options) > 0:
import shlex formatter_options = formatter_arg_parser.parse_args(
split_options = shlex.split(results_formatter_options) results_formatter_options)
else: else:
split_options = [] formatter_options = []
formatter_options = formatter_arg_parser.parse_args(split_options)
# Create the TestResultsFormatter given the processed options. # Create the TestResultsFormatter given the processed options.
results_formatter_object = clazz(results_file_object, formatter_options) results_formatter_object = clazz(results_file_object, formatter_options)

View File

@ -172,11 +172,13 @@ def create_parser():
'test events into some kind of meaningful report, written to ' 'test events into some kind of meaningful report, written to '
'the designated output results file-like object')) 'the designated output results file-like object'))
group.add_argument( group.add_argument(
'--results-formatter-options', '--results-formatter-option',
action='store', '-O',
help=('Specify comma-separated options to pass to the formatter. ' action='append',
'Use --results-formatter-options="--option1[,--option2[,...]]" ' dest='results_formatter_options',
'syntax. Note the "=" is critical, and don\'t use whitespace.')) help=('Specify an option to pass to the formatter. '
'Use --results-formatter-option="--option1=val1" '
'syntax. Note the "=" is critical, don\'t include whitespace.'))
group.add_argument( group.add_argument(
'--event-add-entries', '--event-add-entries',
action='store', action='store',

View File

@ -538,19 +538,25 @@ class XunitFormatter(ResultsFormatter):
help=('cause unknown test events to generate ' help=('cause unknown test events to generate '
'a python assert. Default is to ignore.')) 'a python assert. Default is to ignore.'))
parser.add_argument( parser.add_argument(
"--ignore-skip-matching-name", "--ignore-skip-name",
action="store", "-n",
help=('one or more comma-separated python regex patterns, where ' metavar='PATTERN',
action="append",
dest='ignore_skip_name_patterns',
help=('a python regex pattern, where '
'any skipped test with a test method name where regex ' 'any skipped test with a test method name where regex '
'matches (via search) will be ignored for xUnit test ' 'matches (via search) will be ignored for xUnit test '
'result purposes.')) 'result purposes. Can be specified multiple times.'))
parser.add_argument( parser.add_argument(
"--ignore-skip-matching-reason", "--ignore-skip-reason",
action="store", "-r",
help=('one or more comma-separated python regex patterns, where ' metavar='PATTERN',
action="append",
dest='ignore_skip_reason_patterns',
help=('a python regex pattern, where '
'any skipped test with a skip reason where the regex ' 'any skipped test with a skip reason where the regex '
'matches (via search) will be ignored for xUnit test ' 'matches (via search) will be ignored for xUnit test '
'result purposes.')) 'result purposes. Can be specified multiple times.'))
parser.add_argument( parser.add_argument(
"--xpass", action="store", choices=results_mapping_choices, "--xpass", action="store", choices=results_mapping_choices,
default=XunitFormatter.RM_FAILURE, default=XunitFormatter.RM_FAILURE,
@ -564,7 +570,7 @@ class XunitFormatter(ResultsFormatter):
return parser return parser
@staticmethod @staticmethod
def _build_regex_list_from_option(option): def _build_regex_list_from_patterns(patterns):
"""Builds a list of compiled regexes from option value. """Builds a list of compiled regexes from option value.
@param option string containing a comma-separated list of regex @param option string containing a comma-separated list of regex
@ -574,8 +580,8 @@ class XunitFormatter(ResultsFormatter):
patterns provided. patterns provided.
""" """
regex_list = [] regex_list = []
if option is not None and len(option) > 0: if patterns is not None:
for pattern in option.split(","): for pattern in patterns:
regex_list.append(re.compile(pattern)) regex_list.append(re.compile(pattern))
return regex_list return regex_list
@ -591,11 +597,11 @@ class XunitFormatter(ResultsFormatter):
self.invalid_xml_re = XunitFormatter._build_illegal_xml_regex() self.invalid_xml_re = XunitFormatter._build_illegal_xml_regex()
self.total_test_count = 0 self.total_test_count = 0
self.ignore_skip_name_regexes = ( self.ignore_skip_name_regexes = (
XunitFormatter._build_regex_list_from_option( XunitFormatter._build_regex_list_from_patterns(
options.ignore_skip_matching_name)) options.ignore_skip_name_patterns))
self.ignore_skip_reason_regexes = ( self.ignore_skip_reason_regexes = (
XunitFormatter._build_regex_list_from_option( XunitFormatter._build_regex_list_from_patterns(
options.ignore_skip_matching_reason)) options.ignore_skip_reason_patterns))
self.elements = { self.elements = {
"successes": [], "successes": [],