[Lldb-commits] [lldb] 047f127 - [lldb-dap] Migrate DAP io tests. (#209538)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 15 18:55:41 PDT 2026
Author: Ebuka Ezike
Date: 2026-07-16T02:55:36+01:00
New Revision: 047f12725a95425195f96b246300e782808322a3
URL: https://github.com/llvm/llvm-project/commit/047f12725a95425195f96b246300e782808322a3
DIFF: https://github.com/llvm/llvm-project/commit/047f12725a95425195f96b246300e782808322a3.diff
LOG: [lldb-dap] Migrate DAP io tests. (#209538)
the launch_io* tests now allow testing args, input, and environment
together.
Previously, these test configurations were mutually exclusive. They are
now combined, removing the need to create a separate test for each input
source.
Migrated Tests:
- TestDAP_io.py
- TestDAP_launch_io_integratedTerminal.py
- TestDAP_launch_io_internalConsole.py
Added:
Modified:
lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
lldb/test/API/tools/lldb-dap/launch/io/DAP_launch_io.py
lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_integratedTerminal.py
lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_internalConsole.py
lldb/test/API/tools/lldb-dap/launch/io/main.cpp
Removed:
################################################################################
diff --git a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
index 19b2da1dfd6c0..418415f4c3c0f 100644
--- a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
+++ b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
@@ -2,31 +2,20 @@
Test lldb-dap IO handling.
"""
-import sys
-
-from lldbsuite.test.decorators import *
-import lldbdap_testcase
-import dap_server
+from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase
EXIT_FAILURE = 1
EXIT_SUCCESS = 0
-class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_io(DAPTestCaseBase):
def launch(self):
- self.create_debug_adapter()
- self.assertIsNotNone(self.dap_server.process)
- process = self.dap_server.process
-
- def cleanup():
- # If the process is still alive, kill it.
- if process.poll() is None:
- process.kill()
- process.wait()
-
- # Execute the cleanup function during test case tear down.
- self.addTearDownHook(cleanup)
+ adapter = self.create_stdio_debug_adapter()
+ self.assertTrue(adapter.is_alive)
+ self.assertIsNotNone(adapter.process)
+ process = adapter.process
+ self.assertIsNotNone(process.stdin)
return process
def test_eof_immediately(self):
diff --git a/lldb/test/API/tools/lldb-dap/launch/io/DAP_launch_io.py b/lldb/test/API/tools/lldb-dap/launch/io/DAP_launch_io.py
index f5253f6ba7935..3f46d26561a46 100644
--- a/lldb/test/API/tools/lldb-dap/launch/io/DAP_launch_io.py
+++ b/lldb/test/API/tools/lldb-dap/launch/io/DAP_launch_io.py
@@ -1,202 +1,172 @@
"""
Test the redirection of stdio.
-There are three ways to launch the debuggee
+There are three ways to launch the debuggee:
internalConsole, integratedTerminal and externalTerminal.
-For the three configurations, we test if we can read data
-from environments, stdin and cli arguments.
+For each redirection configuration we check the stdin, argv, and env
+input paths. The C++ test program writes whatever it receives from
+each available source.
NOTE: The testcases do not include all possible configurations of
-consoles, environments, stdin and cli arguments.
+consoles and input sources.
"""
from abc import abstractmethod
-import lldbdap_testcase
from tempfile import NamedTemporaryFile
+from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase, DAPTestSession
+from lldbsuite.test.tools.lldb_dap.types import Console, LaunchArgs
-class DAP_launchIO(lldbdap_testcase.DAPTestCaseBase):
- """The class holds the implementation
diff erent ways to redirect the debuggee I/O streams
- which is configurable from the Derived classes.
- Depending on the console type the output will be in
diff erent places.
- It also provides two abstract functions `_get_debuggee_stdout` and `_get_debuggee_stderr`
- that provides the debuggee stdout and stderr.
+class DAP_launchIO(DAPTestCaseBase):
+ """Implements the redirection scenarios that are common to every console.
+
+ Subclasses provide `console` and override `_get_debuggee_stdout` /
+ `_get_debuggee_stderr` for the cases where stdout / stderr are not
+ redirected to files (the streams have to be read from the console
+ instead, which
diff ers between InternalConsole and IntegratedTerminal).
"""
- def all_redirection(self, console: str, with_args: bool = False):
- """Test all standard io redirection."""
- self.build_and_create_debug_adapter()
+ def all_redirection(self, console: Console):
+ """All three streams redirected to files. Verify every input path."""
+ session = self.build_and_create_session()
program = self.getBuildArtifact("a.out")
- input_text = "from stdin with redirection"
- args_text = "string from argv"
- program_args = [args_text] if with_args else None
+ stdin_input = "from stdin"
+ args_input = "from argv"
+ env_input = "from env"
with NamedTemporaryFile("wt") as stdin, NamedTemporaryFile(
"rt"
) as stdout, NamedTemporaryFile("rt") as stderr:
- stdin.write(input_text)
+ stdin.write(stdin_input)
stdin.flush()
- self.launch_and_configurationDone(
- program,
- stdio=[stdin.name, stdout.name, stderr.name],
- console=console,
- args=program_args,
- )
- self.verify_process_exited()
-
- all_stdout = stdout.read()
- all_stderr = stderr.read()
- if with_args:
- self.assertEqual(f"[STDOUT][FROM_ARGV]: {args_text}", all_stdout)
- self.assertEqual(f"[STDERR][FROM_ARGV]: {args_text}", all_stderr)
-
- self.assertNotIn(f"[STDOUT][FROM_ARGV]: {args_text}", all_stderr)
- self.assertNotIn(f"[STDERR][FROM_ARGV]: {args_text}", all_stdout)
+ session.launch(
+ LaunchArgs(
+ program,
+ stdio=[stdin.name, stdout.name, stderr.name],
+ console=console,
+ args=["--read-stdin", args_input],
+ env={"FROM_ENV": env_input},
+ )
+ )
+ session.verify_process_exited()
- else:
- self.assertEqual(f"[STDOUT][FROM_STDIN]: {input_text}", all_stdout)
- self.assertEqual(f"[STDERR][FROM_STDIN]: {input_text}", all_stderr)
+ stdout_text = stdout.read()
+ stderr_text = stderr.read()
+ self.assertIn(f"[STDOUT][FROM_STDIN]: {stdin_input}", stdout_text)
+ self.assertIn(f"[STDOUT][FROM_ARGV]: {args_input}", stdout_text)
+ self.assertIn(f"[STDOUT][FROM_ENV]: {env_input}", stdout_text)
- self.assertNotIn(f"[STDERR][FROM_STDIN]: {input_text}", all_stdout)
- self.assertNotIn(f"[STDOUT][FROM_STDIN]: {input_text}", all_stderr)
+ self.assertIn(f"[STDERR][FROM_STDIN]: {stdin_input}", stderr_text)
+ self.assertIn(f"[STDERR][FROM_ARGV]: {args_input}", stderr_text)
+ self.assertIn(f"[STDERR][FROM_ENV]: {env_input}", stderr_text)
- def stdin_redirection(self, console: str, with_args: bool = False):
- """Test only stdin redirection."""
- self.build_and_create_debug_adapter()
+ def stdin_redirection(self, console: Console):
+ """Only stdin redirected. Verify every input path via console output."""
+ session = self.build_and_create_session()
program = self.getBuildArtifact("a.out")
- input_text = "string from stdin"
- args_text = "string from argv"
- program_args = [args_text] if with_args else None
+ stdin_input = "from stdin"
+ args_input = "from argv"
+ env_input = "from env"
with NamedTemporaryFile("w+t") as stdin:
- stdin.write(input_text)
+ stdin.write(stdin_input)
stdin.flush()
- self.launch_and_configurationDone(
- program, stdio=[stdin.name], console=console, args=program_args
+ session.launch(
+ LaunchArgs(
+ program,
+ stdio=[stdin.name],
+ console=console,
+ args=["--read-stdin", args_input],
+ env={"FROM_ENV": env_input},
+ )
)
- self.verify_process_exited()
+ session.verify_process_exited()
- stdout_text = self._get_debuggee_stdout()
- stderr_text = self._get_debuggee_stderr()
+ stdout_text = self._get_debuggee_stdout(session)
+ stderr_text = self._get_debuggee_stderr(session)
+ self.assertIn(f"[STDOUT][FROM_STDIN]: {stdin_input}", stdout_text)
+ self.assertIn(f"[STDOUT][FROM_ARGV]: {args_input}", stdout_text)
+ self.assertIn(f"[STDOUT][FROM_ENV]: {env_input}", stdout_text)
- if with_args:
- self.assertIn(f"[STDOUT][FROM_ARGV]: {args_text}", stdout_text)
- self.assertIn(f"[STDERR][FROM_ARGV]: {args_text}", stderr_text)
- else:
- self.assertIn(f"[STDOUT][FROM_STDIN]: {input_text}", stdout_text)
- self.assertIn(f"[STDERR][FROM_STDIN]: {input_text}", stderr_text)
-
- def stdout_redirection(self, console: str, with_env: bool = False):
- """Test only stdout redirection."""
- self.build_and_create_debug_adapter()
- program = self.getBuildArtifact("a.out")
+ self.assertIn(f"[STDERR][FROM_STDIN]: {stdin_input}", stderr_text)
+ self.assertIn(f"[STDERR][FROM_ARGV]: {args_input}", stderr_text)
+ self.assertIn(f"[STDERR][FROM_ENV]: {env_input}", stderr_text)
- argv_text = "output with\n multiline"
- # By default unix terminals the ONLCR flag is enabled. which replaces '\n' with '\r\n'
- # see https://man7.org/linux/man-pages/man3/termios.3.html.
- # This does not affect writing to normal files.
- argv_replaced_text = argv_text.replace("\n", "\r\n")
+ def stdout_redirection(self, console: Console):
+ """Only stdout redirected. Verify argv and env paths.
- program_args = [argv_text]
- env_text = "string from env"
- env = {"FROM_ENV": env_text} if with_env else {}
+ stdin is not set up — the C++ program skips reading it because the
+ file descriptor is a tty (would block).
+ """
+ session = self.build_and_create_session()
+ program = self.getBuildArtifact("a.out")
+ args_input = "from argv"
+ env_input = "from env"
with NamedTemporaryFile("rt") as stdout:
- self.launch_and_configurationDone(
- program,
- stdio=[None, stdout.name],
- console=console,
- args=program_args,
- env=env,
+ session.launch(
+ LaunchArgs(
+ program,
+ stdio=[None, stdout.name],
+ console=console,
+ args=[args_input],
+ env={"FROM_ENV": env_input},
+ )
)
- self.verify_process_exited()
+ session.verify_process_exited()
- # check stdout
stdout_text = stdout.read()
- stderr_text = self._get_debuggee_stderr()
- if with_env:
- self.assertIn(f"[STDOUT][FROM_ENV]: {env_text}", stdout_text)
- self.assertIn(f"[STDERR][FROM_ENV]: {env_text}", stderr_text)
-
- self.assertNotIn(f"[STDERR][FROM_ENV]: {env_text}", stdout_text)
- self.assertNotIn(f"[STDOUT][FROM_ENV]: {env_text}", stderr_text)
- else:
- self.assertIn(f"[STDOUT][FROM_ARGV]: {argv_text}", stdout_text)
-
- self.assertNotIn(
- f"[STDERR][FROM_ARGV]: {argv_replaced_text}", stdout_text
- )
- self.assertNotIn(f"[STDOUT][FROM_ARGV]: {argv_text}", stderr_text)
-
- # check stderr
- stderr_text = self._get_debuggee_stderr()
- # FIXME: when using 'integrated' or 'external' terminal we do not correctly
- # escape newlines that are sent to the terminal.
- if console == "integratedConsole":
- if with_env:
- self.assertNotIn(f"[STDOUT][FROM_ENV]: {env_text}", stderr_text)
- self.assertIn(f"[STDERR][FROM_ENV]: {env_text}", stderr_text)
- else:
- self.assertNotIn(
- f"[STDOUT][FROM_ARGV]: {argv_replaced_text}", stderr_text
- )
- self.assertIn(
- f"[STDERR][FROM_ARGV]: {argv_replaced_text}", stderr_text
- )
-
- def stderr_redirection(self, console: str, with_env: bool = False):
- """Test only stdout redirection."""
- self.build_and_create_debug_adapter()
- program = self.getBuildArtifact("a.out")
+ stderr_text = self._get_debuggee_stderr(session)
+ self.assertIn(f"[STDOUT][FROM_ARGV]: {args_input}", stdout_text)
+ self.assertIn(f"[STDOUT][FROM_ENV]: {env_input}", stdout_text)
- argv_text = "output with\n multiline"
- # By default unix terminals the ONLCR flag is enabled. which replaces '\n' with '\r\n'
- # see https://man7.org/linux/man-pages/man3/termios.3.html.
- # This does not affect writing to normal files.
- # Currently out test implementation for external and integrated Terminal does not run the
- # program through a shell terminal.
- argv_replaced_text = argv_text
- if console == "internalConsole":
- argv_replaced_text = argv_text.replace("\n", "\r\n")
- program_args = [argv_text]
- env_text = "string from env"
- env = {"FROM_ENV": env_text} if with_env else {}
+ self.assertIn(f"[STDERR][FROM_ARGV]: {args_input}", stderr_text)
+ self.assertIn(f"[STDERR][FROM_ENV]: {env_input}", stderr_text)
+
+ def stderr_redirection(self, console: Console):
+ """Only stderr redirected. Verify argv and env paths."""
+ session = self.build_and_create_session()
+ program = self.getBuildArtifact("a.out")
+ args_input = "from argv"
+ env_input = "from env"
with NamedTemporaryFile("rt") as stderr:
- self.launch_and_configurationDone(
- program,
- stdio=[None, None, stderr.name],
- console=console,
- args=program_args,
- env=env,
+ session.launch(
+ LaunchArgs(
+ program,
+ stdio=[None, None, stderr.name],
+ console=console,
+ args=[args_input],
+ env={"FROM_ENV": env_input},
+ )
)
- self.verify_process_exited()
- stdout_text = self._get_debuggee_stdout()
+ session.verify_process_exited()
+
+ stdout_text = self._get_debuggee_stdout(session)
stderr_text = stderr.read()
- if with_env:
- self.assertIn(f"[STDOUT][FROM_ENV]: {env_text}", stdout_text)
- self.assertIn(f"[STDERR][FROM_ENV]: {env_text}", stderr_text)
- else:
- self.assertIn(f"[STDOUT][FROM_ARGV]: {argv_replaced_text}", stdout_text)
- self.assertIn(f"[STDERR][FROM_ARGV]: {argv_text}", stderr_text)
+ self.assertIn(f"[STDOUT][FROM_ARGV]: {args_input}", stdout_text)
+ self.assertIn(f"[STDOUT][FROM_ENV]: {env_input}", stdout_text)
+
+ self.assertIn(f"[STDERR][FROM_ARGV]: {args_input}", stderr_text)
+ self.assertIn(f"[STDERR][FROM_ENV]: {env_input}", stderr_text)
@abstractmethod
- def _get_debuggee_stdout(self) -> str:
+ def _get_debuggee_stdout(self, session: DAPTestSession) -> str:
"""Retrieves the standard output (stdout) from the debuggee process.
- The default destination of the debuggee's stdout can vary based on how the debugger
+ The default destination of the debuggee's stdout can vary based on how the debuggee
was launched (either a debug console or a pseudo-terminal (pty)).
It requires subclasses to implement the specific mechanism for obtaining the stdout stream.
"""
raise RuntimeError(f"NotImplemented for {self}")
@abstractmethod
- def _get_debuggee_stderr(self) -> str:
+ def _get_debuggee_stderr(self, session: DAPTestSession) -> str:
"""Retrieves the standard error (stderr) from the debuggee process.
- The default destination of the debuggee's stderr can vary based on how the debugger
+ The default destination of the debuggee's stderr can vary based on how the debuggee
was launched (either a debug console or a pseudo-terminal (pty)).
It requires subclasses to implement the specific mechanism for obtaining the stderr stream.
"""
diff --git a/lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_integratedTerminal.py b/lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_integratedTerminal.py
index 6fa1360e487a6..bf127d8f2d168 100644
--- a/lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_integratedTerminal.py
+++ b/lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_integratedTerminal.py
@@ -2,15 +2,15 @@
Test the redirection after launching in the integrated terminal.
"""
-from typing import IO
+from DAP_launch_io import DAP_launchIO
from lldbsuite.test.decorators import (
skipIfAsan,
skipIfBuildType,
skipIfRemote,
skipIfWindows,
)
-
-from DAP_launch_io import DAP_launchIO
+from lldbsuite.test.tools.lldb_dap import DAPTestSession
+from lldbsuite.test.tools.lldb_dap.types import Console, RunInTerminalRequest
@skipIfRemote
@@ -18,48 +18,24 @@
@skipIfBuildType(["debug"])
@skipIfWindows
class TestDAP_launch_io_IntegratedTerminal(DAP_launchIO):
- console = "integratedTerminal"
+ console = Console.INTEGRATED_TERMINAL
- # all redirection
def test_all_redirection(self):
self.all_redirection(console=self.console)
- def test_all_redirection_with_args(self):
- self.all_redirection(console=self.console, with_args=True)
-
- # stdin
def test_stdin_redirection(self):
self.stdin_redirection(console=self.console)
- def test_stdin_redirection_with_args(self):
- self.stdin_redirection(console=self.console, with_args=True)
-
- # stdout
def test_stdout_redirection(self):
self.stdout_redirection(console=self.console)
- def test_stdout_redirection_with_env(self):
- self.stdout_redirection(console=self.console, with_env=True)
-
- # stderr
def test_stderr_redirection(self):
self.stderr_redirection(console=self.console)
- def test_stderr_redirection_with_env(self):
- self.stderr_redirection(console=self.console, with_env=True)
-
- def _get_debuggee_stdout(self) -> str:
- self.assertIsNotNone(
- self.dap_server.reverse_process, "Expected a debuggee process."
- )
- proc_stdout: IO = self.dap_server.reverse_process.stdout
- self.assertIsNotNone(proc_stdout)
- return proc_stdout.read().decode()
+ def _get_debuggee_stdout(self, session: DAPTestSession) -> str:
+ self.assertIsInstance(session.last_reverse_request(), RunInTerminalRequest)
+ return session.get_stdout()
- def _get_debuggee_stderr(self) -> str:
- self.assertIsNotNone(
- self.dap_server.reverse_process, "Expected a debuggee process."
- )
- proc_stderr = self.dap_server.reverse_process.stderr
- self.assertIsNotNone(proc_stderr)
- return proc_stderr.read().decode()
+ def _get_debuggee_stderr(self, session: DAPTestSession) -> str:
+ self.assertIsInstance(session.last_reverse_request(), RunInTerminalRequest)
+ return session.get_stderr()
diff --git a/lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_internalConsole.py b/lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_internalConsole.py
index d1c47eb28351f..8585917d95787 100644
--- a/lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_internalConsole.py
+++ b/lldb/test/API/tools/lldb-dap/launch/io/TestDAP_launch_io_internalConsole.py
@@ -2,49 +2,31 @@
Test the redirection after launching in the internal console.
"""
-from lldbsuite.test.decorators import skipIfWindows
from DAP_launch_io import DAP_launchIO
+from lldbsuite.test.decorators import skipIfWindows
+from lldbsuite.test.tools.lldb_dap import DAPTestSession
+from lldbsuite.test.tools.lldb_dap.types import Console
@skipIfWindows
class TestDAP_launch_io_InternalConsole(DAP_launchIO):
- console = "internalConsole"
- __debuggee_stdout = None
+ console = Console.INTERNAL
- # all redirection
def test_all_redirection(self):
self.all_redirection(console=self.console)
- def test_all_redirection_with_args(self):
- self.all_redirection(console=self.console, with_args=True)
-
- # stdin
def test_stdin_redirection(self):
self.stdin_redirection(console=self.console)
- def test_stdin_redirection_with_args(self):
- self.stdin_redirection(console=self.console, with_args=True)
-
- # stdout
def test_stdout_redirection(self):
self.stdout_redirection(console=self.console)
- def test_stdout_redirection_with_env(self):
- self.stdout_redirection(console=self.console, with_env=True)
-
- # stderr
def test_stderr_redirection(self):
self.stderr_redirection(console=self.console)
- def test_stderr_redirection_with_env(self):
- self.stderr_redirection(console=self.console, with_env=True)
-
- def _get_debuggee_stdout(self) -> str:
- # self.get_stdout is not idempotent.
- if self.__debuggee_stdout is None:
- self.__debuggee_stdout = self.get_stdout()
- return self.__debuggee_stdout
+ def _get_debuggee_stdout(self, session: DAPTestSession) -> str:
+ return session.get_stdout()
- def _get_debuggee_stderr(self) -> str:
+ def _get_debuggee_stderr(self, session: DAPTestSession) -> str:
# NOTE: In internalConsole stderr writes to stdout.
- return self._get_debuggee_stdout()
+ return self._get_debuggee_stdout(session)
diff --git a/lldb/test/API/tools/lldb-dap/launch/io/main.cpp b/lldb/test/API/tools/lldb-dap/launch/io/main.cpp
index 96a29421c78ea..13bf5d0bd7c2a 100644
--- a/lldb/test/API/tools/lldb-dap/launch/io/main.cpp
+++ b/lldb/test/API/tools/lldb-dap/launch/io/main.cpp
@@ -1,24 +1,37 @@
#include <cstdlib>
+#include <cstring>
#include <iostream>
+#include <string>
int main(int argc, char *argv[]) {
- const bool use_stdin = argc <= 1;
- const char *use_env = std::getenv("FROM_ENV");
-
- if (use_env != nullptr) { // from environment variable
- std::cout << "[STDOUT][FROM_ENV]: " << use_env;
- std::cerr << "[STDERR][FROM_ENV]: " << use_env;
+ // Parse args: an optional positional value, plus the flag --read-stdin.
+ // The flag is set by tests that have wired up stdin redirection. Without
+ // it we never call getline, which would otherwise block on a pipe that's
+ // open but empty.
+ std::string arg_text;
+ bool read_stdin = false;
+ for (int i = 1; i < argc; ++i) {
+ if (std::strcmp(argv[i], "--read-stdin") == 0) {
+ read_stdin = true;
+ } else if (arg_text.empty()) {
+ arg_text = argv[i];
+ }
+ }
- } else if (use_stdin) { // from standard in
+ if (!arg_text.empty()) {
+ std::cout << "[STDOUT][FROM_ARGV]: " << arg_text << "\n";
+ std::cerr << "[STDERR][FROM_ARGV]: " << arg_text << "\n";
+ }
+ if (const char *env = std::getenv("FROM_ENV")) {
+ std::cout << "[STDOUT][FROM_ENV]: " << env << "\n";
+ std::cerr << "[STDERR][FROM_ENV]: " << env << "\n";
+ }
+ if (read_stdin) {
std::string line;
- std::getline(std::cin, line);
- std::cout << "[STDOUT][FROM_STDIN]: " << line;
- std::cerr << "[STDERR][FROM_STDIN]: " << line;
-
- } else { // from argv
- const char *first_arg = argv[1];
- std::cout << "[STDOUT][FROM_ARGV]: " << first_arg;
- std::cerr << "[STDERR][FROM_ARGV]: " << first_arg;
+ if (std::getline(std::cin, line)) {
+ std::cout << "[STDOUT][FROM_STDIN]: " << line << "\n";
+ std::cerr << "[STDERR][FROM_STDIN]: " << line << "\n";
+ }
}
return 0;
}
More information about the lldb-commits
mailing list