|
1 | 1 | import fcntl |
2 | 2 | import functools |
| 3 | +import sys |
3 | 4 | import operator |
4 | 5 | import os |
5 | 6 |
|
@@ -90,6 +91,24 @@ class DummyConnectionNonBlocking(mitogen.parent.Connection): |
90 | 91 | name_prefix = "dummy_non_blocking" |
91 | 92 |
|
92 | 93 |
|
| 94 | +class DummyConnectionClosedStderr(mitogen.parent.Connection): |
| 95 | + """Dummy closed stderr connection""" |
| 96 | + |
| 97 | + pipe_size = 4096 if getattr(fcntl, "F_SETPIPE_SZ", None) else None |
| 98 | + create_child = staticmethod( |
| 99 | + functools.partial( |
| 100 | + own_create_child, |
| 101 | + blocking=True, |
| 102 | + pipe_size=pipe_size, |
| 103 | + pass_stderr=False, |
| 104 | + # `os.close(2)` does not work here as we use file objects in |
| 105 | + # `create_child` and that would cause problems with Python2. |
| 106 | + preexec_fn=lambda: sys.stderr.close(), |
| 107 | + ) |
| 108 | + ) |
| 109 | + name_prefix = "dummy_closed_stderr" |
| 110 | + |
| 111 | + |
93 | 112 | class ConnectionTest(testlib.RouterMixin, testlib.TestCase): |
94 | 113 | def test_stdin_non_blocking(self): |
95 | 114 | """Test that first stage works with non-blocking STDIN |
@@ -129,6 +148,32 @@ def test_stdin_blocking(self): |
129 | 148 | self.assertEqual(3, ctx.call(operator.add, 1, 2)) |
130 | 149 | logs = log.stop() |
131 | 150 |
|
| 151 | + def test_stderr_closed(self): |
| 152 | + """Test that first stage works with closed STDERR |
| 153 | +
|
| 154 | + The boot command should read the preamble from STDIN, write all ECO |
| 155 | + markers to STDOUT, and then execute the preamble. |
| 156 | +
|
| 157 | + This test writes the complete preamble to blocking STDIN. |
| 158 | +
|
| 159 | + 1. Fork child reads from blocking STDIN |
| 160 | + 2. Fork child decompresses the data, does send the handshakes MITO001 and MITO002 |
| 161 | + 3. Fork child crashes (when it tries to close the already closed |
| 162 | + STDERR), but that's non-critical as the parent can read the data |
| 163 | + already written by the fork child. |
| 164 | + 4. Fork child's file descriptors (write pipes) are closed by the OS |
| 165 | + 5. Fork parent does `dup(<read pipe>, <stdin>)` and `exec(<python>)` |
| 166 | + 6. Python reads all data from stdin |
| 167 | + 7. Python runs the preamble code |
| 168 | + 8. A context call works as expected. |
| 169 | +
|
| 170 | + """ |
| 171 | + log = testlib.LogCapturer() |
| 172 | + log.start() |
| 173 | + ctx = self.router._connect(DummyConnectionClosedStderr, connect_timeout=0.5) |
| 174 | + self.assertEqual(3, ctx.call(operator.add, 1, 2)) |
| 175 | + logs = log.stop() |
| 176 | + |
132 | 177 |
|
133 | 178 | class CommandLineTest(testlib.RouterMixin, testlib.TestCase): |
134 | 179 | # Ensure this version of Python produces a command line that is sufficient |
|
0 commit comments