Skip to content

Commit

Permalink
android: generalize support for --test-launcher-filter-file.
Browse files Browse the repository at this point in the history
This adds filter file support to all test types that previously
supported gtest-style test filtering, notably including instrumentation
and junit tests.

This was requested in crrev.com/c/1208442

Change-Id: I552843898735b52b773bdc397c39ecf64e4df275
Reviewed-on: https://chromium-review.googlesource.com/1214083
Commit-Queue: John Budorick <jbudorick@chromium.org>
Reviewed-by: agrieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589682}
  • Loading branch information
jbudorick authored and Commit Bot committed Sep 7, 2018
1 parent 45fe7ce commit 0a582d4
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 127 deletions.
1 change: 1 addition & 0 deletions build/android/PRESUBMIT.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def J(*dirs):
J('pylib', 'utils', 'device_dependencies_test.py'),
J('pylib', 'utils', 'dexdump_test.py'),
J('pylib', 'utils', 'proguard_test.py'),
J('pylib', 'utils', 'test_filter_test.py'),
],
env=pylib_test_env))

Expand Down
39 changes: 2 additions & 37 deletions build/android/pylib/gtest/gtest_test_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from pylib.base import base_test_result
from pylib.base import test_instance
from pylib.symbols import stack_symbolizer
from pylib.utils import test_filter

with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
import unittest_util # pylint: disable=import-error
Expand Down Expand Up @@ -234,35 +235,6 @@ def ParseGTestXML(xml_content):
return results


def ConvertTestFilterFileIntoGTestFilterArgument(input_lines):
"""Converts test filter file contents into --gtest_filter argument.
See //testing/buildbot/filters/README.md for description of the
syntax that |input_lines| are expected to follow.
See
https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#running-a-subset-of-the-tests
for description of the syntax that --gtest_filter argument should follow.
Args:
input_lines: An iterable (e.g. a list or a file) containing input lines.
Returns:
a string suitable for feeding as an argument of --gtest_filter parameter.
"""
# Strip comments and whitespace from each line and filter non-empty lines.
stripped_lines = (l.split('#', 1)[0].strip() for l in input_lines)
filter_lines = list(l for l in stripped_lines if l)

# Split the tests into positive and negative patterns (gtest treats
# every pattern after the first '-' sign as an exclusion).
positive_patterns = ':'.join(l for l in filter_lines if l[0] != '-')
negative_patterns = ':'.join(l[1:] for l in filter_lines if l[0] == '-')
if negative_patterns:
negative_patterns = '-' + negative_patterns

# Join the filter lines into one, big --gtest_filter argument.
return positive_patterns + negative_patterns

