test(panic): enable the memprot panic tests for ESP32-S3

sdkconfig.ci.memprot_esp32s3 was built but listed in no CONFIGS_MEMPROT_*, so
the S3 PMS panic path was never exercised.

expect_gme() takes an optional core, for panics with no attributable core.
This commit is contained in:
harshal.patil
2026-08-26 14:46:42 +05:30
parent 73dd203204
commit 8daab867b7
2 changed files with 63 additions and 13 deletions

View File

@@ -35,11 +35,13 @@ def configs_for_app(app_path: str, configs: Sequence[str]) -> list[tuple[str, st
return [(app_path, config) for config in configs]
def configs_with_esp32s2_xfail(
configs: Sequence[tuple[str, str] | tuple[str, str, Any]], reason: str
def configs_with_xfail(
configs: Sequence[tuple[str, str] | tuple[str, str, Any]],
reason: str,
targets: Sequence[str] = ('esp32s2',),
) -> list[tuple[str, str] | tuple[str, str, Any]]:
return [
(entry[0], entry[1], pytest.mark.xfail(reason=reason, run=False)) if entry[1] == 'esp32s2' else entry
(entry[0], entry[1], pytest.mark.xfail(reason=reason, run=False)) if entry[1] in targets else entry
for entry in configs
]
@@ -618,7 +620,8 @@ def test_panic_handler_crash1(dut: PanicTestDut, config: str, test_func_name: st
#########################
# Memprot-related tests are supported only on targets with PMS/PMA peripheral;
# currently ESP32-S2, ESP32-C3, ESP32-C2, ESP32-H2, ESP32-H21, ESP32-C6, ESP32-P4, ESP32-C5 and ESP32-C61 are supported
# currently ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C2, ESP32-H2, ESP32-H21, ESP32-C6, ESP32-P4,
# ESP32-C5, ESP32-C61, ESP32-H4 are supported
# ESP32-P4 rev < 3.0 runs on a dedicated rev 1.x runner (its binary is built for
# and only boots on rev < 3.0 silicon), so its configs carry the esp32p4_rev1 marker.
P4_REV_LESS_THAN_V3_MARKER = pytest.mark.esp32p4_rev1
@@ -627,6 +630,7 @@ CONFIGS_MEMPROT_IDRAM = list(
zip(
[
'memprot_esp32s2',
'memprot_esp32s3',
'memprot_esp32c3',
'memprot_esp32c2',
'memprot_esp32c5',
@@ -639,6 +643,7 @@ CONFIGS_MEMPROT_IDRAM = list(
],
[
'esp32s2',
'esp32s3',
'esp32c3',
'esp32c2',
'esp32c5',
@@ -662,6 +667,7 @@ CONFIGS_MEMPROT_RTC_FAST_MEM = list(
zip(
[
'memprot_esp32s2',
'memprot_esp32s3',
'memprot_esp32c3',
'memprot_esp32c5',
'memprot_esp32c6',
@@ -670,7 +676,7 @@ CONFIGS_MEMPROT_RTC_FAST_MEM = list(
'memprot_esp32h21',
'memprot_esp32s31',
],
['esp32s2', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'],
['esp32s2', 'esp32s3', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'],
)
) + [
('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
@@ -792,6 +798,13 @@ def iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> None:
dut.expect(r'Write operation at address [0-9xa-f]+ not permitted \((\S+)\)')
dut.expect_reg_dump(0)
dut.expect_backtrace()
elif dut.target == 'esp32s3':
dut.expect_gme('Memory protection fault')
dut.expect(r' memory type: (\S+)')
dut.expect(r' faulting address: [0-9xa-f]+')
dut.expect(r' operation type: (\S+)')
dut.expect_reg_dump(0)
dut.expect_backtrace()
elif dut.target == 'esp32c3':
dut.expect_exact(r'Test error: Test function has returned')
else:
@@ -824,6 +837,13 @@ def iram_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None:
dut.expect(r'Write operation at address [0-9xa-f]+ not permitted \((\S+)\)')
dut.expect_reg_dump(0)
dut.expect_backtrace()
elif dut.target == 'esp32s3':
dut.expect_gme('Memory protection fault')
dut.expect(r' memory type: (\S+)')
dut.expect(r' faulting address: [0-9xa-f]+')
dut.expect(r' operation type: (\S+)')
dut.expect_reg_dump(0)
dut.expect_backtrace()
elif dut.target == 'esp32c3':
dut.expect_gme('Memory protection fault')
dut.expect(r' memory type: (\S+)')
@@ -915,10 +935,10 @@ def iram_reg4_write_violation(dut: PanicTestDut, test_func_name: str) -> None:
@pytest.mark.generic
@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
# TODO: IDF-6820: ESP32-S2 -> Fix incorrect panic reason: Unhandled debug exception
# TODO: IDF-6820: ESP32-S2 / ESP32-S3 -> Fix incorrect panic reason: Unhandled debug exception
@idf_parametrize(
'config,target,markers',
configs_with_esp32s2_xfail(CONFIGS_MEMPROT_IDRAM, 'Incorrect panic reason may be observed'),
configs_with_xfail(CONFIGS_MEMPROT_IDRAM, 'Incorrect panic reason may be observed', targets=('esp32s2', 'esp32s3')),
indirect=['config', 'target'],
)
def test_iram_reg4_write_violation(dut: PanicTestDut, test_func_name: str) -> None:
@@ -940,6 +960,11 @@ def dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
dut.expect(r'Unknown operation at address [0-9xa-f]+ not permitted \((\S+)\)')
dut.expect_reg_dump(0)
dut.expect_backtrace(corrupted=True)
elif dut.target == 'esp32s3':
dut.expect_gme('Cache error', core=None)
dut.expect_exact('MMU entry fault error')
dut.expect_reg_dump(0)
dut.expect_backtrace()
else:
dut.expect_gme('Instruction access fault')
dut.expect_reg_dump(0)
@@ -953,7 +978,7 @@ def dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs
@idf_parametrize(
'config,target,markers',
configs_with_esp32s2_xfail(CONFIGS_MEMPROT_IDRAM, 'Multiple panic reasons for the same test may surface'),
configs_with_xfail(CONFIGS_MEMPROT_IDRAM, 'Multiple panic reasons for the same test may surface'),
indirect=['config', 'target'],
)
def test_dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
@@ -974,6 +999,11 @@ def dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
dut.expect_gme('InstructionFetchError')
dut.expect_reg_dump(0)
dut.expect_backtrace(corrupted=True)
elif dut.target == 'esp32s3':
dut.expect_gme('Cache error', core=None)
dut.expect_exact('MMU entry fault error')
dut.expect_reg_dump(0)
dut.expect_backtrace(corrupted=True)
else:
dut.expect_gme('Instruction access fault')
dut.expect_reg_dump(0)
@@ -987,7 +1017,7 @@ def dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs
@idf_parametrize(
'config,target,markers',
configs_with_esp32s2_xfail(CONFIGS_MEMPROT_IDRAM, 'Multiple panic reasons for the same test may surface'),
configs_with_xfail(CONFIGS_MEMPROT_IDRAM, 'Multiple panic reasons for the same test may surface'),
indirect=['config', 'target'],
)
def test_dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
@@ -1024,6 +1054,12 @@ def test_rtc_fast_reg2_execute_violation(dut: PanicTestDut, test_func_name: str)
dut.expect(r'Read operation at address [0-9xa-f]+ not permitted \((\S+)\)')
dut.expect_reg_dump(0)
dut.expect_backtrace()
elif dut.target == 'esp32s3':
dut.expect(r' memory type: (\S+)')
dut.expect(r' faulting address: [0-9xa-f]+')
dut.expect(r' operation type: (\S+)')
dut.expect_reg_dump(0)
dut.expect_backtrace()
elif dut.target == 'esp32c3':
dut.expect(r' memory type: (\S+)')
dut.expect(r' faulting address: [0-9xa-f]+')
@@ -1039,7 +1075,7 @@ def test_rtc_fast_reg2_execute_violation(dut: PanicTestDut, test_func_name: str)
# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs
@idf_parametrize(
'config,target,markers',
configs_with_esp32s2_xfail(CONFIGS_MEMPROT_RTC_FAST_MEM, 'Multiple panic reasons for the same test may surface'),
configs_with_xfail(CONFIGS_MEMPROT_RTC_FAST_MEM, 'Multiple panic reasons for the same test may surface'),
indirect=['config', 'target'],
)
def test_rtc_fast_reg3_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
@@ -1050,6 +1086,13 @@ def test_rtc_fast_reg3_execute_violation(dut: PanicTestDut, test_func_name: str)
dut.expect(r'Unknown operation at address [0-9xa-f]+ not permitted \((\S+)\)')
dut.expect_reg_dump(0)
dut.expect_backtrace()
elif dut.target == 'esp32s3':
dut.expect_gme('Memory protection fault')
dut.expect(r' memory type: (\S+)')
dut.expect(r' faulting address: [0-9xa-f]+')
dut.expect(r' operation type: (\S+)')
dut.expect_reg_dump(0)
dut.expect_backtrace()
elif dut.target == 'esp32c3':
dut.expect_gme('Memory protection fault')
dut.expect(r' memory type: (\S+)')

View File

@@ -104,9 +104,16 @@ class PanicTestDut(IdfDut):
result = self.expect(pattern, return_what_before_match=True).decode('utf-8')
return result.strip()
def expect_gme(self, reason: str) -> None:
"""Expect method for Guru Meditation Errors"""
self.expect_exact(f"Guru Meditation Error: Core 0 panic'ed ({reason})")
def expect_gme(self, reason: str, core: int | None = 0) -> None:
"""Expect method for Guru Meditation Errors
Pass core=None to accept any core id, including the -1 reported for
faults which cannot be attributed to a single core.
"""
if core is None:
self.expect(rf"Guru Meditation Error: Core\s+\S+ panic'ed \({re.escape(reason)}\)")
else:
self.expect_exact(f"Guru Meditation Error: Core {core} panic'ed ({reason})")
def expect_reg_dump(self, core: int | None = None) -> None:
if core is None: