7
7
import sys
8
8
import shutil
9
9
import tempfile
10
+ import junit_xml
10
11
import ruamel .yaml as yaml
11
12
import ruamel .yaml .scanner as yamlscanner
12
13
import pipes
@@ -35,6 +36,22 @@ def format(cls, expected, actual, cause=None):
35
36
return cls (message )
36
37
37
38
39
+ class TestResult (object ):
40
+
41
+ """Encapsulate relevant test result data."""
42
+
43
+ def __init__ (self , return_code , standard_output , error_output ):
44
+ # type: (int, str, str) -> None
45
+ self .return_code = return_code
46
+ self .standard_output = standard_output
47
+ self .error_output = error_output
48
+
49
+ def create_test_case (self , test ):
50
+ # type: (Dict[Text, Any]) -> junit_xml.TestCase
51
+ doc = test .get (u'doc' , 'N/A' ).strip ()
52
+ return junit_xml .TestCase (doc , stdout = self .standard_output , stderr = self .error_output )
53
+
54
+
38
55
def compare_file (expected , actual ):
39
56
# type: (Dict[str,Any], Dict[str,Any]) -> None
40
57
if "path" in expected :
@@ -127,9 +144,9 @@ def compare(expected, actual): # type: (Any, Any) -> None
127
144
raise CompareFail (str (e ))
128
145
129
146
130
- def run_test (args , i , tests ): # type: (argparse.Namespace, int, List[Dict[str, str]]) -> int
147
+ def run_test (args , i , tests ): # type: (argparse.Namespace, int, List[Dict[str, str]]) -> TestResult
131
148
out = {} # type: Dict[str,Any]
132
- outdir , outstr , test_command = None , None , None
149
+ outdir = outstr = outerr = test_command = None
133
150
t = tests [i ]
134
151
try :
135
152
test_command = [args .tool ]
@@ -148,23 +165,32 @@ def run_test(args, i, tests): # type: (argparse.Namespace, int, List[Dict[str,
148
165
149
166
sys .stderr .write ("\r Test [%i/%i] " % (i + 1 , len (tests )))
150
167
sys .stderr .flush ()
151
- outstr = subprocess .check_output (test_command )
168
+
169
+ process = subprocess .Popen (test_command , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
170
+ outstr , outerr = process .communicate ()
171
+ return_code = process .poll ()
172
+ if return_code :
173
+ raise subprocess .CalledProcessError (return_code , " " .join (test_command ))
174
+
152
175
out = json .loads (outstr )
153
176
except ValueError as v :
154
177
_logger .error (str (v ))
155
178
_logger .error (outstr )
179
+ _logger .error (outerr )
156
180
except subprocess .CalledProcessError as err :
157
181
if err .returncode == UNSUPPORTED_FEATURE :
158
- return UNSUPPORTED_FEATURE
182
+ return TestResult ( UNSUPPORTED_FEATURE , outstr , outerr )
159
183
else :
160
184
_logger .error (u"""Test failed: %s""" , " " .join ([pipes .quote (tc ) for tc in test_command ]))
161
185
_logger .error (t .get ("doc" ))
162
186
_logger .error ("Returned non-zero" )
163
- return 1
187
+ _logger .error (outerr )
188
+ return TestResult (1 , outstr , outerr )
164
189
except (yamlscanner .ScannerError , TypeError ) as e :
165
190
_logger .error (u"""Test failed: %s""" , " " .join ([pipes .quote (tc ) for tc in test_command ]))
166
191
_logger .error (outstr )
167
192
_logger .error (u"Parse error %s" , str (e ))
193
+ _logger .error (outerr )
168
194
except KeyboardInterrupt :
169
195
_logger .error (u"""Test interrupted: %s""" , " " .join ([pipes .quote (tc ) for tc in test_command ]))
170
196
raise
@@ -182,10 +208,7 @@ def run_test(args, i, tests): # type: (argparse.Namespace, int, List[Dict[str,
182
208
if outdir :
183
209
shutil .rmtree (outdir , True )
184
210
185
- if failed :
186
- return 1
187
- else :
188
- return 0
211
+ return TestResult ((1 if failed else 0 ), outstr , outerr )
189
212
190
213
191
214
def main (): # type: () -> int
@@ -215,7 +238,8 @@ def main(): # type: () -> int
215
238
failures = 0
216
239
unsupported = 0
217
240
passed = 0
218
- xml_lines = [] # type: List[Text]
241
+ suite_name , _ = os .path .splitext (os .path .basename (args .test ))
242
+ report = junit_xml .TestSuite (suite_name , [])
219
243
220
244
if args .only_tools :
221
245
alltests = tests
@@ -251,28 +275,26 @@ def main(): # type: () -> int
251
275
for i in ntest ]
252
276
try :
253
277
for i , job in zip (ntest , jobs ):
254
- rt = job .result ()
278
+ test_result = job .result ()
279
+ test_case = test_result .create_test_case (tests [i ])
255
280
total += 1
256
- if rt == 1 :
281
+ if test_result . return_code == 1 :
257
282
failures += 1
258
- elif rt == UNSUPPORTED_FEATURE :
283
+ test_case .add_failure_info ("N/A" )
284
+ elif test_result .return_code == UNSUPPORTED_FEATURE :
259
285
unsupported += 1
286
+ test_case .add_skipped_info ("Unsupported" )
260
287
else :
261
288
passed += 1
262
- xml_lines += make_xml_lines ( tests [ i ], rt , args . test )
289
+ report . test_cases . append ( test_case )
263
290
except KeyboardInterrupt :
264
291
for job in jobs :
265
292
job .cancel ()
266
293
_logger .error ("Tests interrupted" )
267
294
268
295
if args .junit_xml :
269
296
with open (args .junit_xml , 'w' ) as fp :
270
- fp .write ('<testsuites>\n ' )
271
- fp .write (' <testsuite name="%s" tests="%s" failures="%s" skipped="%s">\n ' % (
272
- args .tool , len (ntest ), failures , unsupported ))
273
- fp .writelines (xml_lines )
274
- fp .write (' </testsuite>\n ' )
275
- fp .write ('</testsuites>\n ' )
297
+ junit_xml .TestSuite .to_file (fp , [report ])
276
298
277
299
if failures == 0 and unsupported == 0 :
278
300
_logger .info ("All tests passed" )
@@ -285,25 +307,5 @@ def main(): # type: () -> int
285
307
return 1
286
308
287
309
288
- def make_xml_lines (test , rt , test_case_group = 'N/A' ):
289
- # type: (Dict[Text, Any], int, Text) -> List[Text]
290
- doc = test .get ('doc' , 'N/A' ).strip ()
291
- test_case_group = test_case_group .replace (".yaml" , "" ).replace (".yml" , "" )
292
- elem = ' <testcase name="%s" classname="%s"' % (doc , test_case_group .replace ("." , "_" ))
293
- if rt == 0 :
294
- return [ elem + '/>\n ' ]
295
- if rt == UNSUPPORTED_FEATURE :
296
- return [
297
- elem + '>\n ' ,
298
- ' <skipped/>\n '
299
- '</testcase>\n ' ,
300
- ]
301
- return [
302
- elem + '>\n ' ,
303
- ' <failure message="N/A">N/A</failure>\n '
304
- '</testcase>\n ' ,
305
- ]
306
-
307
-
308
310
if __name__ == "__main__" :
309
311
sys .exit (main ())
0 commit comments