@@ -9,20 +9,23 @@ import os
import re
import shutil
import tempfile
+import time
import glob
import fnmatch
import unittest
import json
import logging
+import shlex
from oeqa.selftest.case import OESelftestTestCase
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
+from oeqa.utils.commands import runCmd, Command, bitbake, get_bb_var, create_temp_layer
from oeqa.utils.commands import get_bb_vars, runqemu, runqemu_check_taps, get_test_layer
from oeqa.core.decorator import OETestTag
from bb.utils import mkdirhier, edit_bblayers_conf
oldmetapath = None
+
def setUpModule():
global templayerdir
templayerdir = tempfile.mkdtemp(prefix='devtoolqa')
@@ -2523,8 +2526,23 @@ class DevtoolUpgradeTests(DevtoolBase):
runCmd("grep %s %s" % (modconfopt, codeconfigfile))
+
+class RunCmdBackground:
+ """Context manager to manage a background subprocess"""
+ def __init__(self, command, output_log=None, **options):
+ self.cmd = Command(command, bg=True, output_log=output_log, **options)
+
+ def __enter__(self):
+ self.cmd.run()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.cmd.stop()
+
+
class DevtoolIdeSdkTests(DevtoolBase):
+ MAGIC_STRING_ORIG = "Magic: 123456789"
+
def setUp(self):
super().setUp()
self._cmd_logger = None
@@ -2642,7 +2660,6 @@ class DevtoolIdeSdkTests(DevtoolBase):
'%s script not found' % install_deploy_cmd)
runCmd(install_deploy_cmd, output_log=self._cmd_logger)
- MAGIC_STRING_ORIG = "Magic: 123456789"
MAGIC_STRING_NEW = "Magic: 987654321"
ptest_cmd = "ptest-runner " + recipe_name
@@ -2655,7 +2672,7 @@ class DevtoolIdeSdkTests(DevtoolBase):
status, output = qemu.run(example_exe)
self.assertEqual(status, 0, msg="%s failed: %s" %
(example_exe, output))
- self.assertIn(MAGIC_STRING_ORIG, output)
+ self.assertIn(DevtoolIdeSdkTests.MAGIC_STRING_ORIG, output)
# Verify the unmodified ptests work
status, output = qemu.run(ptest_cmd)
@@ -2664,13 +2681,13 @@ class DevtoolIdeSdkTests(DevtoolBase):
# Verify remote debugging works
self._gdb_cross_debugging_multi(
- qemu, recipe_name, example_exe, MAGIC_STRING_ORIG)
+ qemu, recipe_name, example_exe, DevtoolIdeSdkTests.MAGIC_STRING_ORIG)
# Replace the Magic String in the code, compile and deploy to Qemu
cpp_example_lib_hpp = os.path.join(tempdir, 'cpp-example-lib.hpp')
with open(cpp_example_lib_hpp, 'r') as file:
cpp_code = file.read()
- cpp_code = cpp_code.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW)
+ cpp_code = cpp_code.replace(DevtoolIdeSdkTests.MAGIC_STRING_ORIG, MAGIC_STRING_NEW)
with open(cpp_example_lib_hpp, 'w') as file:
file.write(cpp_code)
runCmd(install_deploy_cmd, cwd=tempdir, output_log=self._cmd_logger)
@@ -2679,7 +2696,7 @@ class DevtoolIdeSdkTests(DevtoolBase):
status, output = qemu.run(example_exe)
self.assertEqual(status, 0, msg="%s failed: %s" %
(example_exe, output))
- self.assertNotIn(MAGIC_STRING_ORIG, output)
+ self.assertNotIn(DevtoolIdeSdkTests.MAGIC_STRING_ORIG, output)
self.assertIn(MAGIC_STRING_NEW, output)
# Verify the modified example ptests work
@@ -2704,6 +2721,24 @@ class DevtoolIdeSdkTests(DevtoolBase):
self.assertEqual(r.status, 0)
self.assertIn("GNU gdb", r.output)
+ def _gdb_debug_cpp_example(self, magic_string, gdb_start_cmd="run"):
+ """Get a series of gdb commands to debug the cpp-example-lib example"""
+ gdb_batch_cmd = " -ex 'break main' -ex '%s'" % gdb_start_cmd
+ gdb_batch_cmd += " -ex 'break CppExample::print_json()' -ex 'continue'"
+ gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %s\")'" % magic_string
+ gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %saaa\")'" % magic_string
+ gdb_batch_cmd += " -ex 'list cpp-example-lib.hpp:14,14'"
+ gdb_batch_cmd += " -ex 'continue'"
+ return gdb_batch_cmd
+
+ def _gdb_debug_cpp_example_check(self, gdb_output, magic_string):
+ self.assertIn("Breakpoint 1, main", gdb_output)
+ self.assertIn("$1 = 0", gdb_output) # test.string.compare equal
+ self.assertIn("$2 = -3", gdb_output) # test.string.compare longer
+ self.assertIn(
+ 'inline static const std::string test_string = "cpp-example-lib %s";' % magic_string, gdb_output)
+ self.assertIn("exited normally", gdb_output)
+
def _gdb_cross_debugging_multi(self, qemu, recipe_name, example_exe, magic_string):
"""Verify gdb-cross is working
@@ -2744,24 +2779,12 @@ class DevtoolIdeSdkTests(DevtoolBase):
self.assertIn("gdbserver", r.output)
# Test remote debugging works
- gdb_batch_cmd = " --batch -ex 'break main' -ex 'run'"
- gdb_batch_cmd += " -ex 'break CppExample::print_json()' -ex 'continue'"
- gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %s\")'" % magic_string
- gdb_batch_cmd += " -ex 'print CppExample::test_string.compare(\"cpp-example-lib %saaa\")'" % magic_string
- gdb_batch_cmd += " -ex 'list cpp-example-lib.hpp:14,14'"
- gdb_batch_cmd += " -ex 'continue'"
- return gdb_batch_cmd
-
+ gdb_batch_cmd = " --batch " + self._gdb_debug_cpp_example(magic_string)
r = runCmd(gdb_script + gdb_batch_cmd, output_log=self._cmd_logger)
self.logger.debug("%s %s returned: %s", gdb_script,
gdb_batch_cmd, r.output)
self.assertEqual(r.status, 0)
- self.assertIn("Breakpoint 1, main", r.output)
- self.assertIn("$1 = 0", r.output) # test.string.compare equal
- self.assertIn("$2 = -3", r.output) # test.string.compare longer
- self.assertIn(
- 'inline static const std::string test_string = "cpp-example-lib %s";' % magic_string, r.output)
- self.assertIn("exited normally", r.output)
+ self._gdb_debug_cpp_example_check(r.output, magic_string=magic_string)
# Stop the gdbserver
r = runCmd(gdbserver_script + ' stop', output_log=self._cmd_logger)
@@ -2862,6 +2885,7 @@ class DevtoolIdeSdkTests(DevtoolBase):
bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
recipe_name, testimage, qemu.ip)
runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger)
+
self._gdb_cross()
self._verify_cmake_preset(tempdir)
self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
@@ -2893,6 +2917,7 @@ class DevtoolIdeSdkTests(DevtoolBase):
bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=none' % (
recipe_name, testimage, qemu.ip)
runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger)
+
self._gdb_cross()
self._devtool_ide_sdk_qemu(tempdir, qemu, recipe_name, example_exe)
@@ -2904,22 +2929,264 @@ class DevtoolIdeSdkTests(DevtoolBase):
# after the install and deploy scripts updated the file
self._verify_conf_file(qemu, conf_file, example_user_group, example_user_group)
+ def _verify_launch_json(self, tempdir):
+ """Verify the launch.json file created is valid and contains proper debug configurations"""
+ launch_json_path = os.path.join(tempdir, '.vscode', 'launch.json')
+ self.assertTrue(os.path.exists(launch_json_path), "launch.json file should exist")
+
+ with open(launch_json_path) as launch_j:
+ launch_d = json.load(launch_j)
+
+ self.assertIn("configurations", launch_d)
+ configurations = launch_d["configurations"]
+ self.assertEqual(len(configurations), 3, "Should have exactly three debug configurations")
+
+ # Track configurations found
+ once_configs = []
+ attach_configs = []
+
+ for config in configurations:
+ # Verify required fields exist
+ required_fields = ["name", "type", "request", "program", "cwd", "MIMode",
+ "miDebuggerPath", "miDebuggerServerAddress"]
+ for field in required_fields:
+ self.assertIn(field, config, f"Configuration '{config.get('name', 'Unknown')}' missing required field: {field}")
+
+ # Verify common configuration values
+ self.assertEqual(config["type"], "cppdbg", f"Configuration '{config['name']}' should use cppdbg type")
+ self.assertEqual(config["request"], "launch", f"Configuration '{config['name']}' should be launch type")
+ self.assertEqual(config["cwd"], "${workspaceFolder}", f"Configuration '{config['name']}' should use workspaceFolder as cwd")
+ self.assertEqual(config["MIMode"], "gdb", f"Configuration '{config['name']}' should use gdb MIMode")
+ self.assertEqual(config.get("externalConsole", False), False, f"Configuration '{config['name']}' should not use external console")
+ self.assertEqual(config.get("stopAtEntry", True), True, f"Configuration '{config['name']}' should stop at entry")
+
+ # Verify program path is absolute and exists conceptually
+ program = config["program"]
+ self.assertTrue(program.startswith("/"), f"Configuration '{config['name']}' program path should be absolute: {program}")
+ self.assertIn("/image/usr/bin/", program, f"Configuration '{config['name']}' program should be in image/usr/bin")
+
+ # Verify debugger path
+ debugger_path = config["miDebuggerPath"]
+ self.assertTrue(debugger_path.endswith("-gdb"), f"Configuration '{config['name']}' debugger should end with -gdb: {debugger_path}")
+ self.assertIn("/recipe-sysroot-native/usr/bin/", debugger_path, f"Configuration '{config['name']}' debugger should be in sysroot-native")
+
+ # Verify server address format
+ server_addr = config["miDebuggerServerAddress"]
+ self.assertRegex(server_addr, r"^\d+\.\d+\.\d+\.\d+:\d+$", f"Configuration '{config['name']}' server address should be IP:PORT format: {server_addr}")
+
+ # Verify additional SO lib search path exists and contains debug paths
+ so_paths = config.get("additionalSOLibSearchPath", [])
+ self.assertIn("/.debug", so_paths, f"Configuration '{config['name']}' should include debug symbol paths")
+ self.assertIn("/rootfs-dbg/", so_paths, f"Configuration '{config['name']}' should include rootfs-dbg paths")
+
+ # Verify source file mappings
+ source_map = config.get("sourceFileMap", {})
+ self.assertIsInstance(source_map, dict, f"Configuration '{config['name']}' sourceFileMap should be a dictionary")
+ self.assertIn("/usr/src/debug", source_map, f"Configuration '{config['name']}' should map /usr/src/debug")
+ self.assertIn("${workspaceFolder}", str(source_map), f"Configuration '{config['name']}' should map to workspaceFolder")
+
+ # Verify setup commands for sysroot
+ setup_commands = config.get("setupCommands", [])
+ self.assertTrue(len(setup_commands) >= 1, f"Configuration '{config['name']}' should have setup commands")
+ sysroot_cmd = setup_commands[0]
+ self.assertIn("sysroot", sysroot_cmd.get("text", ""), f"Configuration '{config['name']}' should set sysroot in setup commands")
+
+ # Verify preLaunchTask exists and matches name pattern
+ task = config.get("preLaunchTask", "")
+ self.assertTrue(task, f"Configuration '{config['name']}' preLaunchTask should not be empty")
+ # Task should contain port number and executable name from config name
+ config_name = config["name"]
+ if "_once" in config_name:
+ self.assertIn("_once", task, f"once configuration '{config_name}' should have once preLaunchTask")
+ elif "_attach" in config_name:
+ self.assertIn("_attach", task, f"attach configuration '{config_name}' should have attach preLaunchTask")
+
+ # Categorize configurations
+ config_name = config["name"]
+ if "_once" in config_name:
+ once_configs.append(config_name)
+ elif "_attach" in config_name:
+ attach_configs.append(config_name)
+
+ # Verify we have expected configuration types
+ self.assertEqual(len(once_configs), 2, f"Should have two once configuration, found: {once_configs}")
+ self.assertEqual(len(attach_configs), 1, f"Should have one attach configuration, found: {attach_configs}")
+
+ def _verify_launch_json_debugging(self, tempdir, qemu, recipe_name, example_exe):
+ """Verify remote debugging and deployment works using launch.json configurations
+
+ This method tests the VSCode debug configurations by:
+ 1. Starting gdbserver on target using tasks.json commands
+ 2. Running gdb debugging session with batch commands
+ $BUILDDIR/tmp/work/x86_64-linux/gdb-cross-x86_64/16.3/recipe-sysroot-native/usr/bin/x86_64-poky-linux/x86_64-poky-linux-gdb --batch \
+ -ex 'set sysroot $BUILDDIR/tmp/work/x86-64-v3-poky-linux/cmake-example/1.0/image' \
+ -ex 'set substitute-path /usr/include $BUILDDIR/tmp/work/x86-64-v3-poky-linux/cmake-example/1.0/recipe-sysroot/usr/include' \
+ -ex 'set substitute-path /usr/src/debug/cmake-example/1.0 $BUILDDIR/workspace/sources/cmake-example' \
+ -ex 'set substitute-path /usr/src/debug $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/usr/src/debug' \
+ -ex 'set solib-search-path $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/lib/.debug:\
+ $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/usr/lib/.debug:\
+ $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/usr/lib/debug:\
+ $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/lib:\
+ $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/usr/lib:\
+ $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs/lib:\
+ $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs/usr/lib' \
+ -ex 'file $BUILDDIR/tmp/work/x86-64-v3-poky-linux/cmake-example/1.0/image/usr/bin/cmake-example' \
+ -ex 'target remote 192.168.7.2:1234' \
+ -ex 'break main' \
+ -ex 'continue' \
+ -ex 'break CppExample::print_json()' \
+ -ex 'continue' \
+ -ex 'print CppExample::test_string.compare("cpp-example-lib Magic: 123456789")' \
+ -ex 'print CppExample::test_string.compare("cpp-example-lib Magic: 123456789aaa")' \
+ -ex 'list cpp-example-lib.hpp:14,14' \
+ -ex 'continue'
+ 3. Verifying debug output and stopping gdbserver
+ """
+ with open(os.path.join(tempdir, '.vscode', 'launch.json')) as launch_j:
+ launch_d = json.load(launch_j)
+ with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_j:
+ tasks_d = json.load(tasks_j)
+
+ configurations = launch_d["configurations"]
+ tasks = tasks_d["tasks"]
+
+ # Test one configuration for remote debugging
+ once_config_count = 0
+ for config in configurations:
+ if f"usr-bin-{recipe_name}_once" in config["name"]:
+ once_config_count += 1
+ self._verify_launch_config(tempdir, config, tasks, qemu, example_exe,
+ self._gdb_debug_cpp_example, self._gdb_debug_cpp_example_check)
+ # It works but is not 100% reliable in VSCode
+ # This one: https://github.com/microsoft/vscode-cpptools/issues/4243 ?
+ # elif f"usr-bin-{recipe_name}_attach" in config["name"]
+ # self._verify_launch_config(tempdir, config, tasks, qemu, example_exe)
+ else:
+ continue
+ self.assertEqual(once_config_count, 1, f"Should have one once configuration, found: {once_config_count}")
+
+ def _verify_launch_config(self, tempdir, launch_config, tasks, qemu, example_exe, debug_func=None, debug_check_func=None):
+ self.assertIsNotNone(launch_config, "Should have at least one launch debug configuration")
+
+ # Extract configuration values for launch.json
+ debugger_path = launch_config["miDebuggerPath"]
+ server_addr = launch_config["miDebuggerServerAddress"]
+ prelaunch_task_name = launch_config["preLaunchTask"]
+ program = launch_config["program"]
+ additional_so_lib_search_path = launch_config["additionalSOLibSearchPath"]
+ source_file_map = launch_config["sourceFileMap"]
+ setup_commands = launch_config["setupCommands"]
+
+ # Find the preLaunchTask in tasks.json
+ prelaunch_task = next(
+ (task for task in tasks if task["label"] == prelaunch_task_name), None)
+ self.assertIsNotNone(prelaunch_task, f"PreLaunchTask '{prelaunch_task_name}' not found in tasks.json")
+
+ # Find the dependsOn task if exists (install and deploy-target)
+ if "dependsOn" in prelaunch_task:
+ depends_task_names = prelaunch_task["dependsOn"]
+ for depends_task_name in depends_task_names:
+ depends_task = next(
+ (task for task in tasks if task["label"] == depends_task_name), None)
+ self.assertIsNotNone(depends_task, f"DependsOn task '{depends_task_name}' not found in tasks.json")
+ # For simplicity, we assume the dependsOn task is a prerequisite and does not affect the main command
+ self.logger.debug(f"PreLaunchTask '{prelaunch_task_name}' depends on '{depends_task_name}'")
+
+ # Extract command details from dependsOn task
+ depends_task_command = depends_task["command"]
+ depends_task_args = depends_task.get("args", [])
+ self.logger.debug(f"Would execute dependsOn task: {depends_task_command} {' '.join(depends_task_args)}")
+ runCmd(f"{depends_task_command} {' '.join(depends_task_args)}", output_log=self._cmd_logger)
+
+ # Verify task structure and extract command details
+ self.assertEqual(prelaunch_task["type"], "shell", f"Task '{prelaunch_task_name}' should be shell type")
+ task_command = prelaunch_task["command"]
+ task_args = prelaunch_task["args"]
+
+ # The command should be ssh for remote execution
+ self.assertEqual(task_command, "ssh", f"Task '{prelaunch_task_name}' should use ssh command")
+ self.assertTrue(len(task_args) >= 2, f"Task '{prelaunch_task_name}' should have at least 2 args (ssh options and remote command)")
+
+ sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
+ result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'test -x /usr/bin/gdbserver'),
+ output_log=self._cmd_logger)
+ self.assertEqual(result.status, 0, "gdbserver should be installed on target")
+ result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'test -x ' + os.path.join('/usr/bin', example_exe)),
+ output_log=self._cmd_logger)
+ self.assertEqual(result.status, 0, "Example binary should be installed on target")
+
+ # Start gdbserver on target using the task command (keep the ssh connection open while debugging)
+ ssh_gdbserver_cmd = [task_command] + task_args
+ # Fix shell command escaping - remove extra quotes from the last argument
+ # The task_args likely contains a quoted shell command that needs to be unquoted
+ if len(ssh_gdbserver_cmd) > 0 and ssh_gdbserver_cmd[-1].startswith('"') and ssh_gdbserver_cmd[-1].endswith('"'):
+ ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1] # Remove surrounding quotes
+ self.logger.debug(f"Starting gdbserver with command: {' '.join(ssh_gdbserver_cmd)}")
+ with RunCmdBackground(ssh_gdbserver_cmd, output_log=self._cmd_logger):
+ # Give gdbserver a moment to start
+ time.sleep(1)
+
+ # Verify gdbserver is running on target and listening on expected port
+ result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'), output_log=self._cmd_logger)
+ self.assertEqual(result.status, 0, "Failed to check processes on target")
+ self.assertIn("gdbserver", result.output, "gdbserver should be running on target")
+ _, server_port = server_addr.split(':')
+ self.assertIn(server_port, result.output, f"gdbserver should be listening on port {server_port}")
+
+ if debug_func and debug_check_func:
+ # Do a gdb remote session using the once configuration
+ gdb_batch_cmd = debugger_path + " --batch"
+ for setup_command in setup_commands:
+ # What VSCode does, for tracing add "logging": {"engineLogging": true } to launch.json
+ setup_cmd = setup_command["text"].strip()
+ if setup_cmd.startswith("-"):
+ # Ignore commands starting with '-' as they are VSCode internal commands?
+ continue
+ else:
+ gdb_batch_cmd += ' -ex ' + shlex.quote(setup_cmd)
+ for k, v in source_file_map.items():
+ gdb_batch_cmd += " -ex 'set substitute-path %s %s'" % (k, v.replace("${workspaceFolder}", tempdir))
+ gdb_batch_cmd += " -ex 'set solib-search-path %s'" % additional_so_lib_search_path
+ gdb_batch_cmd += " -ex 'file %s'" % program
+ gdb_batch_cmd += " -ex 'target remote %s'" % server_addr
+ # Add a basic set of command performing a simple debugging session
+ gdb_batch_cmd += debug_func(DevtoolIdeSdkTests.MAGIC_STRING_ORIG, "continue")
+ self.logger.debug(f"Starting gdb session with command: {gdb_batch_cmd}")
+ r = runCmd(gdb_batch_cmd, output_log=self._cmd_logger)
+ self.logger.debug("%s %s returned: %s", debugger_path, gdb_batch_cmd, r.output)
+ self.assertEqual(r.status, 0)
+ debug_check_func(r.output, DevtoolIdeSdkTests.MAGIC_STRING_ORIG)
+
+ @OETestTag("runqemu")
def test_devtool_ide_sdk_code_cmake(self):
"""Verify a cmake recipe works with ide=code mode"""
recipe_name = "cmake-example"
+ example_exe = "cmake-example"
build_file = "CMakeLists.txt"
testimage = "oe-selftest-image"
+ build_file = "CMakeLists.txt"
self._check_workspace()
self._write_bb_config([recipe_name])
- tempdir = self._devtool_ide_sdk_recipe(
- recipe_name, build_file, testimage)
- bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@192.168.17.17 -c --ide=code' % (
- recipe_name, testimage)
- runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger)
- self._verify_cmake_preset(tempdir)
- self._verify_install_script_code(tempdir, recipe_name)
- self._gdb_cross()
+
+ # Verify deployment to Qemu (system mode) works
+ self._check_runqemu_prerequisites()
+ bitbake(testimage)
+ with runqemu(testimage, runqemuparams="nographic") as qemu:
+ tempdir = self._devtool_ide_sdk_recipe(
+ recipe_name, build_file, testimage)
+ bitbake_sdk_cmd = 'devtool ide-sdk %s %s -t root@%s -c --ide=code' % (
+ recipe_name, testimage, qemu.ip)
+ runCmd(bitbake_sdk_cmd, output_log=self._cmd_logger)
+ self._verify_cmake_preset(tempdir)
+ self._verify_install_script_code(tempdir, recipe_name)
+ self._gdb_cross()
+
+ # Verify the launch.json file created is valid
+ self._verify_launch_json(tempdir)
+
+ # Verify deployment and remote debugging works
+ self._verify_launch_json_debugging(tempdir, qemu, recipe_name, example_exe)
def test_devtool_ide_sdk_code_meson(self):
"""Verify a meson recipe works with ide=code mode"""