|  | @@ -34,10 +34,12 @@ import multiprocessing
 | 
	
		
			
				|  |  |  import os
 | 
	
		
			
				|  |  |  import platform
 | 
	
		
			
				|  |  |  import signal
 | 
	
		
			
				|  |  | +import string
 | 
	
		
			
				|  |  |  import subprocess
 | 
	
		
			
				|  |  |  import sys
 | 
	
		
			
				|  |  |  import tempfile
 | 
	
		
			
				|  |  |  import time
 | 
	
		
			
				|  |  | +import xml.etree.cElementTree as ET
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  _DEFAULT_MAX_JOBS = 16 * multiprocessing.cpu_count()
 | 
	
	
		
			
				|  | @@ -159,7 +161,7 @@ class JobSpec(object):
 | 
	
		
			
				|  |  |  class Job(object):
 | 
	
		
			
				|  |  |    """Manages one job."""
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  def __init__(self, spec, bin_hash, newline_on_success, travis):
 | 
	
		
			
				|  |  | +  def __init__(self, spec, bin_hash, newline_on_success, travis, xml_report):
 | 
	
		
			
				|  |  |      self._spec = spec
 | 
	
		
			
				|  |  |      self._bin_hash = bin_hash
 | 
	
		
			
				|  |  |      self._tempfile = tempfile.TemporaryFile()
 | 
	
	
		
			
				|  | @@ -176,19 +178,27 @@ class Job(object):
 | 
	
		
			
				|  |  |      self._state = _RUNNING
 | 
	
		
			
				|  |  |      self._newline_on_success = newline_on_success
 | 
	
		
			
				|  |  |      self._travis = travis
 | 
	
		
			
				|  |  | +    self._xml_test = ET.SubElement(xml_report, 'testcase',
 | 
	
		
			
				|  |  | +                                   name=self._spec.shortname) if xml_report is not None else None
 | 
	
		
			
				|  |  |      message('START', spec.shortname, do_newline=self._travis)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def state(self, update_cache):
 | 
	
		
			
				|  |  |      """Poll current state of the job. Prints messages at completion."""
 | 
	
		
			
				|  |  |      if self._state == _RUNNING and self._process.poll() is not None:
 | 
	
		
			
				|  |  |        elapsed = time.time() - self._start
 | 
	
		
			
				|  |  | +      self._tempfile.seek(0)
 | 
	
		
			
				|  |  | +      stdout = self._tempfile.read()
 | 
	
		
			
				|  |  | +      filtered_stdout = filter(lambda x: x in string.printable, stdout.decode(errors='ignore'))
 | 
	
		
			
				|  |  | +      if self._xml_test is not None:
 | 
	
		
			
				|  |  | +        self._xml_test.set('time', str(elapsed))
 | 
	
		
			
				|  |  | +        ET.SubElement(self._xml_test, 'system-out').text = filtered_stdout
 | 
	
		
			
				|  |  |        if self._process.returncode != 0:
 | 
	
		
			
				|  |  |          self._state = _FAILURE
 | 
	
		
			
				|  |  | -        self._tempfile.seek(0)
 | 
	
		
			
				|  |  | -        stdout = self._tempfile.read()
 | 
	
		
			
				|  |  |          message('FAILED', '%s [ret=%d, pid=%d]' % (
 | 
	
		
			
				|  |  |              self._spec.shortname, self._process.returncode, self._process.pid),
 | 
	
		
			
				|  |  |              stdout, do_newline=True)
 | 
	
		
			
				|  |  | +        if self._xml_test is not None:
 | 
	
		
			
				|  |  | +          ET.SubElement(self._xml_test, 'failure', message='Failure').text
 | 
	
		
			
				|  |  |        else:
 | 
	
		
			
				|  |  |          self._state = _SUCCESS
 | 
	
		
			
				|  |  |          message('PASSED', '%s [time=%.1fsec]' % (self._spec.shortname, elapsed),
 | 
	
	
		
			
				|  | @@ -200,6 +210,9 @@ class Job(object):
 | 
	
		
			
				|  |  |        stdout = self._tempfile.read()
 | 
	
		
			
				|  |  |        message('TIMEOUT', self._spec.shortname, stdout, do_newline=True)
 | 
	
		
			
				|  |  |        self.kill()
 | 
	
		
			
				|  |  | +      if self._xml_test is not None:
 | 
	
		
			
				|  |  | +        ET.SubElement(self._xml_test, 'system-out').text = stdout
 | 
	
		
			
				|  |  | +        ET.SubElement(self._xml_test, 'error', message='Timeout')
 | 
	
		
			
				|  |  |      return self._state
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def kill(self):
 | 
	
	
		
			
				|  | @@ -212,7 +225,7 @@ class Jobset(object):
 | 
	
		
			
				|  |  |    """Manages one run of jobs."""
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def __init__(self, check_cancelled, maxjobs, newline_on_success, travis,
 | 
	
		
			
				|  |  | -               stop_on_failure, cache):
 | 
	
		
			
				|  |  | +               stop_on_failure, cache, xml_report):
 | 
	
		
			
				|  |  |      self._running = set()
 | 
	
		
			
				|  |  |      self._check_cancelled = check_cancelled
 | 
	
		
			
				|  |  |      self._cancelled = False
 | 
	
	
		
			
				|  | @@ -224,6 +237,7 @@ class Jobset(object):
 | 
	
		
			
				|  |  |      self._cache = cache
 | 
	
		
			
				|  |  |      self._stop_on_failure = stop_on_failure
 | 
	
		
			
				|  |  |      self._hashes = {}
 | 
	
		
			
				|  |  | +    self._xml_report = xml_report
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def start(self, spec):
 | 
	
		
			
				|  |  |      """Start a job. Return True on success, False on failure."""
 | 
	
	
		
			
				|  | @@ -250,7 +264,8 @@ class Jobset(object):
 | 
	
		
			
				|  |  |          self._running.add(Job(spec,
 | 
	
		
			
				|  |  |                                bin_hash,
 | 
	
		
			
				|  |  |                                self._newline_on_success,
 | 
	
		
			
				|  |  | -                              self._travis))
 | 
	
		
			
				|  |  | +                              self._travis,
 | 
	
		
			
				|  |  | +                              self._xml_report))
 | 
	
		
			
				|  |  |        except:
 | 
	
		
			
				|  |  |          message('FAILED', spec.shortname)
 | 
	
		
			
				|  |  |          self._cancelled = True
 | 
	
	
		
			
				|  | @@ -324,11 +339,13 @@ def run(cmdlines,
 | 
	
		
			
				|  |  |          travis=False,
 | 
	
		
			
				|  |  |          infinite_runs=False,
 | 
	
		
			
				|  |  |          stop_on_failure=False,
 | 
	
		
			
				|  |  | -        cache=None):
 | 
	
		
			
				|  |  | +        cache=None,
 | 
	
		
			
				|  |  | +        xml_report=None):
 | 
	
		
			
				|  |  |    js = Jobset(check_cancelled,
 | 
	
		
			
				|  |  |                maxjobs if maxjobs is not None else _DEFAULT_MAX_JOBS,
 | 
	
		
			
				|  |  |                newline_on_success, travis, stop_on_failure,
 | 
	
		
			
				|  |  | -              cache if cache is not None else NoCache())
 | 
	
		
			
				|  |  | +              cache if cache is not None else NoCache(),
 | 
	
		
			
				|  |  | +              xml_report)
 | 
	
		
			
				|  |  |    for cmdline in cmdlines:
 | 
	
		
			
				|  |  |      if not js.start(cmdline):
 | 
	
		
			
				|  |  |        break
 |