Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(248)

Side by Side Diff: third_party/grpc/src/python/grpcio/tests/_runner.py

Issue 1932353002: Initial checkin of gRPC to third_party/ Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2015-2016, Google Inc.
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 import cStringIO as StringIO
31 import collections
32 import fcntl
33 import multiprocessing
34 import os
35 import select
36 import signal
37 import sys
38 import threading
39 import time
40 import unittest
41 import uuid
42
43 from tests import _loader
44 from tests import _result
45
46
47 class CapturePipe(object):
48 """A context-manager pipe to redirect output to a byte array.
49
50 Attributes:
51 _redirect_fd (int): File descriptor of file to redirect writes from.
52 _saved_fd (int): A copy of the original value of the redirected file
53 descriptor.
54 _read_thread (threading.Thread or None): Thread upon which reads through the
55 pipe are performed. Only non-None when self is started.
56 _read_fd (int or None): File descriptor of the read end of the redirect
57 pipe. Only non-None when self is started.
58 _write_fd (int or None): File descriptor of the write end of the redirect
59 pipe. Only non-None when self is started.
60 output (bytearray or None): Redirected output from writes to the redirected
61 file descriptor. Only valid during and after self has started.
62 """
63
64 def __init__(self, fd):
65 self._redirect_fd = fd
66 self._saved_fd = os.dup(self._redirect_fd)
67 self._read_thread = None
68 self._read_fd = None
69 self._write_fd = None
70 self.output = None
71
72 def start(self):
73 """Start redirection of writes to the file descriptor."""
74 self._read_fd, self._write_fd = os.pipe()
75 os.dup2(self._write_fd, self._redirect_fd)
76 flags = fcntl.fcntl(self._read_fd, fcntl.F_GETFL)
77 fcntl.fcntl(self._read_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
78 self._read_thread = threading.Thread(target=self._read)
79 self._read_thread.start()
80
81 def stop(self):
82 """Stop redirection of writes to the file descriptor."""
83 os.close(self._write_fd)
84 os.dup2(self._saved_fd, self._redirect_fd) # auto-close self._redirect_fd
85 self._read_thread.join()
86 self._read_thread = None
87 # we waited for the read thread to finish, so _read_fd has been read and we
88 # can close it.
89 os.close(self._read_fd)
90
91 def _read(self):
92 """Read-thread target for self."""
93 self.output = bytearray()
94 while True:
95 select.select([self._read_fd], [], [])
96 read_bytes = os.read(self._read_fd, 1024)
97 if read_bytes:
98 self.output.extend(read_bytes)
99 else:
100 break
101
102 def write_bypass(self, value):
103 """Bypass the redirection and write directly to the original file.
104
105 Arguments:
106 value (str): What to write to the original file.
107 """
108 if self._saved_fd is None:
109 os.write(self._redirect_fd, value)
110 else:
111 os.write(self._saved_fd, value)
112
113 def __enter__(self):
114 self.start()
115 return self
116
117 def __exit__(self, type, value, traceback):
118 self.stop()
119
120 def close(self):
121 """Close any resources used by self not closed by stop()."""
122 os.close(self._saved_fd)
123
124
125 class AugmentedCase(collections.namedtuple('AugmentedCase', [
126 'case', 'id'])):
127 """A test case with a guaranteed unique externally specified identifier.
128
129 Attributes:
130 case (unittest.TestCase): TestCase we're decorating with an additional
131 identifier.
132 id (object): Any identifier that may be considered 'unique' for testing
133 purposes.
134 """
135
136 def __new__(cls, case, id=None):
137 if id is None:
138 id = uuid.uuid4()
139 return super(cls, AugmentedCase).__new__(cls, case, id)
140
141
142 class Runner(object):
143
144 def run(self, suite):
145 """See setuptools' test_runner setup argument for information."""
146 # only run test cases with id starting with given prefix
147 testcase_filter = os.getenv('GRPC_PYTHON_TESTRUNNER_FILTER')
148 filtered_cases = []
149 for case in _loader.iterate_suite_cases(suite):
150 if not testcase_filter or case.id().startswith(testcase_filter):
151 filtered_cases.append(case)
152
153 # Ensure that every test case has no collision with any other test case in
154 # the augmented results.
155 augmented_cases = [AugmentedCase(case, uuid.uuid4())
156 for case in filtered_cases]
157 case_id_by_case = dict((augmented_case.case, augmented_case.id)
158 for augmented_case in augmented_cases)
159 result_out = StringIO.StringIO()
160 result = _result.TerminalResult(
161 result_out, id_map=lambda case: case_id_by_case[case])
162 stdout_pipe = CapturePipe(sys.stdout.fileno())
163 stderr_pipe = CapturePipe(sys.stderr.fileno())
164 kill_flag = [False]
165
166 def sigint_handler(signal_number, frame):
167 if signal_number == signal.SIGINT:
168 kill_flag[0] = True # Python 2.7 not having 'local'... :-(
169 signal.signal(signal_number, signal.SIG_DFL)
170
171 def fault_handler(signal_number, frame):
172 stdout_pipe.write_bypass(
173 'Received fault signal {}\nstdout:\n{}\n\nstderr:{}\n'
174 .format(signal_number, stdout_pipe.output, stderr_pipe.output))
175 os._exit(1)
176
177 def check_kill_self():
178 if kill_flag[0]:
179 stdout_pipe.write_bypass('Stopping tests short...')
180 result.stopTestRun()
181 stdout_pipe.write_bypass(result_out.getvalue())
182 stdout_pipe.write_bypass(
183 '\ninterrupted stdout:\n{}\n'.format(stdout_pipe.output))
184 stderr_pipe.write_bypass(
185 '\ninterrupted stderr:\n{}\n'.format(stderr_pipe.output))
186 os._exit(1)
187 signal.signal(signal.SIGINT, sigint_handler)
188 signal.signal(signal.SIGSEGV, fault_handler)
189 signal.signal(signal.SIGBUS, fault_handler)
190 signal.signal(signal.SIGABRT, fault_handler)
191 signal.signal(signal.SIGFPE, fault_handler)
192 signal.signal(signal.SIGILL, fault_handler)
193 # Sometimes output will lag after a test has successfully finished; we
194 # ignore such writes to our pipes.
195 signal.signal(signal.SIGPIPE, signal.SIG_IGN)
196
197 # Run the tests
198 result.startTestRun()
199 for augmented_case in augmented_cases:
200 sys.stdout.write('Running {}\n'.format(augmented_case.case.id()))
201 sys.stdout.flush()
202 case_thread = threading.Thread(
203 target=augmented_case.case.run, args=(result,))
204 try:
205 with stdout_pipe, stderr_pipe:
206 case_thread.start()
207 while case_thread.is_alive():
208 check_kill_self()
209 time.sleep(0)
210 case_thread.join()
211 except:
212 # re-raise the exception after forcing the with-block to end
213 raise
214 result.set_output(
215 augmented_case.case, stdout_pipe.output, stderr_pipe.output)
216 sys.stdout.write(result_out.getvalue())
217 sys.stdout.flush()
218 result_out.truncate(0)
219 check_kill_self()
220 result.stopTestRun()
221 stdout_pipe.close()
222 stderr_pipe.close()
223
224 # Report results
225 sys.stdout.write(result_out.getvalue())
226 sys.stdout.flush()
227 signal.signal(signal.SIGINT, signal.SIG_DFL)
228 with open('report.xml', 'w') as report_xml_file:
229 _result.jenkins_junit_xml(result).write(report_xml_file)
230 return result
231
OLDNEW
« no previous file with comments | « third_party/grpc/src/python/grpcio/tests/_result.py ('k') | third_party/grpc/src/python/grpcio/tests/interop/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698