Skip to content

Commit 9289b0b

Browse files
committed
first_stage_test: Add test_closed_stdin and test_closed_stdout
Signed-off-by: Marc Hartmayer <[email protected]>
1 parent 267e160 commit 9289b0b

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

tests/first_stage_test.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fcntl
22
import functools
33
import operator
4+
import os
45

56
import mitogen.core
67
import mitogen.parent
@@ -269,3 +270,93 @@ def test_timeout_error(self):
269270
b(""),
270271
stderr,
271272
)
273+
274+
def test_closed_stdin(self):
275+
"""
276+
The boot command should write an ECO marker to stdout, read the
277+
preamble from stdin, then execute it.
278+
279+
TODO UNDER DISCUSSION
280+
281+
This test attaches closes stdin to create a specific failure
282+
1. Fork child tries to read from STDIN, but fails as it is closed
283+
2. Fork child raises TimeoutError
284+
3. Fork child's file descriptors (write pipes) are closed by the OS
285+
4. Fork parent does `dup(<read pipe>, <stdin>)` and `exec(<python>)`
286+
5. Python reads `b''` (i.e. EOF) from stdin (a closed pipe)
287+
6. Python runs `''` (a valid script) and exits with success
288+
"""
289+
# We do not want to wait the default of 10s, change it to 0.1s
290+
self.conn._first_stage_timeout = 0.1
291+
args = self.conn.get_boot_command()
292+
293+
proc = testlib.subprocess.Popen(
294+
args=args,
295+
stdout=testlib.subprocess.PIPE,
296+
stderr=testlib.subprocess.PIPE,
297+
preexec_fn=lambda: os.close(0),
298+
close_fds=True,
299+
)
300+
try:
301+
stdout, stderr = proc.communicate(timeout=12)
302+
except testlib.subprocess.TimeoutExpired:
303+
proc.kill()
304+
proc.wait(timeout=3)
305+
self.fail("Closed STDIN situation was not recognized")
306+
self.assertEqual(1, proc.returncode)
307+
self.assertEqual(stdout, b"")
308+
self.assertIn(
309+
b("Bad file descriptor"),
310+
stderr,
311+
)
312+
313+
def test_closed_stdout(self):
314+
"""The boot command should write an ECO marker to stdout, read the
315+
preamble from stdin, then execute it.
316+
317+
TODO UNDER DISCUSSION
318+
319+
This test writes some data to STDIN and closes it then to create an
320+
EOF situation.
321+
1. Fork child tries to read from STDIN, but stops as EOF is received.
322+
2. Fork child crashes (trying to decompress the junk data)
323+
3. Fork child's file descriptors (write pipes) are closed by the OS
324+
4. Fork parent does `dup(<read pipe>, <stdin>)` and `exec(<python>)`
325+
5. Python reads `b''` (i.e. EOF) from stdin (a closed pipe)
326+
6. Python runs `''` (a valid script) and exits with success"""
327+
328+
stdout_r, stdout_w = mitogen.core.pipe()
329+
mitogen.core.set_cloexec(stdout_r.fileno())
330+
stderr_r, stderr_w = mitogen.core.pipe()
331+
mitogen.core.set_cloexec(stderr_r.fileno())
332+
try:
333+
proc = testlib.subprocess.Popen(
334+
args=self.args,
335+
stdout=stdout_w,
336+
stderr=stderr_w,
337+
preexec_fn=lambda: os.close(0),
338+
)
339+
except Exception:
340+
stdout_r.close()
341+
stdout_w.close()
342+
stderr_w.close()
343+
stderr_r.close()
344+
raise
345+
stdout_w.close()
346+
stderr_w.close()
347+
try:
348+
returncode = proc.wait(timeout=1)
349+
except testlib.subprocess.TimeoutExpired:
350+
proc.kill()
351+
proc.wait(timeout=3)
352+
self.fail("Closed STDOUT situation was not detected")
353+
else:
354+
stderr = stderr_r.read()
355+
finally:
356+
stderr_r.close()
357+
stdout_r.close()
358+
self.assertEqual(1, returncode)
359+
self.assertIn(
360+
b("Bad file descriptor"),
361+
stderr,
362+
)

0 commit comments

Comments
 (0)