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; } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
