Skip to content

Commit

Permalink
Implement --list_tests in run_suite_class (#951)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdstanko authored Jan 17, 2025
1 parent 130f6d9 commit 2ccc96a
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 1 deletion.
29 changes: 28 additions & 1 deletion mobly/suite_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,33 @@ class is found, search in the module that is calling
return test_suites[0]


def _print_test_names_for_suite(suite_class):
"""Prints the names of all the tests in a suite classes.
Args:
suite_class: a test suite_class to be run.
"""
config = config_parser.TestRunConfig()
runner = test_runner.TestRunner(
log_dir=config.log_path, testbed_name=config.testbed_name
)
cls = suite_class(runner, config)
try:
cls.setup_suite(config)
finally:
cls.teardown_suite()

last = ''
for name in runner.get_full_test_names():
tag = name.split('.')[0]
# Print tags when we encounter a new one. Prefer this to grouping by
# tag first since we should print any duplicate entries.
if tag != last:
last = tag
print('==========> %s <==========' % tag)
print(name)


def _print_test_names(test_classes):
"""Prints the names of all the tests in all test classes.
Args:
Expand Down Expand Up @@ -239,7 +266,7 @@ def run_suite_class(argv=None):
cli_args = _parse_cli_args(argv)
suite_class = _find_suite_class()
if cli_args.list_tests:
_print_test_names([suite_class])
_print_test_names_for_suite(suite_class)
sys.exit(0)
test_configs = config_parser.load_test_config_file(
cli_args.config, cli_args.test_bed
Expand Down
47 changes: 47 additions & 0 deletions mobly/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,53 @@ def time_elapsed_sec(self):
return None
return self._end_counter - self._start_counter

def get_full_test_names(self):
"""Returns the names of all tests that will be run in this test runner.
Returns:
A list of test names. Each test name is in the format of
<test.TAG>.<test_name>.
"""
test_names = []
for test_run_info in self._test_run_infos:
test_config = test_run_info.config.copy()
test_config.test_class_name_suffix = test_run_info.test_class_name_suffix
test = test_run_info.test_class(test_config)

tests = self._get_test_names_from_class(test)
if test_run_info.tests is not None:
# If tests is provided, verify that all tests exist in the class.
tests_set = set(tests)
for test_name in test_run_info.tests:
if test_name not in tests_set:
raise Error(
'Unknown test method: %s in class %s', (test_name, test.TAG)
)
test_names.append(f'{test.TAG}.{test_name}')
else:
test_names.extend([f'{test.TAG}.{n}' for n in tests])

return test_names

def _get_test_names_from_class(self, test):
"""Returns the names of all the tests in a test class.
Args:
test: module, the test module to print names from.
"""
try:
# Executes pre-setup procedures, this is required since it might
# generate test methods that we want to return as well.
test._pre_run()
if test.tests:
# Specified by run list in class.
return list(test.tests)
else:
# No test method specified by user, list all in test class.
return test.get_existing_test_names()
finally:
test._clean_up()

def __init__(self, log_dir, testbed_name):
"""Constructor for TestRunner.
Expand Down
21 changes: 21 additions & 0 deletions tests/mobly/suite_runner_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,27 @@ def test_run_suite_class_finds_suite_class_when_not_in_main_module(

mock_setup_suite.assert_called_once()

@mock.patch('builtins.print')
def test_print_test_names_for_suites(self, mock_print):
class FakeTestSuite(base_suite.BaseSuite):

def setup_suite(self, config):
self.add_test_class(FakeTest1, name_suffix='A')
self.add_test_class(FakeTest1, name_suffix='B')
self.add_test_class(FakeTest1, name_suffix='C', tests=['test_a'])
self.add_test_class(FakeTest1, name_suffix='D', tests=[])

suite_runner._print_test_names_for_suite(FakeTestSuite)
calls = [
mock.call('==========> FakeTest1_A <=========='),
mock.call('FakeTest1_A.test_a'),
mock.call('==========> FakeTest1_B <=========='),
mock.call('FakeTest1_B.test_a'),
mock.call('==========> FakeTest1_C <=========='),
mock.call('FakeTest1_C.test_a'),
]
mock_print.assert_has_calls(calls)

def test_print_test_names(self):
mock_test_class = mock.MagicMock()
mock_cls_instance = mock.MagicMock()
Expand Down
48 changes: 48 additions & 0 deletions tests/mobly/test_runner_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,54 @@ def test__find_test_class_when_multiple_test_classes(self):
with mock.patch.dict('sys.modules', __main__=multiple_subclasses_module):
test_class = test_runner._find_test_class()

def test_get_full_test_names(self):
"""Verifies that calling get_test_names works properly."""
config = self.base_mock_test_config.copy()
tr = test_runner.TestRunner(self.log_dir, self.testbed_name)
with tr.mobly_logger():
tr.add_test_class(
config, integration_test.IntegrationTest, name_suffix='A'
)
tr.add_test_class(
config, integration_test.IntegrationTest, name_suffix='B'
)
tr.add_test_class(
config, integration2_test.Integration2Test, name_suffix='A'
)
tr.add_test_class(
config, integration2_test.Integration2Test, name_suffix='B'
)

results = tr.get_full_test_names()
self.assertIn('IntegrationTest_A.test_hello_world', results)
self.assertIn('IntegrationTest_B.test_hello_world', results)
self.assertIn('Integration2Test_A.test_hello_world', results)
self.assertIn('Integration2Test_B.test_hello_world', results)
self.assertEqual(len(results), 4)

def test_get_full_test_names_test_list(self):
"""Verifies that calling get_test_names with test list works properly."""
config = self.base_mock_test_config.copy()
tr = test_runner.TestRunner(self.log_dir, self.testbed_name)
with tr.mobly_logger():
tr.add_test_class(
config, integration_test.IntegrationTest, tests=['test_hello_world']
)

results = tr.get_full_test_names()
self.assertIn('IntegrationTest.test_hello_world', results)
self.assertEqual(len(results), 1)

def test_get_full_test_names_test_list_empty(self):
"""Verifies that calling get_test_names with empty test list works properly."""
config = self.base_mock_test_config.copy()
tr = test_runner.TestRunner(self.log_dir, self.testbed_name)
with tr.mobly_logger():
tr.add_test_class(config, integration_test.IntegrationTest, tests=[])

results = tr.get_full_test_names()
self.assertEqual(len(results), 0)

def test_print_test_names(self):
mock_test_class = mock.MagicMock()
mock_cls_instance = mock.MagicMock()
Expand Down

0 comments on commit 2ccc96a

Please sign in to comment.