Patchwork [3,of,4] tests: make pipes / PTYs non-inheritable in test-stdio.py

login
register
mail settings
Submitter Manuel Jacob
Date July 17, 2020, 2:38 a.m.
Message ID <91f6d0508acac2c8eb0f.1594953529@tmp>
Download mbox | patch
Permalink /patch/46771/
State Accepted
Headers show

Comments

Manuel Jacob - July 17, 2020, 2:38 a.m.
# HG changeset patch
# User Manuel Jacob <me@manueljacob.de>
# Date 1594939053 -7200
#      Fri Jul 17 00:37:33 2020 +0200
# Node ID 91f6d0508acac2c8eb0fbf8864528f8f584e697c
# Parent  90b4881b969cb70d68c8b5248e784987d4c70de4
# EXP-Topic stdio-broken_pipe
tests: make pipes / PTYs non-inheritable in test-stdio.py

A following patch requires that to test closing the receiving end of the pipe /
PTYs.

Even for existing tests, it might be safer to make the lifetime of the pipes /
PTYs as short as possible.

Patch

diff --git a/tests/test-stdio.py b/tests/test-stdio.py
--- a/tests/test-stdio.py
+++ b/tests/test-stdio.py
@@ -16,6 +16,25 @@ 
 from mercurial import pycompat
 
 
+if pycompat.ispy3:
+
+    def set_noninheritable(fd):
+        # On Python 3, file descriptors are non-inheritable by default.
+        pass
+
+
+else:
+    if pycompat.iswindows:
+        # unused
+        set_noninheritable = None
+    else:
+        import fcntl
+
+        def set_noninheritable(fd):
+            old = fcntl.fcntl(fd, fcntl.F_GETFD)
+            fcntl.fcntl(fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
+
+
 TEST_BUFFERING_CHILD_SCRIPT = r'''
 import os
 
@@ -64,9 +83,15 @@ 
                 pass
 
 
+# In the following, we set the FDs non-inheritable mainly to make it possible
+# for tests to close the receiving end of the pipe / PTYs.
+
+
 @contextlib.contextmanager
 def _devnull():
     devnull = os.open(os.devnull, os.O_WRONLY)
+    # We don't have a receiving end, so it's not worth the effort on Python 2
+    # on Windows to make the FD non-inheritable.
     with _closing([devnull]):
         yield (None, devnull)
 
@@ -74,6 +99,10 @@ 
 @contextlib.contextmanager
 def _pipes():
     rwpair = os.pipe()
+    # Pipes are already non-inheritable on Windows.
+    if not pycompat.iswindows:
+        set_noninheritable(rwpair[0])
+        set_noninheritable(rwpair[1])
     with _closing(rwpair):
         yield rwpair
 
@@ -86,6 +115,8 @@ 
     import tty
 
     rwpair = pty.openpty()
+    set_noninheritable(rwpair[0])
+    set_noninheritable(rwpair[1])
     with _closing(rwpair):
         tty.setraw(rwpair[0])
         yield rwpair