aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorboon <ohbooneng@google.com>2023-12-29 09:15:34 +0800
committerGitHub <noreply@github.com>2023-12-28 17:15:34 -0800
commit46ff13e8bfaac24b6ba3ea43b2da9b5a1b0dec68 (patch)
treeaf8400a5c85037110faa2899aeec09fb3e0dae08
parent6c5d666c44d350a2f906351c645d4f8313463a43 (diff)
downloadmobly-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.py12
-rw-r--r--mobly/records.py28
-rwxr-xr-xtests/mobly/base_test_test.py26
-rwxr-xr-xtests/mobly/records_test.py1
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