aboutsummaryrefslogtreecommitdiff
path: root/absl
diff options
context:
space:
mode:
authorYilei Yang <yileiyang@google.com>2021-10-07 12:49:41 -0700
committerCopybara-Service <copybara-worker@google.com>2021-10-07 12:50:14 -0700
commit1910e64fe1b6e2a276afe0170f382c3b56802402 (patch)
tree25ba513ed68a290310ab935a5e87f714f0a3a15f /absl
parent5bc02408a1ef380b606f43fc463018f243611c72 (diff)
downloadabsl-py-1910e64fe1b6e2a276afe0170f382c3b56802402.tar.gz
#128: support matching substring and glob patterns using bazel's `--test_filter=` flag (Python 3.7+ only).
Previously, the `--test_filter=` arguments are passed to Python's unittest as positional arguments. This only matches by class or method's full names. After this, `--test_filter=` has the same meaning as unittest's `-k` flag: https://docs.python.org/3/library/unittest.html#cmdoption-unittest-k One notable behavior difference: previously if the filter doesn't match any tests, the test would fail; now it doesn't fail. This also makes filtering `absltest.parameterized` tests significantly more user friendly. This change is only for Python 3.7+. PiperOrigin-RevId: 401582298 Change-Id: I074e9283706fe9c88a8dbb392084fb6548cebc6f
Diffstat (limited to 'absl')
-rw-r--r--absl/CHANGELOG.md5
-rw-r--r--absl/testing/BUILD3
-rw-r--r--absl/testing/absltest.py14
-rw-r--r--absl/testing/tests/absltest_filtering_test.py70
-rw-r--r--absl/testing/tests/absltest_filtering_test_helper.py16
5 files changed, 101 insertions, 7 deletions
diff --git a/absl/CHANGELOG.md b/absl/CHANGELOG.md
index 5e2506f..55b5f0f 100644
--- a/absl/CHANGELOG.md
+++ b/absl/CHANGELOG.md
@@ -6,7 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com).
## Unreleased
-Nothing notable unreleased.
+### Changed
+
+* (testing) #128: When running bazel with its `--test_filter=` flag, it now
+ treats the filters as `unittest`'s `-k` flag in Python 3.7+.
## 0.14.1 (2021-09-30)
diff --git a/absl/testing/BUILD b/absl/testing/BUILD
index 609d626..14c9a5f 100644
--- a/absl/testing/BUILD
+++ b/absl/testing/BUILD
@@ -106,7 +106,7 @@ py_library(
py_test(
name = "tests/absltest_filtering_test",
- size = "small",
+ size = "medium",
srcs = ["tests/absltest_filtering_test.py"],
data = [":tests/absltest_filtering_test_helper"],
python_version = "PY3",
@@ -128,6 +128,7 @@ py_binary(
srcs_version = "PY3",
deps = [
":absltest",
+ ":parameterized",
"//absl:app",
],
)
diff --git a/absl/testing/absltest.py b/absl/testing/absltest.py
index a6a8a8c..a79cde7 100644
--- a/absl/testing/absltest.py
+++ b/absl/testing/absltest.py
@@ -2336,8 +2336,12 @@ def _setup_filtering(argv):
The following environment variable is used in this method:
TESTBRIDGE_TEST_ONLY: string, if set, is forwarded to the unittest
- framework to use as a test filter. Its value is split with shlex
- before being passed as positional arguments on argv.
+ framework to use as a test filter. Its value is split with shlex, then:
+ 1. On Python 3.6 and before, split values are passed as positional
+ arguments on argv.
+ 2. On Python 3.7+, split values are passed to unittest's `-k` flag. Tests
+ are matched by glob patterns or substring. See
+ https://docs.python.org/3/library/unittest.html#cmdoption-unittest-k
Args:
argv: the argv to mutate in-place.
@@ -2346,7 +2350,11 @@ def _setup_filtering(argv):
if argv is None or not test_filter:
return
- argv[1:1] = shlex.split(test_filter)
+ filters = shlex.split(test_filter)
+ if sys.version_info[:2] >= (3, 7):
+ filters = ['-k=' + test_filter for test_filter in filters]
+
+ argv[1:1] = filters
def _setup_test_runner_fail_fast(argv):
diff --git a/absl/testing/tests/absltest_filtering_test.py b/absl/testing/tests/absltest_filtering_test.py
index 63e3915..30a81f6 100644
--- a/absl/testing/tests/absltest_filtering_test.py
+++ b/absl/testing/tests/absltest_filtering_test.py
@@ -18,6 +18,7 @@ from __future__ import division
from __future__ import print_function
import subprocess
+import sys
from absl import logging
from absl.testing import _bazelize_command
@@ -63,7 +64,12 @@ class TestFilteringTest(absltest.TestCase):
if use_env_variable:
env['TESTBRIDGE_TEST_ONLY'] = test_filter
elif test_filter:
- additional_args.extend(test_filter.split(' '))
+ if sys.version_info[:2] >= (3, 7):
+ # The -k flags are passed as positional arguments to absl.flags.
+ additional_args.append('--')
+ additional_args.extend(['-k=' + f for f in test_filter.split(' ')])
+ else:
+ additional_args.extend(test_filter.split(' '))
proc = subprocess.Popen(
args=([_bazelize_command.get_executable_path(self._test_name)] +
@@ -115,12 +121,72 @@ class TestFilteringTest(absltest.TestCase):
self.assertIn('class B test C', out)
self.assertNotIn('class B test A', out)
- def test_not_found_filters(self, use_env_variable, use_app_run):
+ @absltest.skipIf(
+ sys.version_info[:2] < (3, 7),
+ 'Only Python 3.7+ does glob and substring matching.')
+ def test_substring(self, use_env_variable, use_app_run):
+ out, exit_code = self._run_filtered(
+ 'testA', use_env_variable, use_app_run)
+ self.assertEqual(0, exit_code)
+ self.assertIn('Ran 2 tests', out)
+ self.assertIn('ClassA.testA', out)
+ self.assertIn('ClassB.testA', out)
+
+ @absltest.skipIf(
+ sys.version_info[:2] < (3, 7),
+ 'Only Python 3.7+ does glob and substring matching.')
+ def test_glob_pattern(self, use_env_variable, use_app_run):
+ out, exit_code = self._run_filtered(
+ '__main__.Class*.testA', use_env_variable, use_app_run)
+ self.assertEqual(0, exit_code)
+ self.assertIn('Ran 2 tests', out)
+ self.assertIn('ClassA.testA', out)
+ self.assertIn('ClassB.testA', out)
+
+ @absltest.skipIf(
+ sys.version_info[:2] >= (3, 7),
+ "Python 3.7+ uses unittest's -k flag and doesn't fail if no tests match.")
+ def test_not_found_filters_py36(self, use_env_variable, use_app_run):
out, exit_code = self._run_filtered('NotExistedClass.not_existed_method',
use_env_variable, use_app_run)
self.assertEqual(1, exit_code)
self.assertIn("has no attribute 'NotExistedClass'", out)
+ @absltest.skipIf(
+ sys.version_info[:2] < (3, 7),
+ 'Python 3.6 passes the filter as positional arguments and fails if no '
+ 'tests match.'
+ )
+ def test_not_found_filters_py37(self, use_env_variable, use_app_run):
+ out, exit_code = self._run_filtered('NotExistedClass.not_existed_method',
+ use_env_variable, use_app_run)
+ self.assertEqual(0, exit_code)
+ self.assertIn('Ran 0 tests', out)
+
+ @absltest.skipIf(
+ sys.version_info[:2] < (3, 7),
+ 'Python 3.6 passes the filter as positional arguments and matches by name'
+ )
+ def test_parameterized_unnamed(self, use_env_variable, use_app_run):
+ out, exit_code = self._run_filtered('ParameterizedTest.test_unnamed',
+ use_env_variable, use_app_run)
+ self.assertEqual(0, exit_code)
+ self.assertIn('Ran 2 tests', out)
+ self.assertIn('parameterized unnamed 1', out)
+ self.assertIn('parameterized unnamed 2', out)
+
+ @absltest.skipIf(
+ sys.version_info[:2] < (3, 7),
+ 'Python 3.6 passes the filter as positional arguments and matches by name'
+ )
+ def test_parameterized_named(self, use_env_variable, use_app_run):
+ out, exit_code = self._run_filtered('ParameterizedTest.test_named',
+ use_env_variable, use_app_run)
+ self.assertEqual(0, exit_code)
+ self.assertIn('Ran 2 tests', out)
+ self.assertIn('parameterized named 1', out)
+ self.assertIn('parameterized named 2', out)
+
if __name__ == '__main__':
absltest.main()
diff --git a/absl/testing/tests/absltest_filtering_test_helper.py b/absl/testing/tests/absltest_filtering_test_helper.py
index 2775a62..2b741ed 100644
--- a/absl/testing/tests/absltest_filtering_test_helper.py
+++ b/absl/testing/tests/absltest_filtering_test_helper.py
@@ -23,6 +23,7 @@ import sys
from absl import app
from absl.testing import absltest
+from absl.testing import parameterized
class ClassA(absltest.TestCase):
@@ -58,6 +59,21 @@ class ClassB(absltest.TestCase):
self.fail('Force failure')
+class ParameterizedTest(parameterized.TestCase):
+ """Helper parameterized test case for absltest_filtering_test."""
+
+ @parameterized.parameters([1, 2])
+ def test_unnamed(self, value):
+ sys.stderr.write('\nparameterized unnamed %s' % value)
+
+ @parameterized.named_parameters(
+ ('test1', 1),
+ ('test2', 2),
+ )
+ def test_named(self, value):
+ sys.stderr.write('\nparameterized named %s' % value)
+
+
def main(argv):
absltest.main(argv=argv)