Jelajahi Sumber

ci: move more openocd scripts into pytest-embedded

Fu Hanxi 3 tahun lalu
induk
melakukan
dc3c630919

+ 12 - 18
.gitlab/ci/target-test.yml

@@ -77,6 +77,17 @@ example_test_pytest_esp32_twai_network:
     - build_pytest_examples_esp32
   tags: [ esp32, twai_network ]
 
+example_test_pytest_esp32_jtag:
+  extends:
+    - .pytest_examples_dir_template
+    - .rules:test:example_test-esp32
+  needs:
+    - build_pytest_examples_esp32
+  tags: [ esp32, jtag ]
+  variables:
+    SETUP_TOOLS: "1"  # need gdb openocd
+    PYTEST_EXTRA_FLAGS: "--log-cli-level DEBUG"
+
 example_test_pytest_esp32s2_generic:
   extends:
     - .pytest_examples_dir_template
@@ -670,7 +681,7 @@ test_app_test_pytest_esp32_jtag:
     - .rules:test:custom_test-esp32
   needs:
     - build_pytest_test_apps_esp32
-  tags: [ esp32, test_jtag_arm]
+  tags: [ esp32, jtag]
   variables:
     SETUP_TOOLS: "1"  # need gdb
     PYTEST_EXTRA_FLAGS: "--log-cli-level DEBUG"
@@ -902,15 +913,6 @@ example_test_007:
     - ESP32
     - Example_I2C_CCS811_SENSOR
 
-example_test_009:
-  extends: .example_test_esp32_template
-  tags:
-    - ESP32
-    - test_jtag_arm
-  variables:
-    SETUP_TOOLS: "1"
-    PYTHON_VER: 3
-
 example_test_011:
   extends: .example_test_esp32_template
   tags:
@@ -986,14 +988,6 @@ example_test_C6_GENERIC:
     - .test_app_template
     - .rules:test:custom_test-esp32s3
 
-test_app_test_001:
-  extends: .test_app_esp32_template
-  tags:
-    - ESP32
-    - test_jtag_arm
-  variables:
-    SETUP_TOOLS: "1"
-
 test_app_test_eth:
   extends: .test_app_esp32_template
   tags:

+ 6 - 1
conftest.py

