Skip to content

Commit e2f7bf7

Browse files
committed
feat(unity): add flag to preserve Python test cases and "is_unity_case" attribute
1 parent 8b81166 commit e2f7bf7

File tree

3 files changed

+116
-4
lines changed

3 files changed

+116
-4
lines changed

pytest-embedded-idf/tests/test_idf.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,3 +897,93 @@ def test_erase_all_with_port_cache_case2(dut):
897897
)
898898

899899
result.assert_outcomes(passed=2)
900+
901+
902+
def test_no_preserve_python_tests(testdir):
903+
testdir.makepyfile(r"""
904+
def test_python_case(dut):
905+
dut.run_all_single_board_cases(name=["normal_case1", "multiple_stages_test"])
906+
""")
907+
908+
testdir.runpytest(
909+
'-s',
910+
'--embedded-services', 'esp,idf',
911+
'--app-path', os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
912+
'--log-cli-level', 'DEBUG',
913+
'--junitxml', 'report.xml',
914+
)
915+
916+
junit_report = ET.parse('report.xml').getroot()[0]
917+
918+
assert junit_report.attrib['tests'] == '2'
919+
for testcase in junit_report.findall('testcase'):
920+
assert testcase.attrib['is_unity_case'] == '1'
921+
922+
def test_preserve_python_tests(testdir):
923+
testdir.makepyfile(r"""
924+
def test_python_case(dut):
925+
dut.run_all_single_board_cases(name=["normal_case1", "multiple_stages_test"])
926+
""")
927+
928+
testdir.runpytest(
929+
'-s',
930+
'--embedded-services', 'esp,idf',
931+
'--app-path', os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
932+
'--log-cli-level', 'DEBUG',
933+
'--junitxml', 'report.xml',
934+
'--unity-test-report-mode', 'merge',
935+
)
936+
937+
junit_report = ET.parse('report.xml').getroot()[0]
938+
939+
assert junit_report.attrib['tests'] == '2'
940+
assert junit_report[0].attrib['is_unity_case'] == '0'
941+
for testcase in junit_report[1:]:
942+
assert testcase.attrib['is_unity_case'] == '1'
943+
944+
945+
def test_preserve_python_tests_with_failures(testdir):
946+
testdir.makepyfile(r"""
947+
def test_python_case(dut):
948+
dut.run_all_single_board_cases(name=["normal_case1", "normal_case2"])
949+
""")
950+
951+
testdir.runpytest(
952+
'-s',
953+
'--embedded-services', 'esp,idf',
954+
'--app-path', os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
955+
'--log-cli-level', 'DEBUG',
956+
'--junitxml', 'report.xml',
957+
'--unity-test-report-mode', 'merge',
958+
)
959+
960+
junit_report = ET.parse('report.xml').getroot()[0]
961+
962+
assert junit_report.attrib['failures'] == '1'
963+
assert junit_report[0].attrib['is_unity_case'] == '0' # Python test case is preserved
964+
assert junit_report[1].attrib['is_unity_case'] == '1' # C test case
965+
assert junit_report[1].find('failure') is None # normal_case1 passed
966+
assert junit_report[2].attrib['is_unity_case'] == '1'
967+
assert junit_report[2].find('failure') is not None # normal_case2 failed
968+
969+
970+
def test_python_func_attribute(testdir):
971+
testdir.makepyfile(r"""
972+
def test_python_case(dut):
973+
dut.run_all_single_board_cases(name=["normal_case1", "multiple_stages_test"])
974+
""")
975+
976+
testdir.runpytest(
977+
'-s',
978+
'--embedded-services', 'esp,idf',
979+
'--app-path', os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
980+
'--log-cli-level', 'DEBUG',
981+
'--junitxml', 'report.xml',
982+
'--unity-test-report-mode', 'merge',
983+
)
984+
985+
junit_report = ET.parse('report.xml').getroot()[0]
986+
987+
assert junit_report[0].attrib['is_unity_case'] == '0' # Python test case
988+
for testcase in junit_report[1:]:
989+
assert testcase.attrib['is_unity_case'] == '1' # Other test cases

