""" Meta-test: generate every kind of scripting extension template on the fly via `scripting extension generate`, then actually register and drive each one through a live lldb debug session, to catch regressions in the generator itself (e.g. broken imports, wrong base-class name, missing __init__ chaining) rather than just checking that a Python file was produced. The stub methods lldb calls into may legitimately report failure (e.g. an `is_alive` stub that returns None can make `process launch` report an error), so success/failure of the driving command is not asserted. What is asserted is that lldb never surfaces an unhandled Python traceback while running the generated code. """ import os import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestScriptingExtensionGenerate(TestBase): NO_DEBUG_INFO_TESTCASE = True def generate_extension(self, kind, generate_all=False): """Run `scripting extension generate` for `kind`, returning the path to the generated file. Fails the test if generation errors out.""" output_path = self.getBuildArtifact(f"generated_{kind}.py") if os.path.exists(output_path): os.remove(output_path) all_flag = " -a" if generate_all else "" self.runCmd( f"scripting extension generate{all_flag} -n Test -o {output_path} {kind}" ) self.assertTrue( os.path.exists(output_path), f"scripting extension generate did not create {output_path}", ) return output_path def run_and_assert_no_traceback(self, cmd): """Run `cmd` and fail the test if lldb surfaced an unhandled Python traceback while executing it. The command itself may still report failure (e.g. a stub method returning None) without that being a generator bug.""" self.runCmd(cmd, check=False) output = self.res.GetOutput() + self.res.GetError() self.assertNotIn( "Traceback (most recent call last)", output, f"command {cmd!r} raised a Python exception:\n{output}", ) def test_generate_operating_system(self): """OperatingSystem: generate, import, and activate via the target.process.python-os-plugin-path setting.""" self.build() path = self.generate_extension("OperatingSystem") self.run_and_assert_no_traceback(f"command script import {path}") lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c")) self.run_and_assert_no_traceback( f"settings set target.process.python-os-plugin-path {path}" ) self.run_and_assert_no_traceback("thread list") self.runCmd("settings clear target.process.python-os-plugin-path", check=False) def test_generate_scripted_platform(self): """ScriptedPlatform: generate, import, and select via `platform select`.""" path = self.generate_extension("ScriptedPlatform") self.run_and_assert_no_traceback(f"command script import {path}") self.run_and_assert_no_traceback( "platform select scripted-platform -C " "generated_ScriptedPlatform.TestScriptedPlatform" ) def test_generate_scripted_process(self): """ScriptedProcess: generate, import, and launch via `process launch -C`.""" self.build() path = self.generate_extension("ScriptedProcess") self.run_and_assert_no_traceback(f"command script import {path}") target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) self.assertTrue(target, VALID_TARGET) self.run_and_assert_no_traceback( "process launch -C generated_ScriptedProcess.TestScriptedProcess" ) def test_generate_scripted_thread_plan(self): """ScriptedThreadPlan: generate, import, and drive via `thread step-scripted -C`.""" self.build() path = self.generate_extension("ScriptedThreadPlan") self.run_and_assert_no_traceback(f"command script import {path}") lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c")) self.run_and_assert_no_traceback( "thread step-scripted -C " "generated_ScriptedThreadPlan.TestScriptedThreadPlan" ) def test_generate_scripted_breakpoint_resolver(self): """ScriptedBreakpointResolver: generate, import, and set via `breakpoint set -P`.""" self.build() path = self.generate_extension("ScriptedBreakpointResolver") self.run_and_assert_no_traceback(f"command script import {path}") target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) self.assertTrue(target, VALID_TARGET) self.run_and_assert_no_traceback( "breakpoint set -P " "generated_ScriptedBreakpointResolver.TestScriptedBreakpointResolver" ) def test_generate_scripted_hook_for_stop_hook(self): """ScriptedHook drives `target stop-hook add -P` (the stop-hook is a subset of the general scripted hook interface).""" self.build() path = self.generate_extension("ScriptedHook") self.run_and_assert_no_traceback(f"command script import {path}") target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) self.assertTrue(target, VALID_TARGET) self.run_and_assert_no_traceback( "target stop-hook add -P generated_ScriptedHook.TestScriptedHook" ) def test_generate_scripted_frame_provider(self): """ScriptedFrameProvider: generate, import, and register via `target frame-provider register -C`. Uses -a to generate all methods since `get_description` is a required static abstract method.""" self.build() path = self.generate_extension("ScriptedFrameProvider", generate_all=True) self.run_and_assert_no_traceback(f"command script import {path}") lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c")) self.run_and_assert_no_traceback( "target frame-provider register -C " "generated_ScriptedFrameProvider.TestScriptedFrameProvider" ) def test_generate_scripted_hook(self): """ScriptedHook: generate, import, and register via `target hook add -P`.""" self.build() path = self.generate_extension("ScriptedHook") self.run_and_assert_no_traceback(f"command script import {path}") target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) self.assertTrue(target, VALID_TARGET) self.run_and_assert_no_traceback( "target hook add -P generated_ScriptedHook.TestScriptedHook" )