| """ |
| 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" |
| ) |