@@ -100,8 +100,11 @@ def __init__(self):
100100 self .next_single_test = None
101101 self .next_single_filename = None
102102
103+ # used by --junit-xml
104+ self .testsuite_xml = None
105+
103106 def accumulate_result (self , test , result ):
104- ok , test_time = result
107+ ok , test_time , xml_data = result
105108 if ok not in (CHILD_ERROR , INTERRUPTED ):
106109 self .test_times .append ((test_time , test ))
107110 if ok == PASSED :
@@ -118,6 +121,15 @@ def accumulate_result(self, test, result):
118121 elif ok != INTERRUPTED :
119122 raise ValueError ("invalid test result: %r" % ok )
120123
124+ if xml_data :
125+ import xml .etree .ElementTree as ET
126+ for e in xml_data :
127+ try :
128+ self .testsuite_xml .append (ET .fromstring (e ))
129+ except ET .ParseError :
130+ print (xml_data , file = sys .__stderr__ )
131+ raise
132+
121133 def display_progress (self , test_index , test ):
122134 if self .ns .quiet :
123135 return
@@ -164,6 +176,9 @@ def parse_args(self, kwargs):
164176 file = sys .stderr )
165177 ns .findleaks = False
166178
179+ if ns .xmlpath :
180+ support .junit_xml_list = self .testsuite_xml = []
181+
167182 # Strip .py extensions.
168183 removepy (ns .args )
169184
@@ -384,7 +399,7 @@ def run_tests_sequential(self):
384399 result = runtest (self .ns , test )
385400 except KeyboardInterrupt :
386401 self .interrupted = True
387- self .accumulate_result (test , (INTERRUPTED , None ))
402+ self .accumulate_result (test , (INTERRUPTED , None , None ))
388403 break
389404 else :
390405 self .accumulate_result (test , result )
@@ -508,6 +523,31 @@ def finalize(self):
508523 if self .ns .runleaks :
509524 os .system ("leaks %d" % os .getpid ())
510525
526+ def save_xml_result (self ):
527+ if not self .ns .xmlpath and not self .testsuite_xml :
528+ return
529+
530+ import xml .etree .ElementTree as ET
531+ root = ET .Element ("testsuites" )
532+
533+ # Manually count the totals for the overall summary
534+ totals = {'tests' : 0 , 'errors' : 0 , 'failures' : 0 }
535+ for suite in self .testsuite_xml :
536+ root .append (suite )
537+ for k in totals :
538+ try :
539+ totals [k ] += int (suite .get (k , 0 ))
540+ except ValueError :
541+ pass
542+
543+ for k , v in totals .items ():
544+ root .set (k , str (v ))
545+
546+ xmlpath = os .path .join (support .SAVEDCWD , self .ns .xmlpath )
547+ with open (xmlpath , 'wb' ) as f :
548+ for s in ET .tostringlist (root ):
549+ f .write (s )
550+
511551 def main (self , tests = None , ** kwargs ):
512552 global TEMPDIR
513553
@@ -570,6 +610,9 @@ def _main(self, tests, kwargs):
570610 self .rerun_failed_tests ()
571611
572612 self .finalize ()
613+
614+ self .save_xml_result ()
615+
573616 if self .bad :
574617 sys .exit (2 )
575618 if self .interrupted :
0 commit comments