@@ -109,7 +109,7 @@ ENV_MARKERS = {
     'MSPI_F8R8': 'runner with Octal Flash and Octal PSRAM',
     'MSPI_F4R8': 'runner with Quad Flash and Octal PSRAM',
     'MSPI_F4R4': 'runner with Quad Flash and Quad PSRAM',
-    'test_jtag_arm': 'runner where the chip is accessible through JTAG as well',
+    'jtag': 'runner where the chip is accessible through JTAG as well',
     'adc': 'ADC related tests should run on adc runners',
     'xtal32k': 'Runner with external 32k crystal connected',
     'no32kXtal': 'Runner with no external 32k crystal connected',
@@ -203,6 +203,11 @@ def get_target_marker_from_expr(markexpr: str) -> str:
 ############
 # Fixtures #
 ############
+@pytest.fixture(scope='session')
+def idf_path() -> str:
+    return os.path.dirname(__file__)
+
+
 @pytest.fixture(scope='session', autouse=True)
 def session_tempdir() -> str:
     _tmpdir = os.path.join(

+ 65 - 0
examples/storage/semihost_vfs/pytest_semihost_vfs.py

@@ -0,0 +1,65 @@
+# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Unlicense OR CC0-1.0
+
+import os
+import shutil
+import tempfile
+import typing as t
+from itertools import zip_longest
+
+import pytest
+from pytest_embedded_idf import IdfDut
+
+TEMP_DIR = tempfile.mkdtemp()
+HOST_FILE_NAME = 'host_file.txt'
+HOST_FILE_PATH = os.path.join(os.path.dirname(__file__), 'data', HOST_FILE_NAME)
+
+
+@pytest.fixture(autouse=True)
+def prepare() -> t.Generator[None, None, None]:
+    shutil.copyfile(HOST_FILE_PATH, os.path.join(TEMP_DIR, HOST_FILE_NAME))
+
+    yield
+
+    shutil.rmtree(TEMP_DIR, ignore_errors=True)
+
+
+@pytest.mark.esp32
+@pytest.mark.jtag
+@pytest.mark.parametrize(
+    'embedded_services, no_gdb, openocd_cli_args',
+    [
+        pytest.param(
+            'esp,idf,jtag',
+            'y',
+            f'-c \'set ESP_SEMIHOST_BASEDIR "{TEMP_DIR}"\' -f board/esp32-wrover-kit-3.3v.cfg',
+            marks=[pytest.mark.esp32],
+        ),
+    ],
+    indirect=True,
+)
+def test_semihost_vfs(dut: IdfDut) -> None:
+    dut.expect_exact('example: Switch to semihosted stdout')
+    dut.expect_exact('example: Switched back to UART stdout')
+    dut.expect_exact('example: Wrote 2798 bytes')
+    dut.expect_exact('====================== HOST DATA START =========================')
+
+    with open(HOST_FILE_PATH) as f:
+        for line in f:
+            if line.strip():
+                dut.expect_exact(line.strip())
+
+    dut.expect_exact('====================== HOST DATA END =========================')
+    dut.expect_exact('example: Read 6121 bytes')
+
+    with open(os.path.join(TEMP_DIR, 'esp32_stdout.txt')) as f:
+
+        def expected_content() -> t.Iterator[str]:
+            yield 'example: Switched to semihosted stdout'
+            for i in range(100):
+                yield 'Semihosted stdout write {}'.format(i)
+            yield 'example: Switch to UART stdout'
+
+        for actual, expected in zip_longest(f, expected_content(), fillvalue='-'):
+            if expected not in actual:  # "in" used because of the printed ASCII color codes
+                raise RuntimeError('"{}" != "{}"'.format(expected, actual.strip()))

+ 0 - 59
examples/storage/semihost_vfs/semihost_vfs_example_test.py

@@ -1,59 +0,0 @@
-import os
-import shutil
-import tempfile
-from io import open
-
-import ttfw_idf
-
-try:
-    from itertools import izip_longest as zip_longest
-except ImportError:
-    # Python 3
-    from itertools import zip_longest
-
-
-@ttfw_idf.idf_example_test(env_tag='test_jtag_arm')
-def test_examples_semihost_vfs(env, extra_data):
-
-    rel_project_path = os.path.join('examples', 'storage', 'semihost_vfs')
-    dut = env.get_dut('semihost_vfs', rel_project_path)
-    idf_path = dut.app.get_sdk_path()
-    proj_path = os.path.join(idf_path, rel_project_path)
-    host_file_name = 'host_file.txt'
-
-    try:
-        temp_dir = tempfile.mkdtemp()
-        host_file_path = os.path.join(proj_path, 'data', host_file_name)
-        shutil.copyfile(host_file_path, os.path.join(temp_dir, host_file_name))
-        cfg_cmds = ['set ESP_SEMIHOST_BASEDIR "{}"'.format(temp_dir)]
-
-        with ttfw_idf.OCDBackend(os.path.join(proj_path, 'openocd.log'), dut.app.target, cfg_cmds=cfg_cmds):
-            dut.start_app()
-            dut.expect_all('example: Switch to semihosted stdout',
-                           'example: Switched back to UART stdout',
-                           'example: Wrote 2798 bytes',
-                           '====================== HOST DATA START =========================',
-                           timeout=20)
-            with open(host_file_path) as f:
-                file_content = [line.strip() for line in f]
-            dut.expect_all(*file_content, timeout=20)
-            dut.expect_all('====================== HOST DATA END =========================',
-                           'example: Read 6121 bytes',
-                           timeout=5)
-
-        with open(os.path.join(temp_dir, 'esp32_stdout.txt')) as f:
-            def expected_content():
-                yield 'example: Switched to semihosted stdout'
-                for i in range(100):
-                    yield 'Semihosted stdout write {}'.format(i)
-                yield 'example: Switch to UART stdout'
-
-            for actual, expected in zip_longest(f, expected_content(), fillvalue='-'):
-                if expected not in actual:  # "in" used because of the printed ASCII color codes
-                    raise RuntimeError('"{}" != "{}"'.format(expected, actual.strip()))
-    finally:
-        shutil.rmtree(temp_dir, ignore_errors=True)
-
-
-if __name__ == '__main__':
-    test_examples_semihost_vfs()

+ 0 - 53
examples/system/app_trace_to_host/example_test.py

@@ -1,53 +0,0 @@
-from __future__ import unicode_literals
-
-import os
-import re
-
-import ttfw_idf
-
-
-@ttfw_idf.idf_example_test(env_tag='test_jtag_arm')
-def test_examples_app_trace_to_host(env, extra_data):
-    rel_project_path = os.path.join('examples', 'system', 'app_trace_to_host')
-    dut = env.get_dut('app_trace_to_host', rel_project_path)
-    idf_path = dut.app.get_sdk_path()
-    proj_path = os.path.join(idf_path, rel_project_path)
-    oocd_log_path = os.path.join(proj_path, 'openocd.log')
-
-    with ttfw_idf.OCDBackend(oocd_log_path, dut.app.target) as ocd:
-        dut.start_app()
-        dut.expect_all('example: Enabling ADC1 on channel 6 / GPIO34.',
-                       'example: Enabling CW generator on DAC channel 0 / GPIO25',
-                       'example: Sampling ADC and sending data to the host...',
-                       re.compile(r'example: Collected \d+ samples in 20 ms.'),
-                       'example: Sampling ADC and sending data to the UART...',
-                       re.compile(r'example: Sample:\d, Value:\d+'),
-                       re.compile(r'example: Collected \d+ samples in 20 ms.'),
-                       timeout=20)
-
-        ocd.apptrace_start('file://adc.log 0 9000 5 0 0')
-        ocd.apptrace_wait_stop(tmo=30)
-
-    with open(oocd_log_path) as oocd_log:
-        cores = 1 if dut.app.get_sdkconfig().get('CONFIG_FREERTOS_UNICORE', '').replace('"','') == 'y' else 2
-        params_str = 'App trace params: from {} cores'.format(cores)
-        for line in oocd_log:
-            if params_str in line:
-                break
-        else:
-            raise RuntimeError('"{}" could not be found in {}'.format(params_str, oocd_log_path))
-
-    with ttfw_idf.CustomProcess(' '.join([os.path.join(idf_path, 'tools/esp_app_trace/logtrace_proc.py'),
-                                          'adc.log',
-                                          os.path.join(dut.app.binary_path, 'app_trace_to_host.elf')]),
-                                logfile='logtrace_proc.log') as logtrace:
-        logtrace.pexpect_proc.expect_exact('Parse trace file')
-        logtrace.pexpect_proc.expect_exact('Parsing completed.')
-        logtrace.pexpect_proc.expect_exact('====================================================================')
-        logtrace.pexpect_proc.expect(re.compile(r'example: Sample:\d+, Value:\d+'))
-        logtrace.pexpect_proc.expect_exact('====================================================================')
-        logtrace.pexpect_proc.expect(re.compile(r'Log records count: \d+'))
-
-
-if __name__ == '__main__':
-    test_examples_app_trace_to_host()

+ 75 - 0
examples/system/app_trace_to_host/pytest_app_trace_to_host.py

@@ -0,0 +1,75 @@
+# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Unlicense OR CC0-1.0
+import os
+import re
+import time
+
+import pexpect
+import pytest
+from pytest_embedded.log import DuplicateStdoutPopen, MessageQueue
+from pytest_embedded_idf import IdfDut
+from pytest_embedded_jtag import OpenOcd
+
+
+def apptrace_wait_stop(openocd: OpenOcd, timeout: int = 30) -> None:
+    stopped = False
+    end_before = time.time() + timeout
+    while not stopped:
+        cmd_out = openocd.write('esp apptrace status')
+        for line in cmd_out.splitlines():
+            if line.startswith('Tracing is STOPPED.'):
+                stopped = True
+                break
+        if not stopped and time.time() > end_before:
+            raise pexpect.TIMEOUT('Failed to wait for apptrace stop!')
+
+        time.sleep(1)
+
+
+@pytest.mark.esp32
+@pytest.mark.jtag
+@pytest.mark.parametrize(
+    'embedded_services, no_gdb',
+    [
+        ('esp,idf,jtag', 'y'),
+    ],
+    indirect=True,
+)
+def test_examples_app_trace_to_host(msg_queue: MessageQueue, dut: IdfDut, openocd: OpenOcd, idf_path: str) -> None:
+    dut.expect_exact('example: Enabling ADC1 on channel 6 / GPIO34.')
+    dut.expect_exact('example: Enabling CW generator on DAC channel 0 / GPIO25')
+    dut.expect_exact('example: Sampling ADC and sending data to the host...')
+    dut.expect(r'example: Collected \d+ samples in 20 ms.')
+    dut.expect_exact('example: Sampling ADC and sending data to the UART...')
+    dut.expect(r'example: Sample:\d, Value:\d+')
+    dut.expect(r'example: Collected \d+ samples in 20 ms.')
+
+    assert 'Targets connected.' in dut.openocd.write('esp apptrace start file://adc.log 0 9000 5 0 0')
+    apptrace_wait_stop(dut.openocd)
+
+    with open(openocd._logfile) as oocd_log:  # pylint: disable=protected-access
+        cores = 1 if dut.app.sdkconfig.get('CONFIG_FREERTOS_UNICORE') == 'y' else 2
+        params_str = 'App trace params: from {} cores'.format(cores)
+        for line in oocd_log:
+            if params_str in line:
+                break
+        else:
+            raise RuntimeError(
+                '"{}" could not be found in {}'.format(params_str, openocd._logfile)  # pylint: disable=protected-access
+            )
+
+    DuplicateStdoutPopen(
+        msg_queue,
+        [
+            os.path.join(idf_path, 'tools', 'esp_app_trace', 'logtrace_proc.py'),
+            'adc.log',
+            os.path.join(dut.app.elf_file),
+        ],
+    )
+
+    dut.expect_exact('Parse trace file')
+    dut.expect_exact('Parsing completed.')
+    dut.expect_exact('====================================================================')
+    dut.expect(re.compile(rb'example: Sample:\d+, Value:\d+'))
+    dut.expect_exact('====================================================================')
+    dut.expect(re.compile(rb'Log records count: \d+'))

+ 0 - 66
examples/system/gcov/example_test.py

@@ -1,66 +0,0 @@
-from __future__ import unicode_literals
-
-import os
-import time
-
-import ttfw_idf
-from ttfw_idf import Utility
-
-
-@ttfw_idf.idf_example_test(env_tag='test_jtag_arm')
-def test_examples_gcov(env, extra_data):
-
-    rel_project_path = os.path.join('examples', 'system', 'gcov')
-    dut = env.get_dut('gcov', rel_project_path)
-    idf_path = dut.app.get_sdk_path()
-    proj_path = os.path.join(idf_path, rel_project_path)
-    openocd_cmd_log = os.path.join(proj_path, 'openocd_cmd.log')
-
-    with ttfw_idf.OCDBackend(os.path.join(proj_path, 'openocd.log'), dut.app.target) as oocd:
-        dut.start_app()
-
-        def expect_counter_output(loop, timeout=10):
-            dut.expect_all('blink_dummy_func: Counter = {}'.format(loop),
-                           'some_dummy_func: Counter = {}'.format(loop * 2),
-                           timeout=timeout)
-
-        expect_counter_output(0, timeout=20)
-        dut.expect('Ready to dump GCOV data...', timeout=5)
-
-        def dump_coverage(cmd):
-            try:
-                response = oocd.cmd_exec(cmd)
-                with open(openocd_cmd_log, 'a') as f:
-                    f.write(response)
-
-                assert all(x in response for x in ['Targets connected.',
-                                                   'gcov_example_main.c.gcda',
-                                                   'gcov_example_func.c.gcda',
-                                                   'some_funcs.c.gcda',
-                                                   'Targets disconnected.',
-                                                   ])
-
-            except AssertionError:
-                # Print what is happening with DUT. Limit the size if it is in loop and generating output.
-                Utility.console_log(dut.read(size=1000))
-                raise
-
-        # Test two hard-coded dumps
-        dump_coverage('esp gcov dump')
-        dut.expect('GCOV data have been dumped.', timeout=5)
-        expect_counter_output(1)
-        dut.expect('Ready to dump GCOV data...', timeout=5)
-        dump_coverage('esp gcov dump')
-        dut.expect('GCOV data have been dumped.', timeout=5)
-
-        for i in range(2, 6):
-            expect_counter_output(i)
-
-        for _ in range(3):
-            time.sleep(1)
-            # Test instant run-time dump
-            dump_coverage('esp gcov')
-
-
-if __name__ == '__main__':
-    test_examples_gcov()

+ 72 - 0
examples/system/gcov/pytest_gcov.py

@@ -0,0 +1,72 @@
+# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Unlicense OR CC0-1.0
+import os.path
+import time
+
+import pytest
+from pytest_embedded_idf import IdfDut
+from pytest_embedded_jtag import OpenOcd
+
+
+@pytest.mark.esp32
+@pytest.mark.jtag
+@pytest.mark.parametrize(
+    'embedded_services, no_gdb',
+    [
+        ('esp,idf,jtag', 'y'),
+    ],
+    indirect=True,
+)
+def test_gcov(dut: IdfDut, openocd: OpenOcd) -> None:
+    # create the generated .gcda folder, otherwise would have error: failed to open file.
+    # normally this folder would be created via `idf.py build`. but in CI the non-related files would not be preserved
+    os.makedirs(os.path.join(dut.app.binary_path, 'esp-idf', 'main', 'CMakeFiles', '__idf_main.dir'), exist_ok=True)
+    os.makedirs(os.path.join(dut.app.binary_path, 'esp-idf', 'sample', 'CMakeFiles', '__idf_sample.dir'), exist_ok=True)
+
+    def expect_counter_output(loop: int, timeout: int = 10) -> None:
+        dut.expect_exact(
+            [f'blink_dummy_func: Counter = {loop}', f'some_dummy_func: Counter = {loop * 2}'],
+            expect_all=True,
+            timeout=timeout,
+        )
+
+    expect_counter_output(0)
+    dut.expect('Ready to dump GCOV data...', timeout=5)
+
+    def dump_coverage(cmd: str) -> None:
+        response = openocd.write(cmd)
+
+        expect_lines = [
+            'Targets connected.',
+            'gcov_example_main.c.gcda',
+            'gcov_example_func.c.gcda',
+            'some_funcs.c.gcda',
+            'Targets disconnected.',
+        ]
+
+        for line in response.splitlines():
+            for expect in expect_lines[:]:
+                if expect in line:
+                    if expect.endswith('.gcda'):  # check file exists
+                        file_path = line.split()[-1].strip("'")
+                        assert os.path.isfile(file_path)
+
+                    expect_lines.remove(expect)
+
+        assert len(expect_lines) == 0
+
+    # Test two hard-coded dumps
+    dump_coverage('esp gcov dump')
+    dut.expect('GCOV data have been dumped.', timeout=5)
+    expect_counter_output(1)
+    dut.expect('Ready to dump GCOV data...', timeout=5)
+    dump_coverage('esp gcov dump')
+    dut.expect('GCOV data have been dumped.', timeout=5)
+
+    for i in range(2, 6):
+        expect_counter_output(i)
+
+    for _ in range(3):
+        time.sleep(1)
+        # Test instant run-time dump
+        dump_coverage('esp gcov')

+ 0 - 66
examples/system/sysview_tracing/example_test.py

@@ -1,66 +0,0 @@
-from __future__ import unicode_literals
-
-import os
-import re
-import tempfile
-import time
-from io import open
-
-import debug_backend
-import ttfw_idf
-
-
-@ttfw_idf.idf_example_test(env_tag='test_jtag_arm')
-def test_examples_sysview_tracing(env, extra_data):
-
-    rel_project_path = os.path.join('examples', 'system', 'sysview_tracing')
-    dut = env.get_dut('sysview_tracing', rel_project_path)
-    proj_path = os.path.join(dut.app.idf_path, rel_project_path)
-    elf_path = os.path.join(dut.app.binary_path, 'sysview_tracing.elf')
-
-    def get_temp_file():
-        with tempfile.NamedTemporaryFile(delete=False) as f:
-            return f.name
-
-    try:
-        tempfiles = [get_temp_file(), get_temp_file()]
-
-        with open(os.path.join(proj_path, 'gdbinit')) as f_in, open(tempfiles[0], 'w') as f_out:
-            new_content = f_in.read()
-            # localhost connection issue occurs in docker unless:
-            new_content = new_content.replace(':3333', '127.0.0.1:3333', 1)
-            new_content = new_content.replace('file:///tmp/sysview_example.svdat', 'file://{}'.format(tempfiles[1]), 1)
-            f_out.write(new_content)
-
-        with ttfw_idf.OCDBackend(os.path.join(proj_path, 'openocd.log'), dut.app.target) as oocd:
-            dut.start_app()
-
-            def dut_expect_task_event():
-                dut.expect(re.compile(r'example: Task\[0x3[0-9A-Fa-f]+\]: received event \d+'), timeout=30)
-
-            dut_expect_task_event()
-
-            gdb_log = os.path.join(proj_path, 'gdb.log')
-            gdb_workdir = os.path.join(proj_path, 'main')
-            with ttfw_idf.GDBBackend(gdb_log, elf_path, dut.app.target, tempfiles[0], gdb_workdir) as p:
-                p.gdb.wait_target_state(debug_backend.TARGET_STATE_RUNNING)
-                stop_reason = p.gdb.wait_target_state(debug_backend.TARGET_STATE_STOPPED)
-                assert stop_reason == debug_backend.TARGET_STOP_REASON_BP, 'STOP reason: {}'.format(stop_reason)
-
-                dut.expect('example: Created task')  # dut has been restarted by gdb since the last dut.expect()
-                dut_expect_task_event()
-
-                # Do a sleep while sysview samples are captured.
-                time.sleep(3)
-                # GDBMI isn't responding now to any commands, therefore, the following command is issued to openocd
-                oocd.cmd_exec('esp sysview stop')
-    finally:
-        for x in tempfiles:
-            try:
-                os.unlink(x)
-            except Exception:
-                pass
-
-
-if __name__ == '__main__':
-    test_examples_sysview_tracing()

+ 47 - 0
examples/system/sysview_tracing/pytest_sysview_tracing.py

@@ -0,0 +1,47 @@
+# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Unlicense OR CC0-1.0
+import re
+import time
+
+import pexpect.fdpexpect
+import pytest
+from pytest_embedded_idf import IdfDut
+
+
+@pytest.mark.esp32
+@pytest.mark.jtag
+@pytest.mark.parametrize(
+    'embedded_services',
+    [
+        'esp,idf,jtag',
+    ],
+    indirect=True,
+)
+def test_examples_sysview_tracing(dut: IdfDut) -> None:
+    def dut_expect_task_event() -> None:
+        dut.expect(re.compile(rb'example: Task\[0x3[0-9A-Fa-f]+\]: received event \d+'), timeout=30)
+
+    dut.gdb.write('mon reset halt')
+    dut.gdb.write('flushregs')
+    dut.gdb.write('b app_main')
+
+    dut.gdb.write('commands', non_blocking=True)
+    dut.gdb.write(
+        'mon esp sysview start file:///tmp/sysview_example0.svdat file:///tmp/sysview_example1.svdat', non_blocking=True
+    )
+    dut.gdb.write('c', non_blocking=True)
+    dut.gdb.write('end')
+
+    dut.gdb.write('c', non_blocking=True)
+    time.sleep(1)  # to avoid EOF file error
+    with open(dut.gdb._logfile) as fr:  # pylint: disable=protected-access
+        gdb_pexpect_proc = pexpect.fdpexpect.fdspawn(fr.fileno())
+        gdb_pexpect_proc.expect('Thread 2 "main" hit Breakpoint 1, app_main ()')
+
+        dut.expect('example: Created task')  # dut has been restarted by gdb since the last dut.expect()
+        dut_expect_task_event()
+
+        # Do a sleep while sysview samples are captured.
+        time.sleep(3)
+        # GDB isn't responding now to any commands, therefore, the following command is issued to openocd
+        dut.openocd.write('esp sysview stop')

+ 0 - 64
examples/system/sysview_tracing_heap_log/example_test.py

@@ -1,64 +0,0 @@
-from __future__ import unicode_literals
-
-import os
-import re
-import tempfile
-from io import open
-
-import debug_backend
-import ttfw_idf
-
-
-@ttfw_idf.idf_example_test(env_tag='test_jtag_arm')
-def test_examples_sysview_tracing_heap_log(env, extra_data):
-
-    rel_project_path = os.path.join('examples', 'system', 'sysview_tracing_heap_log')
-    dut = env.get_dut('sysview_tracing_heap_log', rel_project_path)
-    proj_path = os.path.join(dut.app.idf_path, rel_project_path)
-    elf_path = os.path.join(dut.app.binary_path, 'sysview_tracing_heap_log.elf')
-
-    def get_temp_file():
-        with tempfile.NamedTemporaryFile(delete=False) as f:
-            return f.name
-
-    try:
-        tempfiles = [get_temp_file(), get_temp_file()]
-
-        with open(os.path.join(proj_path, 'gdbinit')) as f_in, open(tempfiles[0], 'w') as f_out:
-            new_content = f_in.read()
-            # localhost connection issue occurs in docker unless:
-            new_content = new_content.replace(':3333', '127.0.0.1:3333', 1)
-            new_content = new_content.replace('file:///tmp/heap_log.svdat', 'file://{}'.format(tempfiles[1]), 1)
-            f_out.write(new_content)
-
-        with ttfw_idf.OCDBackend(os.path.join(proj_path, 'openocd.log'), dut.app.target):
-            dut.start_app()
-            dut.expect('esp_apptrace: Initialized TRAX on CPU0')
-
-            gdb_log = os.path.join(proj_path, 'gdb.log')
-            gdb_workdir = os.path.join(proj_path, 'main')
-            with ttfw_idf.GDBBackend(gdb_log, elf_path, dut.app.target, tempfiles[0], gdb_workdir) as p:
-                for _ in range(2):  # There are two breakpoints
-                    p.gdb.wait_target_state(debug_backend.TARGET_STATE_RUNNING)
-                    stop_reason = p.gdb.wait_target_state(debug_backend.TARGET_STATE_STOPPED)
-                    assert stop_reason == debug_backend.TARGET_STOP_REASON_BP, 'STOP reason: {}'.format(stop_reason)
-
-                # dut has been restarted by gdb since the last dut.expect()
-                dut.expect('esp_apptrace: Initialized TRAX on CPU0')
-
-        with ttfw_idf.CustomProcess(' '.join([os.path.join(dut.app.idf_path, 'tools/esp_app_trace/sysviewtrace_proc.py'),
-                                              '-p',
-                                              '-b', elf_path,
-                                              tempfiles[1]]),
-                                    logfile='sysviewtrace_proc.log') as sysviewtrace:
-            sysviewtrace.pexpect_proc.expect(re.compile(r'Found \d+ leaked bytes in \d+ blocks.'), timeout=120)
-    finally:
-        for x in tempfiles:
-            try:
-                os.unlink(x)
-            except Exception:
-                pass
-
-
-if __name__ == '__main__':
-    test_examples_sysview_tracing_heap_log()

+ 47 - 0
examples/system/sysview_tracing_heap_log/pytest_sysview_tracing_heap_log.py

@@ -0,0 +1,47 @@
+# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Unlicense OR CC0-1.0
+import os.path
+import time
+
+import pexpect.fdpexpect
+import pytest
+from pytest_embedded_idf import IdfDut
+
+TEMP_FILE = os.path.join(os.path.dirname(__file__), 'heap_log.svdat')
+
+
+@pytest.mark.esp32
+@pytest.mark.jtag
+@pytest.mark.parametrize('embedded_services', [
+    'esp,idf,jtag',
+], indirect=True)
+def test_examples_sysview_tracing_heap_log(idf_path: str, dut: IdfDut) -> None:
+    dut.gdb.write('mon reset halt')
+    dut.gdb.write('flushregs')
+
+    dut.gdb.write('tb heap_trace_start')
+    dut.gdb.write('commands', non_blocking=True)
+    dut.gdb.write(f'mon esp sysview start file://{TEMP_FILE}', non_blocking=True)
+    dut.gdb.write('c', non_blocking=True)
+    dut.gdb.write('end')
+
+    dut.gdb.write('tb heap_trace_stop')
+    dut.gdb.write('commands', non_blocking=True)
+    dut.gdb.write('mon esp sysview stop', non_blocking=True)
+    dut.gdb.write('end')
+
+    dut.gdb.write('c', non_blocking=True)
+    dut.expect('esp_apptrace: Initialized TRAX on CPU0')
+
+    time.sleep(1)  # make sure that the sysview file has been generated
+    with pexpect.spawn(' '.join([os.path.join(idf_path, 'tools', 'esp_app_trace', 'sysviewtrace_proc.py'),
+                                 '-p',
+                                 '-b', dut.app.elf_file,
+                                 TEMP_FILE])) as sysviewtrace:
+        sysviewtrace.expect(r'Found \d+ leaked bytes in \d+ blocks.', timeout=120)
+
+    with open(dut.gdb._logfile) as fr:  # pylint: disable=protected-access
+        gdb_pexpect_proc = pexpect.fdpexpect.fdspawn(fr.fileno())
+        gdb_pexpect_proc.expect_exact(
+            'Thread 2 "main" hit Temporary breakpoint 1, heap_trace_start (mode_param=HEAP_TRACE_ALL)', timeout=10)
+        gdb_pexpect_proc.expect_exact('Thread 2 "main" hit Temporary breakpoint 2, heap_trace_stop ()', timeout=10)

+ 2 - 2
tools/test_apps/system/gdb/pytest_gdb.py

@@ -18,8 +18,8 @@ except ModuleNotFoundError:
 
 
 @pytest.mark.supported_targets
-@pytest.mark.test_jtag_arm
-def test_idf_gdb(dut: Dut) -> None:
+@pytest.mark.jtag
+def test_idf_gdb(dut: IdfDut) -> None:
     # Need to wait a moment to connect via OpenOCD after the hard reset happened.
     # Along with this check that app runs ok
     dut.expect('Hello world!')

+ 1 - 1
tools/test_apps/system/gdb_loadable_elf/pytest_gdb_loadable_elf.py

@@ -13,7 +13,7 @@ from pytest_embedded_idf import IdfDut
 @pytest.mark.parametrize('embedded_services, skip_autoflash, erase_all', [
     ('esp,idf,jtag', 'y', 'y'),
 ], indirect=True)
-@pytest.mark.test_jtag_arm
+@pytest.mark.jtag
 def test_loadable_elf(dut: IdfDut, offset: str) -> None:
     dut.gdb.write('mon reset halt')
     dut.gdb.write(f'thb *{offset}')