def TestNameWithoutDisabledPrefix(test_name):
"""Modify the test name without disabled prefix if prefix 'DISABLED_' or
'FLAKY_' presents.
Expand Down Expand Up @@ -338,14 +310,7 @@ def __init__(self, args, data_deps_delegate, error_func):
error_func('Could not find apk or executable for %s' % self._suite)

self._data_deps = []
if args.test_filter:
self._gtest_filter = args.test_filter
elif args.test_filter_file:
with open(args.test_filter_file, 'r') as f:
self._gtest_filter = ConvertTestFilterFileIntoGTestFilterArgument(f)
else:
self._gtest_filter = None

self._gtest_filter = test_filter.InitializeFilterFromArgs(args)
self._run_disabled = args.run_disabled

self._data_deps_delegate = data_deps_delegate
Expand Down
45 changes: 0 additions & 45 deletions build/android/pylib/gtest/gtest_test_instance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,51 +181,6 @@ def testParseGTestXML_none(self):
actual = gtest_test_instance.ParseGTestXML(None)
self.assertEquals([], actual)

def testConvertTestFilterFile_commentsAndBlankLines(self):
input_lines = [
'positive1',
'# comment',
'positive2 # Another comment',
''
'positive3'
]
actual = gtest_test_instance \
.ConvertTestFilterFileIntoGTestFilterArgument(input_lines)
expected = 'positive1:positive2:positive3'
self.assertEquals(expected, actual)

def testConvertTestFilterFile_onlyPositive(self):
input_lines = [
'positive1',
'positive2'
]
actual = gtest_test_instance \
.ConvertTestFilterFileIntoGTestFilterArgument(input_lines)
expected = 'positive1:positive2'
self.assertEquals(expected, actual)

def testConvertTestFilterFile_onlyNegative(self):
input_lines = [
'-negative1',
'-negative2'
]
actual = gtest_test_instance \
.ConvertTestFilterFileIntoGTestFilterArgument(input_lines)
expected = '-negative1:negative2'
self.assertEquals(expected, actual)

def testConvertTestFilterFile_positiveAndNegative(self):
input_lines = [
'positive1',
'positive2',
'-negative1',
'-negative2'
]
actual = gtest_test_instance \
.ConvertTestFilterFileIntoGTestFilterArgument(input_lines)
expected = 'positive1:positive2-negative1:negative2'
self.assertEquals(expected, actual)

def testTestNameWithoutDisabledPrefix_disabled(self):
test_name_list = [
'A.DISABLED_B',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from pylib.utils import instrumentation_tracing
from pylib.utils import proguard
from pylib.utils import shared_preference_utils
from pylib.utils import test_filter


with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
Expand Down Expand Up @@ -62,8 +63,6 @@
_SKIP_PARAMETERIZATION = 'SkipCommandLineParameterization'
_COMMANDLINE_PARAMETERIZATION = 'CommandLineParameter'
_NATIVE_CRASH_RE = re.compile('(process|native) crash', re.IGNORECASE)
_CMDLINE_NAME_SEGMENT_RE = re.compile(
r' with(?:out)? \{[^\}]*\}')
_PICKLE_FORMAT_VERSION = 12


Expand Down Expand Up @@ -181,23 +180,23 @@ def GenerateTestResults(
return results


def FilterTests(tests, test_filter=None, annotations=None,
def FilterTests(tests, filter_str=None, annotations=None,
excluded_annotations=None):
"""Filter a list of tests
Args:
tests: a list of tests. e.g. [
{'annotations": {}, 'class': 'com.example.TestA', 'method':'test1'},
{'annotations": {}, 'class': 'com.example.TestB', 'method':'test2'}]
test_filter: googletest-style filter string.
filter_str: googletest-style filter string.
annotations: a dict of wanted annotations for test methods.
exclude_annotations: a dict of annotations to exclude.
Return:
A list of filtered tests
"""
def gtest_filter(t):
if not test_filter:
if not filter_str:
return True
# Allow fully-qualified name as well as an omitted package.
unqualified_class_test = {
Expand All @@ -216,7 +215,7 @@ def gtest_filter(t):
GetTestNameWithoutParameterPostfix(unqualified_class_test, sep='.')
]

pattern_groups = test_filter.split('-')
pattern_groups = filter_str.split('-')
if len(pattern_groups) > 1:
negative_filter = pattern_groups[1]
if unittest_util.FilterTestNames(names, negative_filter):
Expand Down Expand Up @@ -385,9 +384,9 @@ def __init__(self):
class UnmatchedFilterException(test_exception.TestException):
"""Raised when a user specifies a filter that doesn't match any tests."""

def __init__(self, test_filter):
def __init__(self, filter_str):
super(UnmatchedFilterException, self).__init__(
'Test filter "%s" matched no tests.' % test_filter)
'Test filter "%s" matched no tests.' % filter_str)


def GetTestName(test, sep='#'):
Expand Down Expand Up @@ -622,9 +621,7 @@ def _initializeDataDependencyAttributes(self, args, data_deps_delegate):
logging.warning('No data dependencies will be pushed.')

def _initializeTestFilterAttributes(self, args):
if args.test_filter:
self._test_filter = _CMDLINE_NAME_SEGMENT_RE.sub(
'', args.test_filter.replace('#', '.'))
self._test_filter = test_filter.InitializeFilterFromArgs(args)

def annotation_element(a):
a = a.split('=', 1)
Expand Down
3 changes: 2 additions & 1 deletion build/android/pylib/junit/junit_test_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# found in the LICENSE file.

from pylib.base import test_instance
from pylib.utils import test_filter


class JunitTestInstance(test_instance.TestInstance):
Expand All @@ -18,7 +19,7 @@ def __init__(self, args, _):
self._resource_zips = args.resource_zips
self._robolectric_runtime_deps_dir = args.robolectric_runtime_deps_dir
self._runner_filter = args.runner_filter
self._test_filter = args.test_filter
self._test_filter = test_filter.InitializeFilterFromArgs(args)
self._test_suite = args.test_suite

#override
Expand Down
3 changes: 2 additions & 1 deletion build/android/pylib/linker/linker_test_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from pylib.base import test_instance
from pylib.constants import host_paths
from pylib.linker import test_case
from pylib.utils import test_filter

with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
import unittest_util
Expand All @@ -15,7 +16,7 @@ class LinkerTestInstance(test_instance.TestInstance):
def __init__(self, args):
super(LinkerTestInstance, self).__init__()
self._test_apk = args.test_apk
self._test_filter = args.test_filter
self._test_filter = test_filter.InitializeFilterFromArgs(args)

@property
def test_apk(self):
Expand Down
3 changes: 2 additions & 1 deletion build/android/pylib/perf/perf_test_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from pylib.base import base_test_result
from pylib.base import test_instance
from pylib.constants import host_paths
from pylib.utils import test_filter


_GIT_CR_POS_RE = re.compile(r'^Cr-Commit-Position: refs/heads/master@{#(\d+)}$')
Expand Down Expand Up @@ -77,7 +78,7 @@ def __init__(self, args, _):
self._single_step = (
' '.join(args.single_step_command) if args.single_step else None)
self._steps = args.steps
self._test_filter = args.test_filter
self._test_filter = test_filter.InitializeFilterFromArgs(args)
self._write_buildbot_json = args.write_buildbot_json

#override
Expand Down
80 changes: 80 additions & 0 deletions build/android/pylib/utils/test_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright 2018 The Chromium 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 os
import re


_CMDLINE_NAME_SEGMENT_RE = re.compile(
r' with(?:out)? \{[^\}]*\}')


def ParseFilterFile(input_lines):
"""Converts test filter file contents into --gtest_filter argument.
See //testing/buildbot/filters/README.md for description of the
syntax that |input_lines| are expected to follow.
See
https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#running-a-subset-of-the-tests
for description of the syntax that --gtest_filter argument should follow.
Args:
input_lines: An iterable (e.g. a list or a file) containing input lines.
Returns:
a string suitable for feeding as an argument of --gtest_filter parameter.
"""
# Strip comments and whitespace from each line and filter non-empty lines.
stripped_lines = (l.split('#', 1)[0].strip() for l in input_lines)
filter_lines = [l for l in stripped_lines if l]

# Split the tests into positive and negative patterns (gtest treats
# every pattern after the first '-' sign as an exclusion).
positive_patterns = ':'.join(l for l in filter_lines if l[0] != '-')
negative_patterns = ':'.join(l[1:] for l in filter_lines if l[0] == '-')
if negative_patterns:
negative_patterns = '-' + negative_patterns

# Join the filter lines into one, big --gtest_filter argument.
return positive_patterns + negative_patterns


def AddFilterOptions(parser):
"""Adds filter command-line options to the provided parser.
Args:
parser: an argparse.ArgumentParser instance.
"""
filter_group = parser.add_mutually_exclusive_group()
filter_group.add_argument(
'-f', '--test-filter', '--gtest_filter', '--gtest-filter',
dest='test_filter',
help='googletest-style filter string.',
default=os.environ.get('GTEST_FILTER'))
filter_group.add_argument(
# Deprecated argument.
'--gtest-filter-file',
# New argument.
'--test-launcher-filter-file',
dest='test_filter_file', type=os.path.realpath,
help='Path to file that contains googletest-style filter strings. '
'See also //testing/buildbot/filters/README.md.')


def InitializeFilterFromArgs(args):
"""Returns a filter string from the command-line option values.
Args:
args: an argparse.Namespace instance resulting from a using parser
to which the filter options above were added.
"""
parsed_filter = None
if args.test_filter:
parsed_filter = _CMDLINE_NAME_SEGMENT_RE.sub(
'', args.test_filter.replace('#', '.'))
elif args.test_filter_file:
with open(args.test_filter_file, 'r') as f:
parsed_filter = ParseFilterFile(f)

return parsed_filter
56 changes: 56 additions & 0 deletions build/android/pylib/utils/test_filter_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env vpython
# Copyright 2018 The Chromium 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 sys
import unittest

from pylib.utils import test_filter

class ParseFilterFileTest(unittest.TestCase):

def testParseFilterFile_commentsAndBlankLines(self):
input_lines = [
'positive1',
'# comment',
'positive2 # Another comment',
''
'positive3'
]
actual = test_filter.ParseFilterFile(input_lines)
expected = 'positive1:positive2:positive3'
self.assertEquals(expected, actual)

def testParseFilterFile_onlyPositive(self):
input_lines = [
'positive1',
'positive2'
]
actual = test_filter.ParseFilterFile(input_lines)
expected = 'positive1:positive2'
self.assertEquals(expected, actual)

def testParseFilterFile_onlyNegative(self):
input_lines = [
'-negative1',
'-negative2'
]
actual = test_filter.ParseFilterFile(input_lines)
expected = '-negative1:negative2'
self.assertEquals(expected, actual)

def testParseFilterFile_positiveAndNegative(self):
input_lines = [
'positive1',
'positive2',
'-negative1',
'-negative2'
]
actual = test_filter.ParseFilterFile(input_lines)
expected = 'positive1:positive2-negative1:negative2'
self.assertEquals(expected, actual)


if __name__ == '__main__':
sys.exit(unittest.main())
Loading

0 comments on commit 0a582d4

Please sign in to comment.