[Lldb-commits] [lldb] [lldb][test] Introduce build_and_run test utility (PR #194386)
Raphael Isemann via lldb-commits
lldb-commits at lists.llvm.org
Mon Apr 27 07:17:49 PDT 2026
https://github.com/Teemperor created https://github.com/llvm/llvm-project/pull/194386
We currently have several hundred tests require a running process in a given state, and therefore perform the same three tasks:
* compile a test executable
* set a breakpoint by finding a source regex
* then launch the test process to hit that breakpoint.
A large chunk of these tests do this exact same setup with various versions of copied boilerplate code. The different versions we have all have different conventions of naming the breakpoint comment, the main file (and whether it should be resolved), and different generated error messages if things go wrong.
We already have a standardized and much shorter way of doing this in LLDB (see below), but this still encourages test writers to specify non-standard file names and non-standard breakpoint comment names.
```
self.build()
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
```
This patch introduces a simple `build_and_run` wrapper that takes care of all of these things in one go. It also forces the standard naming scheme that most tests have adoped with a breakpoint comment called `break here` and a `main.*` file.
I already adapted a few tests to this new format so that all added code in this patch is tested. I'll do the updating of the other tests in a several follow up PRs so that downstream folks can easily temporarily revert it if it causes issues.
>From 42c385ed375268a7b9deca9ca50677cfb58824b0 Mon Sep 17 00:00:00 2001
From: Raphael Isemann <rise at apple.com>
Date: Mon, 27 Apr 2026 14:54:59 +0100
Subject: [PATCH] [lldb][test] Introduce build_and_run test utility
We currently have several hundred tests require a running process
in a given state, and therefore perform the same three tasks:
* compile a test executable
* set a breakpoint by finding a source regex
* then launch the test process to hit that breakpoint.
A large chunk of these tests do this exact same setup with various
versions of copied boilerplate code. The different versions we have all
have different conventions of naming the breakpoint comment, the main
file (and whether it should be resolved), and different generated error
messages if things go wrong.
We already have a standardized and much shorter way of doing this in
LLDB (see below), but this still encourages test writers to specify
non-standard file names and non-standard breakpoint comment names.
```
self.build()
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
```
This patch introduces a simple `build_and_run` wrapper that takes care
of all of these things in one go. It also forces the standard naming
scheme that most tests have adoped with a breakpoint comment called
`break here` and a `main.*` file.
I already adapted a few tests to this new format so that all added code
in this patch is tested. I'll do the updating of the other tests in a
several follow up PRs so that downstream folks can easily temporarily
revert it if it causes issues.
---
lldb/packages/Python/lldbsuite/test/lldbtest.py | 17 +++++++++++++++++
.../anonymous-struct/TestCallUserAnonTypedef.py | 5 +----
.../dollar-in-variable/TestDollarInVariable.py | 5 +----
.../objc/bitfield_ivars/TestBitfieldIvars.py | 5 +----
...estObjCConflictingNamesForClassUpdateExpr.py | 5 +----
5 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 3922509d5d4f9..22d5c0483fb98 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1579,6 +1579,23 @@ def build(
self.runBuildCommand(command)
+ def build_and_run(self, dictionary=None):
+ """
+ Builds the target binary, launches it and runs to the breakpoint
+ location specified by the '// break here' comment.
+ """
+ self.build(dictionary=dictionary)
+
+ main_candidates = ["main.c", "main.cpp", "main.m", "main.mm"]
+
+ for candidate in main_candidates:
+ if os.path.exists(candidate):
+ lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec(candidate, False)
+ )
+ return
+ self.fail(f"Could not find any main file in {self.mydir}")
+
def runBuildCommand(self, command):
self.trace(shlex.join(command))
try:
diff --git a/lldb/test/API/commands/expression/anonymous-struct/TestCallUserAnonTypedef.py b/lldb/test/API/commands/expression/anonymous-struct/TestCallUserAnonTypedef.py
index 3be03a6caf8c7..8b15c6359493f 100644
--- a/lldb/test/API/commands/expression/anonymous-struct/TestCallUserAnonTypedef.py
+++ b/lldb/test/API/commands/expression/anonymous-struct/TestCallUserAnonTypedef.py
@@ -16,8 +16,5 @@
class TestExprLookupAnonStructTypedef(TestBase):
def test(self):
"""Test typedeffed untagged struct arguments for function call expressions"""
- self.build()
- lldbutil.run_to_source_breakpoint(
- self, "// break here", lldb.SBFileSpec("main.cpp")
- )
+ self.build_and_run()
self.expect_expr("multiply(&s)", result_type="double", result_value="1")
diff --git a/lldb/test/API/commands/expression/dollar-in-variable/TestDollarInVariable.py b/lldb/test/API/commands/expression/dollar-in-variable/TestDollarInVariable.py
index 1b18cd137751b..4e3be3f49de32 100644
--- a/lldb/test/API/commands/expression/dollar-in-variable/TestDollarInVariable.py
+++ b/lldb/test/API/commands/expression/dollar-in-variable/TestDollarInVariable.py
@@ -6,10 +6,7 @@
class TestCase(TestBase):
def test(self):
- self.build()
- lldbutil.run_to_source_breakpoint(
- self, "// break here", lldb.SBFileSpec("main.c")
- )
+ self.build_and_run()
self.expect_expr("$__lldb_expr_result", result_type="int", result_value="11")
self.expect_expr("$foo", result_type="int", result_value="12")
diff --git a/lldb/test/API/lang/objc/bitfield_ivars/TestBitfieldIvars.py b/lldb/test/API/lang/objc/bitfield_ivars/TestBitfieldIvars.py
index d9f95aeee0cbd..294c6fb18a4c2 100644
--- a/lldb/test/API/lang/objc/bitfield_ivars/TestBitfieldIvars.py
+++ b/lldb/test/API/lang/objc/bitfield_ivars/TestBitfieldIvars.py
@@ -6,10 +6,7 @@
class TestBitfieldIvars(TestBase):
def test(self):
- self.build()
- lldbutil.run_to_source_breakpoint(
- self, "// break here", lldb.SBFileSpec("main.m")
- )
+ self.build_and_run()
self.expect_expr(
"chb->hb->field1", result_type="unsigned int", result_value="0"
diff --git a/lldb/test/API/lang/objcxx/conflicting-names-class-update-utility-expr/TestObjCConflictingNamesForClassUpdateExpr.py b/lldb/test/API/lang/objcxx/conflicting-names-class-update-utility-expr/TestObjCConflictingNamesForClassUpdateExpr.py
index ad58bd33c7307..c3fc4b7c0ba41 100644
--- a/lldb/test/API/lang/objcxx/conflicting-names-class-update-utility-expr/TestObjCConflictingNamesForClassUpdateExpr.py
+++ b/lldb/test/API/lang/objcxx/conflicting-names-class-update-utility-expr/TestObjCConflictingNamesForClassUpdateExpr.py
@@ -17,10 +17,7 @@ class list works even when user-code contains functions with apparently
function.
"""
- self.build()
- lldbutil.run_to_source_breakpoint(
- self, "// break here", lldb.SBFileSpec("main.mm")
- )
+ self.build_and_run()
# First check our side effect variable is in its initial state.
self.expect_expr("called_function", result_summary='"none"')
More information about the lldb-commits
mailing list