comparison tests/testlib/sigpipe-remote.py @ 47557:ed81f2be5527

test: use a python script in `test-transaction-rollback-on-sigpipe.t` This still does not work on Windows, but at least this is a python script now. Differential Revision: https://phab.mercurial-scm.org/D10947
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Fri, 02 Jul 2021 20:20:37 +0200
parents
children df6148ca7120
comparison
equal deleted inserted replaced
47556:640fdb7fd67b 47557:ed81f2be5527
1 #!/usr/bin/env python3
2 from __future__ import print_function
3
4 import os
5 import subprocess
6 import sys
7 import threading
8 import time
9
10 # we cannot use mercurial.testing as long as python2 is not dropped as the test will only install the mercurial module for python2 in python2 run
11
12
13 def _timeout_factor():
14 """return the current modification to timeout"""
15 default = int(os.environ.get('HGTEST_TIMEOUT_DEFAULT', 360))
16 current = int(os.environ.get('HGTEST_TIMEOUT', default))
17 if current == 0:
18 return 1
19 return current / float(default)
20
21
22 def wait_file(path, timeout=10):
23 timeout *= _timeout_factor()
24 start = time.time()
25 while not os.path.exists(path):
26 if (time.time() - start) > timeout:
27 raise RuntimeError(b"timed out waiting for file: %s" % path)
28 time.sleep(0.01)
29
30
31 def write_file(path, content=b''):
32 with open(path, 'wb') as f:
33 f.write(content)
34
35
36 # end of mercurial.testing content
37
38 if sys.version_info[0] < 3:
39 print('SIGPIPE-HELPER: script should run with Python 3', file=sys.stderr)
40 sys.exit(255)
41
42
43 def sysbytes(s):
44 return s.encode('utf-8')
45
46
47 def sysstr(s):
48 return s.decode('latin-1')
49
50
51 piped_stdout = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
52 piped_stderr = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
53
54 stdout_writer = os.fdopen(piped_stdout[1], "rb")
55 stdout_reader = os.fdopen(piped_stdout[0], "rb")
56 stderr_writer = os.fdopen(piped_stderr[1], "rb")
57 stderr_reader = os.fdopen(piped_stderr[0], "rb")
58
59 DEBUG_FILE = os.environ.get('SIGPIPE_REMOTE_DEBUG_FILE')
60 if DEBUG_FILE is None:
61 debug_stream = sys.stderr.buffer
62 else:
63 debug_stream = open(DEBUG_FILE, 'bw', buffering=0)
64
65 SYNCFILE1 = os.environ.get('SYNCFILE1')
66 SYNCFILE2 = os.environ.get('SYNCFILE2')
67 if SYNCFILE1 is None:
68 print('SIGPIPE-HELPER: missing variable $SYNCFILE1', file=sys.stderr)
69 sys.exit(255)
70 if SYNCFILE2 is None:
71 print('SIGPIPE-HELPER: missing variable $SYNCFILE2', file=sys.stderr)
72 sys.exit(255)
73
74 debug_stream.write(b'SIGPIPE-HELPER: Starting\n')
75
76 TESTLIB_DIR = os.path.dirname(sys.argv[0])
77 WAIT_SCRIPT = os.path.join(TESTLIB_DIR, 'wait-on-file')
78
79 hooks_cmd = '%s 10 %s %s'
80 hooks_cmd %= (
81 WAIT_SCRIPT,
82 SYNCFILE2,
83 SYNCFILE1,
84 )
85
86 cmd = ['hg']
87 cmd += sys.argv[1:]
88 sub = subprocess.Popen(
89 cmd,
90 bufsize=0,
91 close_fds=True,
92 stdin=sys.stdin,
93 stdout=stdout_writer,
94 stderr=stderr_writer,
95 )
96
97 debug_stream.write(b'SIGPIPE-HELPER: Mercurial started\n')
98
99
100 shut_down = threading.Event()
101
102 close_lock = threading.Lock()
103
104
105 def _read(stream):
106 try:
107 return stream.read()
108 except ValueError:
109 # read on closed file
110 return None
111
112
113 def forward_stdout():
114 while not shut_down.is_set():
115 c = _read(stdout_reader)
116 while c is not None:
117 sys.stdout.buffer.write(c)
118 c = _read(stdout_reader)
119 time.sleep(0.001)
120 with close_lock:
121 if not stdout_reader.closed:
122 stdout_reader.close()
123 debug_stream.write(b'SIGPIPE-HELPER: stdout closed\n')
124
125
126 def forward_stderr():
127 while not shut_down.is_set():
128 c = _read(stderr_reader)
129 if c is not None:
130 sys.stderr.buffer.write(c)
131 c = _read(stderr_reader)
132 time.sleep(0.001)
133 with close_lock:
134 if not stderr_reader.closed:
135 stderr_reader.close()
136 debug_stream.write(b'SIGPIPE-HELPER: stderr closed\n')
137
138
139 stdout_thread = threading.Thread(target=forward_stdout, daemon=True)
140 stderr_thread = threading.Thread(target=forward_stderr, daemon=True)
141
142 try:
143 stdout_thread.start()
144 stderr_thread.start()
145
146 debug_stream.write(b'SIGPIPE-HELPER: Redirection in place\n')
147
148 try:
149 wait_file(sysbytes(SYNCFILE1))
150 except RuntimeError as exc:
151 msg = sysbytes(str(exc))
152 debug_stream.write(b'SIGPIPE-HELPER: wait failed: %s\n' % msg)
153 else:
154 debug_stream.write(b'SIGPIPE-HELPER: SYNCFILE1 detected\n')
155 with close_lock:
156 if not stdout_reader.closed:
157 stdout_reader.close()
158 if not stderr_reader.closed:
159 stderr_reader.close()
160 sys.stdin.close()
161 debug_stream.write(b'SIGPIPE-HELPER: pipes closed\n')
162 debug_stream.write(b'SIGPIPE-HELPER: creating SYNCFILE2\n')
163 write_file(sysbytes(SYNCFILE2))
164 finally:
165 debug_stream.write(b'SIGPIPE-HELPER: Shutting down\n')
166 shut_down.set()
167 if not sys.stdin.closed:
168 sys.stdin.close()
169 try:
170 sub.wait(timeout=30)
171 except subprocess.TimeoutExpired:
172 msg = b'SIGPIPE-HELPER: Server process failed to terminate\n'
173 debug_stream.write(msg)
174 else:
175 debug_stream.write(b'SIGPIPE-HELPER: Server process terminated\n')
176 debug_stream.write(b'SIGPIPE-HELPER: Shut down\n')