Skip to content

Commit a8186cf

Browse files
authored
Merge pull request #405 from espressif/fix/pre-run-failure
Fix/pre run failure
2 parents da8c239 + a26de86 commit a8186cf

File tree

28 files changed

+383
-504
lines changed

28 files changed

+383
-504
lines changed

.gitignore

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ venv/
7171
.idea/
7272

7373
# built binaries
74-
tests/fixtures/*/build/*
74+
tests/fixtures/*/build*/*
7575
!tests/fixtures/*_coredump_*/build/gdbinit/
7676
tests/fixtures/*_coredump_*/build/gdbinit/*
7777
!tests/fixtures/*_coredump_*/build/gdbinit/prefix_map
@@ -82,15 +82,15 @@ tests/fixtures/*_panic/build/gdbinit/*
8282
!tests/fixtures/*_panic/build/gdbinit/prefix_map
8383
!tests/fixtures/*_panic/build/*.elf
8484

85-
!tests/fixtures/*/build/bootloader/
86-
tests/fixtures/*/build/bootloader/*
87-
!tests/fixtures/*/build/bootloader/*.bin
88-
!tests/fixtures/*/build/partition_table/
89-
!tests/fixtures/*/build/config/
90-
tests/fixtures/*/build/config/*
91-
!tests/fixtures/*/build/config/sdkconfig.json
92-
!tests/fixtures/*/build/flasher_args.json
93-
!tests/fixtures/*/build/*.bin
85+
!tests/fixtures/*/build*/bootloader/
86+
tests/fixtures/*/build*/bootloader/*
87+
!tests/fixtures/*/build*/bootloader/*.bin
88+
!tests/fixtures/*/build*/partition_table/
89+
!tests/fixtures/*/build*/config/
90+
tests/fixtures/*/build*/config/*
91+
!tests/fixtures/*/build*/config/sdkconfig.json
92+
!tests/fixtures/*/build*/flasher_args.json
93+
!tests/fixtures/*/build*/*.bin
9494

9595
sdkconfig
9696
sdkconfig.old

.idf_build_apps.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
recursive = true
2+
check_warnings = true
3+
build_dir = "build_@t"
4+
verbose = 1 # INFO

