diff options
author | boon <ohbooneng@google.com> | 2023-12-29 09:15:34 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-28 17:15:34 -0800 |
commit | 46ff13e8bfaac24b6ba3ea43b2da9b5a1b0dec68 (patch) | |
tree | af8400a5c85037110faa2899aeec09fb3e0dae08 | |
parent | 6c5d666c44d350a2f906351c645d4f8313463a43 (diff) | |
download | mobly-46ff13e8bfaac24b6ba3ea43b2da9b5a1b0dec68.tar.gz |
Add parent field to records to represent repeat and retry information. (#905)
This provides a generic way to mark the sequential relationship of test result records.
The intention is to make it easier for downstream parsers to process test result records for repeat and retry cases.
The `retry_parent` field is now deprecated in test result record.
-rw-r--r-- | mobly/base_test.py | 12 | ||||
-rw-r--r-- | mobly/records.py | 28 | ||||
-rwxr-xr-x | tests/mobly/base_test_test.py | 26 | ||||
-rwxr-xr-x | tests/mobly/records_test.py | 1 |
4 files changed, 60 insertions, 7 deletions
diff --git a/mobly/base_test.py b/mobly/base_test.py index ae8d023..e5060af 100644 --- a/mobly/base_test.py +++ b/mobly/base_test.py @@ -720,6 +720,7 @@ class BaseTestClass: retry_name = f'{test_name}_retry_{i+1}' new_record = records.TestResultRecord(retry_name, self.TAG) new_record.retry_parent = previous_record + new_record.parent = (previous_record, records.TestParentType.RETRY) previous_record = self.exec_one_test(retry_name, test_method, new_record) if not should_retry(previous_record): break @@ -749,16 +750,23 @@ class BaseTestClass: if max_consecutive_error == 0: max_consecutive_error = repeat_count + previous_record = None for i in range(repeat_count): new_test_name = f'{test_name}_{i}' - record = self.exec_one_test(new_test_name, test_method) - if record.result in [ + new_record = records.TestResultRecord(new_test_name, self.TAG) + if i > 0: + new_record.parent = (previous_record, records.TestParentType.REPEAT) + previous_record = self.exec_one_test( + new_test_name, test_method, new_record + ) + if previous_record.result in [ records.TestResultEnums.TEST_RESULT_FAIL, records.TestResultEnums.TEST_RESULT_ERROR, ]: consecutive_error_count += 1 else: consecutive_error_count = 0 + if consecutive_error_count == max_consecutive_error: logging.error( 'Repeated test case "%s" has consecutively failed %d iterations, ' diff --git a/mobly/records.py b/mobly/records.py index c5f41ef..216f3f7 100644 --- a/mobly/records.py +++ b/mobly/records.py @@ -37,6 +37,13 @@ class Error(Exception): """Raised for errors in record module members.""" +class TestParentType(enum.Enum): + """The type of parent in a chain of executions of the same test.""" + + REPEAT = 'repeat' + RETRY = 'retry' + + def uid(uid): """Decorator specifying the unique identifier (UID) of a test case. @@ -182,6 +189,7 @@ class TestResultEnums: RECORD_STACKTRACE = 'Stacktrace' RECORD_SIGNATURE = 'Signature' RECORD_RETRY_PARENT = 'Retry Parent' + RECORD_PARENT = 'Parent' RECORD_POSITION = 'Position' TEST_RESULT_PASS = 'PASS' TEST_RESULT_FAIL = 'FAIL' @@ -317,9 +325,10 @@ class TestResultRecord: uid: User-defined unique identifier of the test. signature: string, unique identifier of a test record, the value is generated by Mobly. - retry_parent: TestResultRecord, only set for retry iterations. This is the - test result record of the previous retry iteration. Parsers can use this - field to construct the chain of execution for each retried test. + retry_parent: [DEPRECATED] Use the `parent` field instead. + parent: tuple[TestResultRecord, TestParentType], set for multiple iterations + of a test. This is the test result record of the previous iteration. + Parsers can use this field to construct the chain of execution for each test. termination_signal: ExceptionRecord, the main exception of the test. extra_errors: OrderedDict, all exceptions occurred during the entire test lifecycle. The order of occurrence is preserved. @@ -334,6 +343,7 @@ class TestResultRecord: self.uid = None self.signature = None self.retry_parent = None + self.parent = None self.termination_signal = None self.extra_errors = collections.OrderedDict() self.result = None @@ -506,6 +516,14 @@ class TestResultRecord: d[TestResultEnums.RECORD_RETRY_PARENT] = ( self.retry_parent.signature if self.retry_parent else None ) + d[TestResultEnums.RECORD_PARENT] = ( + { + 'parent': self.parent[0].signature, + 'type': self.parent[1].value, + } + if self.parent + else None + ) d[TestResultEnums.RECORD_EXTRAS] = self.extras d[TestResultEnums.RECORD_DETAILS] = self.details d[TestResultEnums.RECORD_TERMINATION_SIGNAL_TYPE] = ( @@ -644,9 +662,9 @@ class TestResult: count = 0 for record in self.passed: r = record - while r.retry_parent: + while r.parent is not None: count += 1 - r = r.retry_parent + r = r.parent[0] return count @property diff --git a/tests/mobly/base_test_test.py b/tests/mobly/base_test_test.py index 630bd82..bfc7b75 100755 --- a/tests/mobly/base_test_test.py +++ b/tests/mobly/base_test_test.py @@ -2615,7 +2615,13 @@ class BaseTestTest(unittest.TestCase): self.assertEqual(error_record_1.test_name, 'test_something') self.assertEqual(error_record_2.test_name, 'test_something_retry_1') self.assertIs(error_record_1, error_record_2.retry_parent) + self.assertEqual( + (error_record_1, records.TestParentType.RETRY), error_record_2.parent + ) self.assertIs(error_record_2, pass_record.retry_parent) + self.assertEqual( + (error_record_2, records.TestParentType.RETRY), pass_record.parent + ) def test_retry_generated_test_last_pass(self): max_count = 3 @@ -2650,7 +2656,13 @@ class BaseTestTest(unittest.TestCase): self.assertEqual(error_record_1.test_name, 'test_generated_1') self.assertEqual(error_record_2.test_name, 'test_generated_1_retry_1') self.assertIs(error_record_1, error_record_2.retry_parent) + self.assertEqual( + (error_record_1, records.TestParentType.RETRY), error_record_2.parent + ) self.assertIs(error_record_2, pass_record.retry_parent) + self.assertEqual( + (error_record_2, records.TestParentType.RETRY), pass_record.parent + ) def test_retry_all_fail(self): max_count = 3 @@ -2679,7 +2691,13 @@ class BaseTestTest(unittest.TestCase): self.assertEqual(error_record_2.test_name, 'test_something_retry_1') self.assertEqual(error_record_3.test_name, 'test_something_retry_2') self.assertIs(error_record_1, error_record_2.retry_parent) + self.assertEqual( + (error_record_1, records.TestParentType.RETRY), error_record_2.parent + ) self.assertIs(error_record_2, error_record_3.retry_parent) + self.assertEqual( + (error_record_2, records.TestParentType.RETRY), error_record_3.parent + ) def test_uid(self): class MockBaseTest(base_test.BaseTestClass): @@ -2726,9 +2744,17 @@ class BaseTestTest(unittest.TestCase): bt_cls = MockBaseTest(self.mock_test_cls_configs) bt_cls.run() self.assertEqual(repeat_count, len(bt_cls.results.passed)) + previous_record = None for i, record in enumerate(bt_cls.results.passed): self.assertEqual(record.test_name, f'test_something_{i}') self.assertEqual(record.uid, 'some-uid') + if i == 0: + self.assertIsNone(record.parent) + else: + self.assertEqual( + record.parent, (previous_record, records.TestParentType.REPEAT) + ) + previous_record = record def test_uid_with_repeat(self): repeat_count = 3 diff --git a/tests/mobly/records_test.py b/tests/mobly/records_test.py index bcee67a..f3140a9 100755 --- a/tests/mobly/records_test.py +++ b/tests/mobly/records_test.py @@ -98,6 +98,7 @@ class RecordsTest(unittest.TestCase): ) d[records.TestResultEnums.RECORD_UID] = None d[records.TestResultEnums.RECORD_RETRY_PARENT] = None + d[records.TestResultEnums.RECORD_PARENT] = None d[records.TestResultEnums.RECORD_CLASS] = None d[records.TestResultEnums.RECORD_EXTRA_ERRORS] = {} d[records.TestResultEnums.RECORD_STACKTRACE] = stacktrace |