Commit cbbb1cab authored by Dmitrii Golovanov's avatar Dmitrii Golovanov Committed by Carles Cufi
Browse files

tests: gdbstub: Improve test case



Gdbstub test improvements: using pytest fixtures, parametrization, and
expected pattern matching on outputs from GDB and the test application.

Signed-off-by: default avatarDmitrii Golovanov <dmitrii.golovanov@intel.com>
parent c9e651b1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
CONFIG_GDBSTUB=y
CONFIG_GDBSTUB_SERIAL_BACKEND=y
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_USERSPACE=y
CONFIG_KOBJECT_TEXT_AREA=4096
+20 −0
Original line number Diff line number Diff line
#
# Copyright (c) 2023 intel Corporation.
#
# SPDX-License-Identifier: Apache-2.0
#

import pytest

def pytest_addoption(parser):
    parser.addoption('--gdb_timeout')
    parser.addoption('--gdb_script')

@pytest.fixture()
def gdb_script(request):
    return request.config.getoption('--gdb_script')

@pytest.fixture()
def gdb_timeout(request):
    return int(request.config.getoption('--gdb_timeout', default=60))
#
+51 −15
Original line number Diff line number Diff line
@@ -4,10 +4,12 @@

import os
import subprocess
from twister_harness import DeviceAdapter
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"))
@@ -15,20 +17,54 @@ from twisterlib.cmakecache import CMakeCache

logger = logging.getLogger(__name__)


def test_gdbstub(dut: DeviceAdapter):
    """
    Test gdbstub feature using a gdb script. We connect to the DUT and run some
    basic gdb commands and evaluate return code to determine pass or failure.
    """
@pytest.fixture()
def gdb_process(dut: DeviceAdapter, gdb_script, gdb_timeout):
    build_dir = dut.device_config.build_dir
    cmake_cache = CMakeCache.from_file(build_dir / 'CMakeCache.txt')
    gdb = cmake_cache.get('CMAKE_GDB', None)
    assert gdb
    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
    cmd = [gdb, '-x', f'{source_dir}/run.gdbinit', f'{build_dir}/zephyr/zephyr.elf']
    logger.info(f'Test command: {shlex.join(cmd)}')
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=20)
    logger.debug('Output:\n%s' % result.stdout)
    assert result.returncode == 0
    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 logging file {gdb_log_file}',
           '-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'),
    ]

def test_gdbstub(dut: DeviceAdapter, gdb_process, expected_app, expected_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 'Inferior 1 [Remote target] will be killed' in gdb_process.stdout,'Expecting explicit quit from the GDB script and kill QEMU test app.'
    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'
#
+2 −9
Original line number Diff line number Diff line
@@ -20,19 +20,12 @@ static int test(void)
	return a + b;
}

static void thread_entry(void *p1, void *p2, void *p3)
{
	printk("Hello from user thread!\n");
}

int main(void)
{
	int ret;

	printk("%s():enter\n", __func__);
	ret = test();
	printk("%d\n", ret);
	printk("ret=%d\n", ret);
	return 0;
}

K_THREAD_DEFINE(thread, STACKSIZE, thread_entry, NULL, NULL, NULL,
		7, K_USER, 0);
+23 −0
Original line number Diff line number Diff line
set pagination off
#symbol-file build/zephyr/zephyr.elf
set trace-commands on
set logging enabled on

target remote :5678

b test
b main.c:33
b main.c:29
c

# break at test()
s
set var a = 2
c

# break at main()
if ret == 6
	printf "PASSED\n"
	printf "GDB:PASSED\n"
	quit 0
else
	printf "FAILED\n"
	printf "GDB:FAILED\n"
	quit 1
end
Loading