Skip to content

Commit

Permalink
Log path changes in BaseTestClass. (#650)
Browse files Browse the repository at this point in the history
* Add a new level of output directoy specific to test classes.
* Direct `log_path` to class-specific.
* Add a `root_output_path` pointing to the test run path
  (what `log_path` used to point to)
  • Loading branch information
xpconanfan committed Oct 24, 2019
1 parent 01b3226 commit 828cf20
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 23 deletions.
25 changes: 16 additions & 9 deletions mobly/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import functools
import inspect
import logging
import os

from future.utils import raise_with_traceback

Expand Down Expand Up @@ -71,8 +72,11 @@ class BaseTestClass(object):
test bed config.
current_test_info: RuntimeTestInfo, runtime information on the test
currently being executed.
log_path: string, specifies the root directory for all logs written
by a test run.
root_output_path: string, storage path for output files associated with
the entire test run. A test run can have multiple test class
executions. This includes the test summary and Mobly log files.
log_path: string, storage path for files specific to a single test
class execution.
test_bed_name: [Deprecated, use 'testbed_name' instead]
string, the name of the test bed used by a test run.
testbed_name: string, the name of the test bed used by a test run.
Expand All @@ -94,14 +98,16 @@ def __init__(self, configs):
configs: A config_parser.TestRunConfig object.
"""
self.tests = []
self._class_name = self.__class__.__name__
if configs.test_class_name_suffix and self.TAG is None:
self.TAG = '%s_%s' % (self._class_name,
configs.test_class_name_suffix)
elif self.TAG is None:
self.TAG = self._class_name
class_identifier = self.__class__.__name__
if configs.test_class_name_suffix:
class_identifier = '%s_%s' % (class_identifier,
configs.test_class_name_suffix)
if self.TAG is None:
self.TAG = class_identifier
# Set params.
self.log_path = configs.log_path
self.root_output_path = configs.log_path
self.log_path = os.path.join(self.root_output_path, class_identifier)
utils.create_dir(self.log_path)
# Deprecated, use 'testbed_name'
self.test_bed_name = configs.test_bed_name
self.testbed_name = configs.testbed_name
Expand Down Expand Up @@ -809,6 +815,7 @@ def run(self, test_names=None):
Returns:
The test results object of this class.
"""
logging.log_path = self.log_path
# Executes pre-setup procedures, like generating test methods.
if not self._setup_generated_tests():
return self.results
Expand Down
9 changes: 9 additions & 0 deletions mobly/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ def _setup_test_logger(log_path, prefix=None):
log.addHandler(fh_debug)
log.log_path = log_path
logging.log_path = log_path
logging.root_output_path = log_path


def kill_test_logger(logger):
Expand Down Expand Up @@ -241,6 +242,14 @@ def create_latest_log_alias(actual_path, alias):
def setup_test_logger(log_path, prefix=None, alias='latest'):
"""Customizes the root logger for a test run.
In addition to configuring the Mobly logging handlers, this also sets two
attributes on the `logging` module for the output directories:
root_output_path: path to the directory for the entire test run.
log_path: same as `root_output_path` outside of a test class run. In the
context of a test class run, this is the output directory for files
specific to a test class.
Args:
log_path: string, the location of the report file.
prefix: optional string, a prefix for each log line in terminal.
Expand Down
17 changes: 10 additions & 7 deletions mobly/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,12 @@ class TestRunner(object):
self.results: The test result object used to record the results of
this test run.
"""

class _TestRunInfo(object):
"""Identifies one test class to run, which tests to run, and config to
run it with.
"""

def __init__(self,
config,
test_class,
Expand Down Expand Up @@ -223,8 +225,9 @@ def __init__(self, log_dir, testbed_name):
def _update_log_path(self):
"""Updates the logging values with the current timestamp."""
self._start_time = logger.get_log_file_timestamp()
self._log_path = os.path.join(self._log_dir, self._testbed_name,
self._start_time)
self._root_output_path = os.path.join(self._log_dir,
self._testbed_name,
self._start_time)

@contextlib.contextmanager
def mobly_logger(self, alias='latest'):
Expand All @@ -239,11 +242,11 @@ def mobly_logger(self, alias='latest'):
The host file path where the logs for the test run are stored.
"""
self._update_log_path()
logger.setup_test_logger(self._log_path,
logger.setup_test_logger(self._root_output_path,
self._testbed_name,
alias=alias)
try:
yield self._log_path
yield self._root_output_path
finally:
logger.kill_test_logger(logging.getLogger())

Expand Down Expand Up @@ -318,15 +321,15 @@ def run(self):

# Ensure the log path exists. Necessary if `run` is used outside of the
# `mobly_logger` context.
utils.create_dir(self._log_path)
utils.create_dir(self._root_output_path)

summary_writer = records.TestSummaryWriter(
os.path.join(self._log_path, records.OUTPUT_FILE_SUMMARY))
os.path.join(self._root_output_path, records.OUTPUT_FILE_SUMMARY))
try:
for test_run_info in self._test_run_infos:
# Set up the test-specific config
test_config = test_run_info.config.copy()
test_config.log_path = self._log_path
test_config.log_path = self._root_output_path
test_config.summary_writer = summary_writer
test_config.test_class_name_suffix = test_run_info.test_class_name_suffix
try:
Expand Down
19 changes: 18 additions & 1 deletion tests/mobly/base_test_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ def setUp(self):
def tearDown(self):
shutil.rmtree(self.tmp_dir)

def test_paths(self):
'''Checks the output paths set in `BaseTestClass`.'''
path_checker = mock.MagicMock()

class MockBaseTest(base_test.BaseTestClass):
def test_func(self):
path_checker.log_path = self.log_path
path_checker.root_output_path = self.root_output_path

bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run(test_names=["test_func"])
self.assertEqual(path_checker.root_output_path, self.tmp_dir)
self.assertTrue(os.path.exists(path_checker.root_output_path))
expected_log_path = os.path.join(self.tmp_dir, 'MockBaseTest')
self.assertEqual(path_checker.log_path, expected_log_path)
self.assertTrue(os.path.exists(path_checker.log_path))

def test_current_test_name(self):
class MockBaseTest(base_test.BaseTestClass):
def test_func(self):
Expand Down Expand Up @@ -2176,7 +2193,7 @@ def not_a_test(self):
pass

def test_log_stage_always_logs_end_statement(self):
instance = base_test.BaseTestClass(mock.Mock())
instance = base_test.BaseTestClass(self.mock_test_cls_configs)
instance.current_test_info = mock.Mock()
instance.current_test_info.name = 'TestClass'

Expand Down
15 changes: 10 additions & 5 deletions tests/mobly/output_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class OutputTest(unittest.TestCase):
"""This test class has unit tests for the implementation of Mobly's output
files.
"""

def setUp(self):
self.tmp_dir = tempfile.mkdtemp()
self.base_mock_test_config = config_parser.TestRunConfig()
Expand Down Expand Up @@ -195,7 +196,7 @@ def test_logging_before_run(self):
tr.add_test_class(mock_test_config,
integration_test.IntegrationTest)
tr.run()
output_dir = logging.log_path
output_dir = logging.root_output_path
(summary_file_path, debug_log_path,
info_log_path) = self.assert_output_logs_exist(output_dir)
self.assert_log_contents(debug_log_path,
Expand All @@ -218,10 +219,10 @@ def test_run_twice_for_two_sets_of_logs(self, mock_timestamp):
tr.add_test_class(mock_test_config, integration_test.IntegrationTest)
with tr.mobly_logger():
tr.run()
output_dir1 = logging.log_path
output_dir1 = logging.root_output_path
with tr.mobly_logger():
tr.run()
output_dir2 = logging.log_path
output_dir2 = logging.root_output_path
self.assertNotEqual(output_dir1, output_dir2)
self.assert_output_logs_exist(output_dir1)
self.assert_output_logs_exist(output_dir2)
Expand Down Expand Up @@ -279,7 +280,11 @@ def test_basic_output(self):
tr.add_test_class(mock_test_config,
integration_test.IntegrationTest)
tr.run()
output_dir = logging.log_path
expected_class_path = os.path.join(logging.root_output_path,
'IntegrationTest')
self.assertEqual(expected_class_path, logging.log_path)
os.path.exists(logging.log_path)
output_dir = logging.root_output_path
(summary_file_path, debug_log_path,
info_log_path) = self.assert_output_logs_exist(output_dir)
summary_entries = []
Expand All @@ -303,7 +308,7 @@ def test_teardown_class_output(self):
mock_test_config,
teardown_class_failure_test.TearDownClassFailureTest)
tr.run()
output_dir = logging.log_path
output_dir = logging.root_output_path
summary_file_path = os.path.join(output_dir,
records.OUTPUT_FILE_SUMMARY)
found = False
Expand Down
3 changes: 2 additions & 1 deletion tests/mobly/test_runner_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class TestRunnerTest(unittest.TestCase):
"""This test class has unit tests for the implementation of everything
under mobly.test_runner.
"""

def setUp(self):
self.tmp_dir = tempfile.mkdtemp()
self.base_mock_test_config = config_parser.TestRunConfig()
Expand Down Expand Up @@ -134,7 +135,7 @@ def test_summary_file_entries(self):
tr.add_test_class(mock_test_config,
integration_test.IntegrationTest)
tr.run()
summary_path = os.path.join(logging.log_path,
summary_path = os.path.join(logging.root_output_path,
records.OUTPUT_FILE_SUMMARY)
with io.open(summary_path, 'r', encoding='utf-8') as f:
summary_entries = list(yaml.safe_load_all(f))
Expand Down

0 comments on commit 828cf20

Please sign in to comment.