| 327 | self.join() |
| 328 | |
| 329 | class SubprocessStreamCapturePlugin(Plugin): |
| 330 | name='subprocstreams' |
| 331 | def __init__(self): |
| 332 | Plugin.__init__(self) |
| 333 | self.stream_capturer = StreamCapturer() |
| 334 | self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture') |
| 335 | # This is ugly, but distant parts of the test machinery need to be able |
| 336 | # to redirect streams, so we make the object globally accessible. |
| 337 | nose.iptest_stdstreams_fileno = self.get_write_fileno |
| 338 | |
| 339 | def get_write_fileno(self): |
| 340 | if self.destination == 'capture': |
| 341 | self.stream_capturer.ensure_started() |
| 342 | return self.stream_capturer.writefd |
| 343 | elif self.destination == 'discard': |
| 344 | return os.open(os.devnull, os.O_WRONLY) |
| 345 | else: |
| 346 | return sys.__stdout__.fileno() |
| 347 | |
| 348 | def configure(self, options, config): |
| 349 | Plugin.configure(self, options, config) |
| 350 | # Override nose trying to disable plugin. |
| 351 | if self.destination == 'capture': |
| 352 | self.enabled = True |
| 353 | |
| 354 | def startTest(self, test): |
| 355 | # Reset log capture |
| 356 | self.stream_capturer.reset_buffer() |
| 357 | |
| 358 | def formatFailure(self, test, err): |
| 359 | # Show output |
| 360 | ec, ev, tb = err |
| 361 | captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace') |
| 362 | if captured.strip(): |
| 363 | ev = safe_str(ev) |
| 364 | out = [ev, '>> begin captured subprocess output <<', |
| 365 | captured, |
| 366 | '>> end captured subprocess output <<'] |
| 367 | return ec, '\n'.join(out), tb |
| 368 | |
| 369 | return err |
| 370 | |
| 371 | formatError = formatFailure |
| 372 | |
| 373 | def finalize(self, result): |
| 374 | self.stream_capturer.halt() |
| 375 | |
| 376 | |
| 377 | def run_iptest(): |