1 """Test Python APIs for process IO."""
5 from lldbsuite
.test
.decorators
import *
6 from lldbsuite
.test
.lldbtest
import *
7 from lldbsuite
.test
import lldbutil
10 class ProcessIOTestCase(TestBase
):
11 NO_DEBUG_INFO_TESTCASE
= True
14 # Get the full path to our executable to be debugged.
15 self
.exe
= self
.getBuildArtifact("process_io")
16 self
.local_input_file
= self
.getBuildArtifact("input.txt")
17 self
.local_output_file
= self
.getBuildArtifact("output.txt")
18 self
.local_error_file
= self
.getBuildArtifact("error.txt")
20 self
.input_file
= os
.path
.join(
21 self
.get_process_working_directory(), "input.txt"
23 self
.output_file
= os
.path
.join(
24 self
.get_process_working_directory(), "output.txt"
26 self
.error_file
= os
.path
.join(
27 self
.get_process_working_directory(), "error.txt"
29 self
.lines
= ["Line 1", "Line 2", "Line 3"]
31 @skipIfWindows # stdio manipulation unsupported on Windows
32 @expectedFlakeyLinux(bugnumber
="llvm.org/pr26437")
33 @skipIfDarwinEmbedded # I/O redirection like this is not supported on remote iOS devices yet <rdar://problem/54581135>
34 def test_stdin_by_api(self
):
35 """Exercise SBProcess.PutSTDIN()."""
39 self
.run_process(True)
40 output
= self
.process
.GetSTDOUT(1000)
41 self
.check_process_output(output
, output
)
43 @skipIfWindows # stdio manipulation unsupported on Windows
44 @expectedFlakeyLinux(bugnumber
="llvm.org/pr26437")
45 def test_stdin_redirection(self
):
46 """Exercise SBLaunchInfo::AddOpenFileAction() for STDIN without specifying STDOUT or STDERR."""
51 self
.run_process(False)
52 output
= self
.process
.GetSTDOUT(1000)
53 self
.check_process_output(output
, output
)
55 @skipIfWindows # stdio manipulation unsupported on Windows
56 @expectedFlakeyLinux(bugnumber
="llvm.org/pr26437")
57 @skipIfDarwinEmbedded # debugserver can't create/write files on the device
58 def test_stdout_redirection(self
):
59 """Exercise SBLaunchInfo::AddOpenFileAction() for STDOUT without specifying STDIN or STDERR."""
63 self
.redirect_stdout()
64 self
.run_process(True)
65 output
= self
.read_output_file_and_delete()
66 error
= self
.process
.GetSTDOUT(1000)
67 self
.check_process_output(output
, error
)
69 @skipIfWindows # stdio manipulation unsupported on Windows
70 @expectedFlakeyLinux(bugnumber
="llvm.org/pr26437")
71 @skipIfDarwinEmbedded # debugserver can't create/write files on the device
72 def test_stderr_redirection(self
):
73 """Exercise SBLaunchInfo::AddOpenFileAction() for STDERR without specifying STDIN or STDOUT."""
77 self
.redirect_stderr()
78 self
.run_process(True)
79 output
= self
.process
.GetSTDOUT(1000)
80 error
= self
.read_error_file_and_delete()
81 self
.check_process_output(output
, error
)
83 @skipIfWindows # stdio manipulation unsupported on Windows
84 @expectedFlakeyLinux(bugnumber
="llvm.org/pr26437")
85 @skipIfDarwinEmbedded # debugserver can't create/write files on the device
86 def test_stdout_stderr_redirection(self
):
87 """Exercise SBLaunchInfo::AddOpenFileAction() for STDOUT and STDERR without redirecting STDIN."""
91 self
.redirect_stdout()
92 self
.redirect_stderr()
93 self
.run_process(True)
94 output
= self
.read_output_file_and_delete()
95 error
= self
.read_error_file_and_delete()
96 self
.check_process_output(output
, error
)
98 @skipIfWindows # stdio manipulation unsupported on Windows
99 @expectedFlakeyLinux(bugnumber
="llvm.org/pr26437")
100 @skipIfDarwinEmbedded # debugserver can't create/write files on the device
101 def test_stdout_stderr_redirection_to_existing_files(self
):
102 """Exercise SBLaunchInfo::AddOpenFileAction() for STDOUT and STDERR redirect to output files already exist."""
107 # Create the output and error files with placeholder
108 placeholder
= "This content should be overwritten."
109 # Local file directory and working directory are the same for local debugging
110 f
= open(self
.local_output_file
, "w")
113 f
= open(self
.local_error_file
, "w")
116 if lldb
.remote_platform
:
118 'platform put-file "{local}" "{remote}"'.format(
119 local
=self
.local_output_file
, remote
=self
.output_file
123 'platform put-file "{local}" "{remote}"'.format(
124 local
=self
.local_error_file
, remote
=self
.error_file
128 self
.redirect_stdout()
129 self
.redirect_stderr()
130 self
.run_process(True)
131 output
= self
.read_output_file_and_delete()
132 error
= self
.read_error_file_and_delete()
133 self
.check_process_output(output
, error
)
135 # target_file - path on local file system or remote file system if running remote
136 # local_file - path on local system
137 def read_file_and_delete(self
, target_file
, local_file
):
138 if lldb
.remote_platform
:
140 'platform get-file "{remote}" "{local}"'.format(
141 remote
=target_file
, local
=local_file
146 os
.path
.exists(local_file
),
147 'Make sure "{local}" file exists'.format(local
=local_file
),
149 f
= open(local_file
, "r")
153 # TODO: add 'platform delete-file' file command
154 # if lldb.remote_platform:
155 # self.runCmd('platform delete-file "{remote}"'.format(remote=target_file))
156 os
.unlink(local_file
)
159 def read_output_file_and_delete(self
):
160 return self
.read_file_and_delete(self
.output_file
, self
.local_output_file
)
162 def read_error_file_and_delete(self
):
163 return self
.read_file_and_delete(self
.error_file
, self
.local_error_file
)
165 def create_target(self
):
166 """Create the target and launch info that will be used by all tests"""
167 self
.target
= self
.dbg
.CreateTarget(self
.exe
)
168 self
.launch_info
= self
.target
.GetLaunchInfo()
169 self
.launch_info
.SetWorkingDirectory(self
.get_process_working_directory())
171 def redirect_stdin(self
):
172 """Redirect STDIN (file descriptor 0) to use our input.txt file
174 Make the input.txt file to use when redirecting STDIN, setup a cleanup action
175 to delete the input.txt at the end of the test in case exceptions are thrown,
176 and redirect STDIN in the launch info."""
177 f
= open(self
.local_input_file
, "w")
178 for line
in self
.lines
:
182 if lldb
.remote_platform
:
184 'platform put-file "{local}" "{remote}"'.format(
185 local
=self
.local_input_file
, remote
=self
.input_file
189 # This is the function to remove the custom formats in order to have a
190 # clean slate for the next test case.
192 os
.unlink(self
.local_input_file
)
193 # TODO: add 'platform delete-file' file command
194 # if lldb.remote_platform:
195 # self.runCmd('platform delete-file "{remote}"'.format(remote=self.input_file))
197 # Execute the cleanup function during test case tear down.
198 self
.addTearDownHook(cleanup
)
199 self
.launch_info
.AddOpenFileAction(0, self
.input_file
, True, False)
201 def redirect_stdout(self
):
202 """Redirect STDOUT (file descriptor 1) to use our output.txt file"""
203 self
.launch_info
.AddOpenFileAction(1, self
.output_file
, False, True)
205 def redirect_stderr(self
):
206 """Redirect STDERR (file descriptor 2) to use our error.txt file"""
207 self
.launch_info
.AddOpenFileAction(2, self
.error_file
, False, True)
209 def run_process(self
, put_stdin
):
210 """Run the process to completion and optionally put lines to STDIN via the API if "put_stdin" is True"""
211 # Set the breakpoints
212 self
.breakpoint
= self
.target
.BreakpointCreateBySourceRegex(
213 "Set breakpoint here", lldb
.SBFileSpec("main.c")
215 self
.assertGreater(self
.breakpoint
.GetNumLocations(), 0, VALID_BREAKPOINT
)
217 # Launch the process, and do not stop at the entry point.
218 error
= lldb
.SBError()
219 # This should launch the process and it should exit by the time we get back
220 # because we have synchronous mode enabled
221 self
.process
= self
.target
.Launch(self
.launch_info
, error
)
223 self
.assertTrue(error
.Success(), "Make sure process launched successfully")
224 self
.assertTrue(self
.process
, PROCESS_IS_VALID
)
227 print("process launched.")
229 # Frame #0 should be at our breakpoint.
230 threads
= lldbutil
.get_threads_stopped_at_breakpoint(
231 self
.process
, self
.breakpoint
234 self
.assertEqual(len(threads
), 1)
235 self
.thread
= threads
[0]
236 self
.frame
= self
.thread
.frames
[0]
237 self
.assertTrue(self
.frame
, "Frame 0 is valid.")
240 print("process stopped at breakpoint, sending STDIN via LLDB API.")
242 # Write data to stdin via the public API if we were asked to
244 for line
in self
.lines
:
245 self
.process
.PutSTDIN(line
+ "\n")
247 # Let process continue so it will exit
248 self
.process
.Continue()
249 state
= self
.process
.GetState()
250 self
.assertState(state
, lldb
.eStateExited
, PROCESS_IS_VALID
)
252 def check_process_output(self
, output
, error
):
253 # Since we launched the process without specifying stdin/out/err,
254 # a pseudo terminal is used for stdout/err, and we are satisfied
255 # once "input line=>1" appears in stdout.
258 print("output = '%s'" % output
)
259 print("error = '%s'" % error
)
261 for line
in self
.lines
:
262 check_line
= "input line to stdout: %s" % (line
)
263 self
.assertIn(check_line
, output
, "verify stdout line shows up in STDOUT")
264 for line
in self
.lines
:
265 check_line
= "input line to stderr: %s" % (line
)
266 self
.assertIn(check_line
, error
, "verify stderr line shows up in STDERR")