zephyr/tests/subsys/debug/gdbstub/pytest/test_gdbstub.py
Dmitrii Golovanov 52442a7c49 tests: gdbstub: Fix incomplete code coverage dump
When the test runs with code coverage, make sure the coverage dump
completes its output under pytest execution before the test debug
session ends in both cases: for Zephyr gdbstub feature and for
Qemu internal GDB stub.

Add few more test operations with breakpoints.

Signed-off-by: Dmitrii Golovanov <dmitrii.golovanov@intel.com>
2023-12-06 17:52:18 +00:00

94 lines
3.3 KiB
Python
Executable File

# Copyright (c) 2023 Intel Corporation.
#
# SPDX-License-Identifier: Apache-2.0
import os
import subprocess
import sys
import logging
import shlex
import re
import pytest
from twister_harness import DeviceAdapter
ZEPHYR_BASE = os.getenv("ZEPHYR_BASE")
sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts", "pylib", "twister"))
from twisterlib.cmakecache import CMakeCache
logger = logging.getLogger(__name__)
@pytest.fixture()
def gdb_process(dut: DeviceAdapter, gdb_script, gdb_timeout, gdb_target_remote) -> subprocess.CompletedProcess:
build_dir = dut.device_config.build_dir
cmake_cache = CMakeCache.from_file(os.path.join(build_dir, 'CMakeCache.txt'))
gdb_exec = cmake_cache.get('CMAKE_GDB', None)
assert gdb_exec
source_dir = cmake_cache.get('APPLICATION_SOURCE_DIR', None)
assert source_dir
build_image = cmake_cache.get('BYPRODUCT_KERNEL_ELF_NAME', None)
assert build_image
gdb_log_file = os.path.join(build_dir, 'gdb.log')
cmd = [gdb_exec, '-batch',
'-ex', f'set pagination off',
'-ex', f'set trace-commands on',
'-ex', f'set logging file {gdb_log_file}',
'-ex', f'set logging enabled on',
'-ex', f'target remote {gdb_target_remote}',
'-x', f'{source_dir}/{gdb_script}', build_image]
logger.info(f'Run GDB: {shlex.join(cmd)}')
result = subprocess.run(cmd, capture_output=True, text=True, timeout=gdb_timeout)
logger.info(f'GDB ends rc={result.returncode}')
return result
#
@pytest.fixture(scope="module")
def expected_app():
return [
re.compile(r"Booting from ROM"),
re.compile(r"Booting Zephyr OS build"),
re.compile(r"main\(\):enter"),
]
@pytest.fixture(scope="module")
def expected_gdb():
return [
re.compile(r'Breakpoint 1 at 0x'),
re.compile(r'Breakpoint 2 at 0x'),
re.compile(r'Breakpoint 1, test '),
re.compile(r'Breakpoint 2, main '),
re.compile(r'GDB:PASSED'),
re.compile(r'Breakpoint 3, k_thread_abort '),
re.compile(r'2 .* breakpoint .* in main '),
]
@pytest.fixture(scope="module")
def unexpected_gdb():
return [
re.compile(r'breakpoint .* in test '),
re.compile(r'breakpoint .* in k_thread_abort '),
]
@pytest.fixture(scope="module")
def expected_gdb_detach():
return [
re.compile(r'Inferior.*will be killed'), # Zephyr gdbstub
re.compile(r'Inferior.*detached') # QEMU gdbstub
]
def test_gdbstub(dut: DeviceAdapter, gdb_process, expected_app, expected_gdb, expected_gdb_detach, unexpected_gdb):
"""
Test gdbstub feature using a GDB script. We connect to the DUT, run the
GDB script then evaluate return code and expected patterns at the GDB
and Test Applicaiton outputs.
"""
logger.debug(f"GDB output:\n{gdb_process.stdout}\n")
assert gdb_process.returncode == 0
assert all([ex_re.search(gdb_process.stdout, re.MULTILINE) for ex_re in expected_gdb]), 'No expected GDB output'
assert not any([ex_re.search(gdb_process.stdout, re.MULTILINE) for ex_re in unexpected_gdb]), 'Unexpected GDB output'
assert any([ex_re.search(gdb_process.stdout, re.MULTILINE) for ex_re in expected_gdb_detach]), 'No expected GDB quit'
app_output = '\n'.join(dut.readlines(print_output = False))
logger.debug(f"App output:\n{app_output}\n")
assert all([ex_re.search(app_output, re.MULTILINE) for ex_re in expected_app]), 'No expected Application output'
#