pytest-embedded/pytest_embedded/plugin.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
wokwi_gn,
4444
)
4545
from .log import MessageQueue, PexpectProcess
46-
from .unity import JunitMerger, escape_illegal_xml_chars
46+
from .unity import JunitMerger, UnityTestReportMode, escape_illegal_xml_chars
4747
from .utils import (
4848
SERVICE_LIB_NAMES,
4949
ClassCliOptions,
@@ -111,6 +111,16 @@ def pytest_addoption(parser):
111111
help='y/yes/true for True and n/no/false for False. '
112112
'Set to True to prettify XML junit report. (Default: False)',
113113
)
114+
parser.addoption(
115+
'--unity-test-report-mode',
116+
choices=[UnityTestReportMode.REPLACE.value, UnityTestReportMode.MERGE.value],
117+
default=UnityTestReportMode.REPLACE.value,
118+
help=(
119+
'Specify the behavior for handling Unity test cases in the main JUnit report. '
120+
"'merge' includes them alongside the parent Python test case. "
121+
"'replace' substitutes the parent Python test case with Unity test cases (default)."
122+
),
123+
)
114124

115125
# supports parametrization
116126
base_group.addoption('--root-logdir', help='set session-based root log dir. (Default: system temp folder)')
@@ -1183,7 +1193,9 @@ def unity_tester(dut: t.Union['IdfDut', t.Tuple['IdfDut']]) -> t.Optional['CaseT
11831193

11841194

11851195
def pytest_configure(config: Config) -> None:
1186-
config.stash[_junit_merger_key] = JunitMerger(config.option.xmlpath)
1196+
config.stash[_junit_merger_key] = JunitMerger(
1197+
config.option.xmlpath, config.getoption('unity_test_report_mode', default='replace')
1198+
)
11871199
config.stash[_junit_report_path_key] = config.option.xmlpath
11881200

11891201
config.stash[_pytest_embedded_key] = PytestEmbedded(

pytest-embedded/pytest_embedded/unity.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ class TestFormat(enum.Enum):
7676
FIXTURE = 1
7777

7878

79+
class UnityTestReportMode(str, enum.Enum):
80+
REPLACE = 'replace'
81+
MERGE = 'merge'
82+
83+
7984
class TestCase:
8085
def __init__(self, name: str, result: str, **kwargs):
8186
self.name = name
@@ -206,8 +211,9 @@ class JunitMerger:
206211
SUB_JUNIT_FILENAME = 'dut.xml'
207212
# multi-dut junit reports should be dut-[INDEX].xml
208213

209-
def __init__(self, main_junit: Optional[str]) -> None:
214+
def __init__(self, main_junit: Optional[str], unity_test_report_mode: Optional[str] = None) -> None:
210215
self.junit_path = main_junit
216+
self.unity_test_report_mode = unity_test_report_mode or UnityTestReportMode.REPLACE.value
211217

212218
self._junit = None
213219

@@ -291,9 +297,13 @@ def merge(self, junit_files: List[str]):
291297
raise ValueError(f'Could\'t find test case {test_case_name}, dumped into "debug.xml" for debugging')
292298

293299
junit_case_is_fail = junit_case.find('failure') is not None
294-
junit_parent.remove(junit_case)
300+
301+
junit_case.attrib['is_unity_case'] = '0'
302+
if self.unity_test_report_mode == UnityTestReportMode.REPLACE.value:
303+
junit_parent.remove(junit_case)
295304

296305
for case in merging_cases:
306+
case.attrib['is_unity_case'] = '1'
297307
junit_parent.append(case)
298308

299309
junit_parent.attrib['errors'] = self._int_add(

0 commit comments

Comments
 (0)