pytest-embedded-idf/pytest_embedded_idf/unity_tester.py

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -379,9 +379,14 @@ def _run_normal_case(
379379
try:
380380
_start_at = self._prepare_and_start_case(case, reset, timeout)
381381
except Exception as e:
382-
self._analyze_test_case_result(case, e)
383-
else:
384-
self._analyze_test_case_result(case, None, start_time=_start_at, timeout=timeout)
382+
logging.debug('pre_run_failure: %s. hard reset and retry', e)
383+
try:
384+
_start_at = self._prepare_and_start_case(case, True, timeout)
385+
except Exception as e2:
386+
self._analyze_test_case_result(case, e2)
387+
return
388+
389+
self._analyze_test_case_result(case, None, start_time=_start_at, timeout=timeout)
385390

386391
def _run_multi_stage_case(
387392
self,
@@ -408,37 +413,42 @@ def _run_multi_stage_case(
408413
try:
409414
_start_at = self._prepare_and_start_case(case, reset, timeout)
410415
except Exception as e:
411-
self._analyze_test_case_result(case, e)
412-
else:
413-
failed_subcases = []
416+
logging.debug('pre_run_failure: %s. hard reset and retry', e)
414417
try:
415-
for sub_case in case.subcases:
416-
if sub_case != case.subcases[0]:
417-
ready_before = self._get_ready(timeout, return_before=True)
418-
if ready_before and UNITY_SUMMARY_LINE_REGEX.search(ready_before):
419-
attrs = _parse_unity_test_output(
420-
remove_asci_color_code(ready_before), case.name, self.pexpect_proc.buffer_debug_str
421-
)
422-
if attrs['result'] == 'FAIL':
423-
failed_subcases.append(attrs)
424-
425-
self.confirm_write(case.index, expect_str=f'Running {case.name}...')
426-
427-
self.write(str(sub_case['index']))
428-
except Exception:
429-
# Any exception during the sub-case loop is a runtime failure.
430-
# We'll stop sending commands and let the result recorder handle the failure.
431-
pass
432-
finally:
433-
attrs = self._read_result_and_parse_attrs(case, _start_at, timeout)
418+
_start_at = self._prepare_and_start_case(case, True, timeout)
419+
except Exception as e2:
420+
self._analyze_test_case_result(case, e2)
421+
return
434422

435-
if attrs['result'] == 'FAIL':
436-
failed_subcases.append(attrs)
423+
failed_subcases = []
424+
try:
425+
for sub_case in case.subcases:
426+
if sub_case != case.subcases[0]:
427+
ready_before = self._get_ready(timeout, return_before=True)
428+
if ready_before and UNITY_SUMMARY_LINE_REGEX.search(ready_before):
429+
attrs = _parse_unity_test_output(
430+
remove_asci_color_code(ready_before), case.name, self.pexpect_proc.buffer_debug_str
431+
)
432+
if attrs['result'] == 'FAIL':
433+
failed_subcases.append(attrs)
434+
435+
self.confirm_write(case.index, expect_str=f'Running {case.name}...')
436+
437+
self.write(str(sub_case['index']))
438+
except Exception:
439+
# Any exception during the sub-case loop is a runtime failure.
440+
# We'll stop sending commands and let the result recorder handle the failure.
441+
pass
442+
finally:
443+
attrs = self._read_result_and_parse_attrs(case, _start_at, timeout)
437444

438-
if failed_subcases:
439-
self._add_test_case_to_suite(self._squash_failed_subcases(failed_subcases, _start_at))
440-
else:
441-
self._add_test_case_to_suite(attrs)
445+
if attrs['result'] == 'FAIL':
446+
failed_subcases.append(attrs)
447+
448+
if failed_subcases:
449+
self._add_test_case_to_suite(self._squash_failed_subcases(failed_subcases, _start_at))
450+
else:
451+
self._add_test_case_to_suite(attrs)
442452

443453
def run_single_board_case(self, name: str, reset: bool = False, timeout: float = 30) -> None:
444454
for case in self.test_menu:

pytest-embedded-idf/tests/test_idf.py

Lines changed: 43 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ def test_idf_serial_flash(dut):
6262
def test_custom_idf_device_dut(testdir):
6363
p = os.path.join(testdir.tmpdir, 'hello_world_esp32')
6464
p_c3 = os.path.join(testdir.tmpdir, 'hello_world_esp32c3')
65-
unity_test_path = os.path.join(testdir.tmpdir, 'unit_test_app_esp32')
66-
unity_test_path_c3 = os.path.join(testdir.tmpdir, 'unit_test_app_esp32c3')
65+
66+
unity_test_app_path = os.path.join(testdir.tmpdir, 'unit_test_app_idf')
67+
6768
testdir.makepyfile(f"""
6869
import pytest
6970
@@ -81,14 +82,20 @@ def test_idf_mixed(dut):
8182
8283
def test_idf_unity_tester():
8384
from pytest_embedded.dut_factory import DutFactory
84-
dut1 = DutFactory.create(embedded_services='esp,idf', app_path=r'{unity_test_path}')
85-
dut2 = DutFactory.create(embedded_services='esp,idf', app_path=r'{unity_test_path_c3}')
85+
dut1 = DutFactory.create(
86+
embedded_services='esp,idf', app_path=r'{unity_test_app_path}', build_dir='build_esp32'
87+
)
88+
dut2 = DutFactory.create(
89+
embedded_services='esp,idf', app_path=r'{unity_test_app_path}', build_dir='build_esp32c3'
90+
)
8691
tester = DutFactory.unity_tester(dut1, dut2)
8792
tester.run_all_multi_dev_cases(timeout=10)
8893
8994
def test_idf_run_all_single_board_cases():
9095
from pytest_embedded.dut_factory import DutFactory
91-
dut1 = DutFactory.create(embedded_services='esp,idf', app_path=r'{unity_test_path}')
96+
dut1 = DutFactory.create(
97+
embedded_services='esp,idf', app_path=r'{unity_test_app_path}', build_dir='build_esp32'
98+
)
9299
dut1.run_all_single_board_cases(reset=True, timeout=10)
93100
""")
94101

@@ -424,7 +431,9 @@ def test_flash_another_app(dut):
424431
result = testdir.runpytest(
425432
'-s',
426433
'--app-path',
427-
os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
434+
os.path.join(testdir.tmpdir, 'unit_test_app_idf'),
435+
'--build-dir',
436+
'build_esp32',
428437
'--embedded-services',
429438
'esp,idf',
430439
'--part-tool',
@@ -748,55 +757,20 @@ def test_select_to_run():
748757
assert IdfUnityDutMixin._select_to_run([['hello', '!w']], None, None, ['hello', 'world'], None, None)
749758

750759

751-
def test_dut_run_all_single_board_cases_reset_false(testdir):
752-
testdir.makepyfile(r"""
753-
def test_dut_run_all_single_board_cases(dut):
754-
dut.run_all_single_board_cases(timeout=10)
755-
""")
756-
testdir.runpytest(
757-
'-s',
758-
'--embedded-services',
759-
'esp,idf',
760-
'--app-path',
761-
os.path.join(testdir.tmpdir, 'unit_test_app_esp32c3'),
762-
'--log-cli-level',
763-
'DEBUG',
764-
'--junitxml',
765-
'report.xml',
766-
)
767-
768-
junit_report = ET.parse('report.xml').getroot()[0]
769-
770-
assert junit_report.attrib['errors'] == '0'
771-
assert junit_report.attrib['failures'] == '2'
772-
assert junit_report.attrib['skipped'] == '2'
773-
assert junit_report.attrib['tests'] == '1'
774-
775-
testcases = junit_report.findall('.//testcase')
776-
assert testcases[0].attrib['name'] == 'normal_case_pass'
777-
assert testcases[1].attrib['name'] == 'normal_case_crash'
778-
assert testcases[2].attrib['name'] == 'normal_case_stuck'
779-
assert testcases[3].attrib['name'] == 'normal_case_skip_when_not_reset'
780-
assert testcases[4].attrib['name'] == 'multiple_stages_test'
781-
782-
assert 10 < float(testcases[1].attrib['time']) < 10.1
783-
assert 'Guru Meditation Error' in testcases[1][0].attrib['message']
784-
785-
assert 'Skipped due to a failure before test execution.' in testcases[3][0].attrib['message']
786-
assert 'Skipped due to a failure before test execution.' in testcases[4][0].attrib['message']
787-
788-
789-
def test_dut_run_all_single_board_cases_reset_true(testdir):
790-
testdir.makepyfile(r"""
760+
@pytest.mark.parametrize('reset', [True, False])
761+
def test_dut_run_all_single_board_cases(testdir, reset):
762+
testdir.makepyfile(rf"""
791763
def test_dut_run_all_single_board_cases(dut):
792-
dut.run_all_single_board_cases(reset=True, timeout=10)
764+
dut.run_all_single_board_cases(timeout=10, reset={reset})
793765
""")
794766
testdir.runpytest(
795767
'-s',
796768
'--embedded-services',
797769
'esp,idf',
798770
'--app-path',
799-
os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
771+
os.path.join(testdir.tmpdir, 'unit_test_app_idf'),
772+
'--build-dir',
773+
'build_esp32c3',
800774
'--log-cli-level',
801775
'DEBUG',
802776
'--junitxml',
@@ -814,7 +788,7 @@ def test_dut_run_all_single_board_cases(dut):
814788
assert testcases[0].attrib['name'] == 'normal_case_pass'
815789
assert testcases[1].attrib['name'] == 'normal_case_crash'
816790
assert testcases[2].attrib['name'] == 'normal_case_stuck'
817-
assert testcases[3].attrib['name'] == 'normal_case_skip_when_not_reset'
791+
assert testcases[3].attrib['name'] == 'normal_case_pass_auto_reset'
818792
assert testcases[4].attrib['name'] == 'multiple_stages_test'
819793

820794
assert 10 < float(testcases[1].attrib['time']) < 10.1
@@ -832,7 +806,9 @@ def test_dut_run_all_single_board_cases(dut):
832806
'--embedded-services',
833807
'esp,idf',
834808
'--app-path',
835-
os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
809+
os.path.join(testdir.tmpdir, 'unit_test_app_idf'),
810+
'--build-dir',
811+
'build_esp32',
836812
'--log-cli-level',
837813
'DEBUG',
838814
'--junitxml',
@@ -843,8 +819,8 @@ def test_dut_run_all_single_board_cases(dut):
843819

844820
assert junit_report.attrib['errors'] == '0'
845821
assert junit_report.attrib['failures'] == '2'
846-
assert junit_report.attrib['skipped'] == '1'
847-
assert junit_report.attrib['tests'] == '1'
822+
assert junit_report.attrib['skipped'] == '0'
823+
assert junit_report.attrib['tests'] == '2'
848824

849825

850826
def test_dut_run_all_single_board_cases_invert_group(testdir):
@@ -857,7 +833,9 @@ def test_dut_run_all_single_board_cases(dut):
857833
'--embedded-services',
858834
'esp,idf',
859835
'--app-path',
860-
os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
836+
os.path.join(testdir.tmpdir, 'unit_test_app_idf'),
837+
'--build-dir',
838+
'build_esp32',
861839
'--junitxml',
862840
'report.xml',
863841
)
@@ -882,7 +860,9 @@ def test_dut_run_all_single_board_cases(dut):
882860
'--embedded-services',
883861
'esp,idf',
884862
'--app-path',
885-
os.path.join(testdir.tmpdir, 'unit_test_app_esp32'),
863+
os.path.join(testdir.tmpdir, 'unit_test_app_idf'),
864+
'--build-dir',
865+
'build_esp32',
886866
'--junitxml',
887867
'report.xml',
888868
)
@@ -916,9 +896,9 @@ def test_unity_test_case_runner_without_reset(unity_tester):
916896
'--count',
917897
2,
918898
'--app-path',
919-
f'{os.path.join(testdir.tmpdir, "unit_test_app_esp32")}'
920-
f'|'
921-
f'{os.path.join(testdir.tmpdir, "unit_test_app_esp32c3")}',
899+
os.path.join(testdir.tmpdir, 'unit_test_app_idf'),
900+
'--build-dir',
901+
'build_esp32|build_esp32c3',
922902
'--junitxml',
923903
'report.xml',
924904
)
@@ -949,9 +929,9 @@ def test_unity_test_case_runner_without_reset(unity_tester):
949929
'--count',
950930
2,
951931
'--app-path',
952-
f'{os.path.join(testdir.tmpdir, "unit_test_app_esp32")}'
953-
f'|'
954-
f'{os.path.join(testdir.tmpdir, "unit_test_app_esp32c3")}',
932+
f'{os.path.join(testdir.tmpdir, "unit_test_app_idf")}',
933+
'--build-dir',
934+
'build_esp32|build_esp32c3',
955935
'--junitxml',
956936
'report.xml',
957937
)
@@ -1003,7 +983,9 @@ def test_python_case(dut):
1003983
'--embedded-services',
1004984
'esp,idf',
1005985
'--app-path',
1006-
os.path.join(testdir.tmpdir, 'unit_test_app_esp32c3'),
986+
f'{os.path.join(testdir.tmpdir, "unit_test_app_idf")}',
987+
'--build-dir',
988+
'build_esp32c3',
1007989
'--junitxml',
1008990
'report.xml',
1009991
'--unity-test-report-mode',
-25.7 KB
Binary file not shown.
-165 KB
Binary file not shown.

tests/fixtures/unit_test_app_esp32c3/CMakeLists.txt

Lines changed: 0 additions & 7 deletions
This file was deleted.
Binary file not shown.
Binary file not shown.

tests/fixtures/unit_test_app_esp32c3/main/CMakeLists.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)