Fix terminal width to 80 columns in tests (#2444)

* Fix terminal width to 80 columns in tests

Set a `PWNDBG_IN_TEST`environment variable when running gdb in tests. Use the dimensions in `LINES` and `COLUMNS` when looking up the window size when that envvar is set.

This makes context output always be 80 columns wide which allows to compare to hardcoded output.

* Use `width` and `height` parameters for cmd window size

They are updated to the correct values when switching between tui and cli mode. So it's unnecessary to parse `info win`.

* Remove workarounds for terminal size in DEVELOPING

* Don't bother updating the env of the debuggee
This commit is contained in:
peace-maker 2024-09-19 18:02:58 +02:00 committed by GitHub
parent 5553a93a3e
commit 6892a09c5a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 22 additions and 43 deletions

View File

@ -51,19 +51,6 @@ We have four types of tests: `gdb-tests`,`qemu-tests`, `unit-tests`, and Linux k
To run these tests, run [`./tests.sh`](./tests.sh). You can filter the tests to run by providing an argument to the script, such as `./tests.sh heap`, which will only run tests that contain "heap" in the name. You can also drop into the PDB debugger when a test fails with `./tests.sh --pdb`. To run these tests, run [`./tests.sh`](./tests.sh). You can filter the tests to run by providing an argument to the script, such as `./tests.sh heap`, which will only run tests that contain "heap" in the name. You can also drop into the PDB debugger when a test fails with `./tests.sh --pdb`.
Some of the tests rely on output that depends on a certain width/height of the terminal, so you will likely see many test failures when simply running `./tests.sh`. To run the tests in the expected environment, you can use:
```sh
docker compose run --build -T ubuntu24.04 ./tests.sh
# The `-T` disables the use of a pseudo-TTY
```
If you want rapidly iterate on tests (waiting for the container to rebuild and all the test source code files to compile can take a while), you can pipe `cat` on both ends to disable the PTY and get the correct terminal width/height for the tests.
```sh
cat | ./tests.sh | cat
```
To invoke cross-architecture tests, use `./qemu-tests.sh`, and to run unit tests, use `./unit-tests.sh` To invoke cross-architecture tests, use `./qemu-tests.sh`, and to run unit tests, use `./unit-tests.sh`
## Writing Tests ## Writing Tests

View File

@ -6,6 +6,7 @@ from typing import Any
from typing import Generator from typing import Generator
from typing import List from typing import List
from typing import Literal from typing import Literal
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Tuple from typing import Tuple
from typing import TypeVar from typing import TypeVar
@ -1254,38 +1255,22 @@ class GDB(pwndbg.dbg_mod.Debugger):
return f"%#{2 * pwndbg.gdblib.arch.ptrsize}x" % address return f"%#{2 * pwndbg.gdblib.arch.ptrsize}x" % address
@override @override
def get_cmd_window_size(self) -> Tuple[int, int]: def get_cmd_window_size(self) -> Tuple[Optional[int], Optional[int]]:
"""Get the size of the command window in TUI mode which could be different than the terminal window width \ """Get the size of the command window.
with horizontal split "tui new-layout hsrc { -horizontal src 1 cmd 1 } 1".
Possible output of "info win" in TUI mode: GDB keeps these parameters up to date with the actual window size
(gdb) info win of the command output. This is the full terminal size in CLI mode
Name Lines Columns Focus or the size of the cmd window in TUI mode.
src 77 104 (has focus)
cmd 77 105
Output of "info win" in non-TUI mode: When the window size is set to be unlimited (0), the parameter
(gdb) info win is None.
The TUI is not active.""" """
try: width = gdb.parameter("width")
info_out = gdb.execute("info win", to_string=True).split() height = gdb.parameter("height")
except gdb.error: return (
# Return None if the command is not compiled into GDB height if height is None else int(height),
# (gdb.error: Undefined info command: "win". Try "help info") width if width is None else int(width),
return None, None )
if "cmd" not in info_out:
# if TUI is not enabled, info win will output "The TUI is not active."
return None, None
# parse cmd window size from the output of "info win"
cmd_win_index = info_out.index("cmd")
if len(info_out) <= cmd_win_index + 2:
return None, None
elif (
not info_out[cmd_win_index + 1].isdigit() and not info_out[cmd_win_index + 2].isdigit()
):
return None, None
else:
return int(info_out[cmd_win_index + 1]), int(info_out[cmd_win_index + 2])
@override @override
def set_python_diagnostics(self, enabled: bool) -> None: def set_python_diagnostics(self, enabled: bool) -> None:

View File

@ -73,6 +73,8 @@ def get_window_size(target=sys.stdin):
rows, cols = get_cmd_window_size() rows, cols = get_cmd_window_size()
if rows is not None and cols is not None: if rows is not None and cols is not None:
return rows, cols return rows, cols
elif os.environ.get("PWNDBG_IN_TEST") is not None:
return fallback
try: try:
# get terminal size and force ret buffer len of 4 bytes for safe unpacking by passing equally long arg # get terminal size and force ret buffer len of 4 bytes for safe unpacking by passing equally long arg
rows, cols = struct.unpack("hh", fcntl.ioctl(target.fileno(), termios.TIOCGWINSZ, b"1234")) rows, cols = struct.unpack("hh", fcntl.ioctl(target.fileno(), termios.TIOCGWINSZ, b"1234"))

View File

@ -4,6 +4,8 @@ This file should consist of global test fixtures.
from __future__ import annotations from __future__ import annotations
import os
import gdb import gdb
import pytest import pytest
@ -17,8 +19,11 @@ def start_binary():
""" """
def _start_binary(path, *args): def _start_binary(path, *args):
os.environ["PWNDBG_IN_TEST"] = "1"
gdb.execute("file " + path) gdb.execute("file " + path)
gdb.execute("set exception-verbose on") gdb.execute("set exception-verbose on")
gdb.execute("set width 80")
os.environ["COLUMNS"] = "80"
gdb.execute("starti " + " ".join(args)) gdb.execute("starti " + " ".join(args))
global _start_binary_called global _start_binary_called