Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/etc/debugger_tester/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os

debugger = os.environ.get("DEBUGGER_TESTER_DEBUGGER")

if debugger == "lldb":
from .lldb.batchmode import main as main
elif debugger == "gdb":
from .gdb.gdb_commands import ReprCommand as ReprCommand
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class Target(Enum):

def get_target() -> Target:
# set by compiletest when launching LLDB
t: str = os.environ["LLDB_BATCHMODE_TARGET_TRIPLE"]
t: str = os.environ["DEBUGGER_TESTER_TARGET_TRIPLE"]

if t.endswith("windows-msvc"):
return Target.WindowsMsvc
Expand All @@ -79,7 +79,7 @@ def get_target() -> Target:
return Target.NonWindows


BLESS: Final[bool] = os.environ["LLDB_BATCHMODE_BLESS_TEST_DATA"] == "1"
BLESS: Final[bool] = os.environ["DEBUGGER_TESTER_BLESS_TEST_DATA"] == "1"
"""Global constant set by `compiletest` that determines whether or not we are blessing the test
data."""

Expand Down Expand Up @@ -490,7 +490,7 @@ class TargetData:
Additionally, since there are differences in the internals of some structs based on OS (e.g.
`PathBuf`/`OsString`), we need to be aware of whether we're on Windows or not.

A global var `TARGET` is set to the current variant upon `lldb_batchmode`'s instantiation using
A global var `TARGET` is set to the current variant upon `debugger_tester`'s instantiation using
an env var passed from `compiletest` and is not expected to change afterwards.
"""

Expand All @@ -513,7 +513,7 @@ class TargetData:
@staticmethod
def initialize() -> "TargetData":
result = TargetData()
path = os.environ["LLDB_BATCHMODE_INPUT_DATA_PATH"]
path = os.environ["DEBUGGER_TESTER_INPUT_DATA_PATH"]
if not os.path.isfile(path):
if BLESS:
return result
Expand All @@ -535,12 +535,12 @@ def initialize() -> "TargetData":
return result

def save_blessing(self, metadata: BlessMetadata):
"""Writes the entirety of `self` to the env var `LLDB_BATCHMODE_INPUT_DATA_PATH`, which is
set by `compiletest` before running `lldb_batchmode. Used to finalize changes made by one or
more `from_lldb.bless_variable` calls.
"""Writes the entirety of `self` to the env var `DEBUGGER_TESTER_INPUT_DATA_PATH`, which is
set by `compiletest` before running `debugger_tester`. Used to finalize changes made by one
or more `from_lldb.bless_variable` calls.

This function should be called exactly once, right before
`lldb_batchmode.runner.main` exits if the following conditions are met:
`debugger_tester.lldb.batchmode.main` exits if the following conditions are met:

1. No other exceptions or error states occurred
2. `BLESS == True`
Expand All @@ -551,7 +551,7 @@ def save_blessing(self, metadata: BlessMetadata):
"""

self.bless_metadata = metadata
path = os.environ["LLDB_BATCHMODE_INPUT_DATA_PATH"]
path = os.environ["DEBUGGER_TESTER_INPUT_DATA_PATH"]
# dumping directly to a file is somewhat unsafe. If the `Variable`/`Type` data ends up in a
# state that cannot be serialized correctly, the json ends up malformed, and we could end up
# overwriting valid test data with a complete mess. Since the in-memory data is typically
Expand Down Expand Up @@ -599,3 +599,66 @@ def clean_nones(value):


INPUT_DATA: TargetData = TargetData.initialize()


TYPES_TESTED: dict[str, Result] = {}
"""Since types are unique and unchanging, we only need to test each type once. This also helps
ensure we have tested all types in `INPUT_DATA`
"""


VARS_TESTED: list[dict[str, Result]] = []
"""Used to help ensure all expected variables were tested. Each element of the list corresponds to a
breakpoint, and contains a set of all of the variable names tested for that breakpoint."""


def tested_all_types() -> bool:
"""Returns true if all types in INPUT_DATA were tested this run."""

expected_types = set(INPUT_DATA.types)
untested_types = expected_types.difference(TYPES_TESTED.keys())

if len(untested_types) != 0:
print(
f"{ANSI_RED}[repr error]{ANSI_END} The following types were expected, but were not \
tested:\n {untested_types}"
)

return len(untested_types) == 0


def tested_all_variables() -> bool:
expected_vars = [set(vars) for vars in INPUT_DATA.breakpoints]
untested_vars = [
expected.difference(tested.keys())
for expected, tested in zip(expected_vars, VARS_TESTED)
]

tested_not_expected = [
set(tested.keys()).difference(expected)
for expected, tested in zip(expected_vars, VARS_TESTED)
]

result = True

for i, v in enumerate(untested_vars):
if len(v) == 0:
continue

result = False
print(
f"{ANSI_RED}[repr error]{ANSI_END} The following variables were expected at \
breakpoint#{i}, but were not tested:\n {v}"
)

for i, v in enumerate(tested_not_expected):
if len(v) == 0:
continue

result = False
print(
f"{ANSI_RED}[repr error]{ANSI_END} The following variables were tested, but do not \
exist in the input data at breakpoint#{i}:\n {v}"
)

return result
Comment on lines +604 to +664

@Walnut356 Walnut356 Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were just moved from check_lldb.py.

I might be able to factor more of the checking logic out and share it between lldb and gdb. There are less differences for their API than I was expecting. It would probably require passing a few wrappers to the check function that abstract away the debugger-specific accesses, but we'll see.

View changes since the review

Empty file.
Loading
Loading