7
7
import json
8
8
import logging
9
9
import os
10
- import pipes
11
10
import shutil
12
11
import sys
13
12
import tempfile
14
13
import threading
15
14
import time
15
+ from typing import Any , Dict , List , Optional , Text
16
+ from concurrent .futures import ThreadPoolExecutor
16
17
17
18
import ruamel .yaml as yaml
18
19
import ruamel .yaml .scanner as yamlscanner
19
20
import schema_salad .ref_resolver
20
- from concurrent .futures import ThreadPoolExecutor
21
21
from six .moves import range
22
22
from six .moves import zip
23
- from typing import Any , Dict , List
24
23
import pkg_resources # part of setuptools
25
24
26
25
import junit_xml
27
- from cwltest .utils import compare , CompareFail , TestResult , REQUIRED , get_test_number_by_key
26
+ from cwltest .utils import (compare , CompareFail , TestResult , REQUIRED ,
27
+ get_test_number_by_key )
28
28
29
29
_logger = logging .getLogger ("cwltest" )
30
30
_logger .addHandler (logging .StreamHandler ())
31
31
_logger .setLevel (logging .INFO )
32
32
33
33
UNSUPPORTED_FEATURE = 33
34
- DEFAULT_TIMEOUT = 900 # 15 minutes
34
+ DEFAULT_TIMEOUT = 600 # 10 minutes
35
35
36
36
if sys .version_info < (3 , 0 ):
37
37
import subprocess32 as subprocess
38
+ from pipes import quote
38
39
else :
39
40
import subprocess
41
+ from shlex import quote
40
42
41
43
templock = threading .Lock ()
42
44
43
45
44
- def prepare_test_command (args , i , tests ):
45
- # type: (argparse.Namespace, int, List[Dict[str, str]]) -> List[str]
46
- t = tests [i ]
47
- test_command = [args .tool ]
48
- test_command .extend (args .args )
46
+ def prepare_test_command (tool , # type: str
47
+ args , # type: List[str]
48
+ testargs , # type: Optional[List[str]]
49
+ test # type: Dict[str, str]
50
+ ): # type: (...) -> List[str]
51
+ """ Turn the test into a command line. """
52
+ test_command = [tool ]
53
+ test_command .extend (args )
49
54
50
55
# Add additional arguments given in test case
51
- if args . testargs is not None :
52
- for testarg in args . testargs :
56
+ if testargs is not None :
57
+ for testarg in testargs :
53
58
(test_case_name , prefix ) = testarg .split ('==' )
54
- if test_case_name in t :
55
- test_command .extend ([prefix , t [test_case_name ]])
59
+ if test_case_name in test :
60
+ test_command .extend ([prefix , test [test_case_name ]])
56
61
57
62
# Add prefixes if running on MacOSX so that boot2docker writes to /Users
58
63
with templock :
59
- if 'darwin' in sys .platform and args . tool == 'cwltool' :
64
+ if 'darwin' in sys .platform and tool == 'cwltool' :
60
65
outdir = tempfile .mkdtemp (prefix = os .path .abspath (os .path .curdir ))
61
- test_command .extend (["--tmp-outdir-prefix={}" .format (outdir ), "--tmpdir-prefix={}" .format (outdir )])
66
+ test_command .extend (["--tmp-outdir-prefix={}" .format (outdir ),
67
+ "--tmpdir-prefix={}" .format (outdir )])
62
68
else :
63
69
outdir = tempfile .mkdtemp ()
64
70
test_command .extend (["--outdir={}" .format (outdir ),
65
71
"--quiet" ,
66
- t ["tool" ]])
67
- if t .get ("job" ):
68
- test_command .append (t ["job" ])
72
+ os . path . normcase ( test ["tool" ]) ])
73
+ if test .get ("job" ):
74
+ test_command .append (os . path . normcase ( test ["job" ]) )
69
75
return test_command
70
76
71
77
72
- def run_test (args , i , tests , timeout ):
73
- # type: (argparse.Namespace, int, List[Dict[str, str]], int) -> TestResult
78
+ def run_test (args , # type: argparse.Namespace
79
+ test , # type: Dict[str, str]
80
+ test_number , # type: int
81
+ total_tests , # type: int
82
+ timeout # type: int
83
+ ): # type: (...) -> TestResult
74
84
75
85
global templock
76
86
77
87
out = {} # type: Dict[str,Any]
78
- outdir = outstr = outerr = test_command = None
88
+ outdir = outstr = outerr = None
89
+ test_command = [] # type: List[str]
79
90
duration = 0.0
80
- t = tests [i ]
81
91
prefix = ""
82
92
suffix = ""
83
93
if sys .stderr .isatty ():
@@ -86,12 +96,18 @@ def run_test(args, i, tests, timeout):
86
96
suffix = "\n "
87
97
try :
88
98
process = None # type: subprocess.Popen
89
- test_command = prepare_test_command (args , i , tests )
90
-
91
- if t .get ("short_name" ):
92
- sys .stderr .write ("%sTest [%i/%i] %s: %s%s\n " % (prefix , i + 1 , len (tests ), t .get ("short_name" ), t .get ("doc" ), suffix ))
99
+ test_command = prepare_test_command (
100
+ args .tool , args .args , args .testargs , test )
101
+
102
+ if test .get ("short_name" ):
103
+ sys .stderr .write (
104
+ "%sTest [%i/%i] %s: %s%s\n "
105
+ % (prefix , test_number , total_tests , test .get ("short_name" ),
106
+ test .get ("doc" ), suffix ))
93
107
else :
94
- sys .stderr .write ("%sTest [%i/%i] %s%s\n " % (prefix , i + 1 , len (tests ), t .get ("doc" ), suffix ))
108
+ sys .stderr .write (
109
+ "%sTest [%i/%i] %s%s\n "
110
+ % (prefix , test_number , total_tests , test .get ("doc" ), suffix ))
95
111
sys .stderr .flush ()
96
112
97
113
start_time = time .time ()
@@ -104,38 +120,40 @@ def run_test(args, i, tests, timeout):
104
120
raise subprocess .CalledProcessError (return_code , " " .join (test_command ))
105
121
106
122
out = json .loads (outstr )
107
- except ValueError as v :
108
- _logger .error (str (v ))
123
+ except ValueError as err :
124
+ _logger .error (str (err ))
109
125
_logger .error (outstr )
110
126
_logger .error (outerr )
111
127
except subprocess .CalledProcessError as err :
112
128
if err .returncode == UNSUPPORTED_FEATURE :
113
129
return TestResult (UNSUPPORTED_FEATURE , outstr , outerr , duration , args .classname )
114
- elif t .get ("should_fail" , False ):
130
+ if test .get ("should_fail" , False ):
115
131
return TestResult (0 , outstr , outerr , duration , args .classname )
116
- else :
117
- _logger .error (u"""Test failed: %s""" , " " . join ([ pipes . quote ( tc ) for tc in test_command ] ))
118
- _logger .error (t . get ( "doc" ) )
119
- _logger .error ("Returned non-zero" )
120
- _logger . error ( outerr )
121
- return TestResult ( 1 , outstr , outerr , duration , args . classname , str ( err ))
122
- except ( yamlscanner . ScannerError , TypeError ) as e :
123
- _logger . error ( u"""Test failed: %s""" , " " .join ([pipes . quote (tc ) for tc in test_command ]))
132
+ _logger . error ( u"""Test failed: %s""" , " " . join ([ quote ( tc ) for tc in test_command ]))
133
+ _logger .error (test . get ( "doc" ))
134
+ _logger .error (u"Returned non-zero" )
135
+ _logger .error (outerr )
136
+ return TestResult ( 1 , outstr , outerr , duration , args . classname , str ( err ) )
137
+ except ( yamlscanner . ScannerError , TypeError ) as err :
138
+ _logger . error ( u"""Test failed: %s""" ,
139
+ u " " .join ([quote (tc ) for tc in test_command ]))
124
140
_logger .error (outstr )
125
- _logger .error (u"Parse error %s" , str (e ))
141
+ _logger .error (u"Parse error %s" , str (err ))
126
142
_logger .error (outerr )
127
143
except KeyboardInterrupt :
128
- _logger .error (u"""Test interrupted: %s""" , " " .join ([pipes .quote (tc ) for tc in test_command ]))
144
+ _logger .error (u"""Test interrupted: %s""" ,
145
+ u" " .join ([quote (tc ) for tc in test_command ]))
129
146
raise
130
147
except subprocess .TimeoutExpired :
131
- _logger .error (u"""Test timed out: %s""" , " " .join ([pipes .quote (tc ) for tc in test_command ]))
132
- _logger .error (t .get ("doc" ))
148
+ _logger .error (u"""Test timed out: %s""" ,
149
+ u" " .join ([quote (tc ) for tc in test_command ]))
150
+ _logger .error (test .get ("doc" ))
133
151
return TestResult (2 , outstr , outerr , timeout , args .classname , "Test timed out" )
134
152
finally :
135
153
if process is not None and process .returncode is None :
136
154
_logger .error (u"""Terminating lingering process""" )
137
155
process .terminate ()
138
- for a in range (0 , 3 ):
156
+ for _ in range (0 , 3 ):
139
157
time .sleep (1 )
140
158
if process .poll () is not None :
141
159
break
@@ -144,24 +162,25 @@ def run_test(args, i, tests, timeout):
144
162
145
163
fail_message = ''
146
164
147
- if t .get ("should_fail" , False ):
148
- _logger .warning (u"""Test failed: %s""" , " " .join ([pipes . quote (tc ) for tc in test_command ]))
149
- _logger .warning (t .get ("doc" ))
165
+ if test .get ("should_fail" , False ):
166
+ _logger .warning (u"""Test failed: %s""" , u " " .join ([quote (tc ) for tc in test_command ]))
167
+ _logger .warning (test .get ("doc" ))
150
168
_logger .warning (u"Returned zero but it should be non-zero" )
151
169
return TestResult (1 , outstr , outerr , duration , args .classname )
152
170
153
171
try :
154
- compare (t .get ("output" ), out )
172
+ compare (test .get ("output" ), out )
155
173
except CompareFail as ex :
156
- _logger .warning (u"""Test failed: %s""" , " " .join ([pipes . quote (tc ) for tc in test_command ]))
157
- _logger .warning (t .get ("doc" ))
174
+ _logger .warning (u"""Test failed: %s""" , u " " .join ([quote (tc ) for tc in test_command ]))
175
+ _logger .warning (test .get ("doc" ))
158
176
_logger .warning (u"Compare failure %s" , ex )
159
177
fail_message = str (ex )
160
178
161
179
if outdir :
162
180
shutil .rmtree (outdir , True )
163
181
164
- return TestResult ((1 if fail_message else 0 ), outstr , outerr , duration , args .classname , fail_message )
182
+ return TestResult ((1 if fail_message else 0 ), outstr , outerr , duration ,
183
+ args .classname , fail_message )
165
184
166
185
167
186
def arg_parser (): # type: () -> argparse.ArgumentParser
@@ -175,17 +194,18 @@ def arg_parser(): # type: () -> argparse.ArgumentParser
175
194
help = "CWL runner executable to use (default 'cwl-runner'" )
176
195
parser .add_argument ("--only-tools" , action = "store_true" , help = "Only test CommandLineTools" )
177
196
parser .add_argument ("--junit-xml" , type = str , default = None , help = "Path to JUnit xml file" )
178
- parser .add_argument ("--test-arg" , type = str , help = "Additional argument given in test cases and "
179
- " required prefix for tool runner." ,
180
- metavar = "cache==--cache-dir" , action = "append" , dest = "testargs" )
197
+ parser .add_argument ("--test-arg" , type = str , help = "Additional argument "
198
+ "given in test cases and required prefix for tool runner." ,
199
+ default = None , metavar = "cache==--cache-dir" , action = "append" , dest = "testargs" )
181
200
parser .add_argument ("args" , help = "arguments to pass first to tool runner" , nargs = argparse .REMAINDER )
182
201
parser .add_argument ("-j" , type = int , default = 1 , help = "Specifies the number of tests to run simultaneously "
183
202
"(defaults to one)." )
184
203
parser .add_argument ("--verbose" , action = "store_true" , help = "More verbose output during test run." )
185
204
parser .add_argument ("--classname" , type = str , default = "" , help = "Specify classname for the Test Suite." )
186
- parser .add_argument ("--timeout" , type = int , default = DEFAULT_TIMEOUT , help = "Time of execution in seconds after "
187
- "which the test will be skipped. "
188
- "Defaults to 900 sec (15 minutes)" )
205
+ parser .add_argument ("--timeout" , type = int , default = DEFAULT_TIMEOUT ,
206
+ help = "Time of execution in seconds after which the test will be "
207
+ "skipped. Defaults to {} seconds ({} minutes)." .format (
208
+ DEFAULT_TIMEOUT , DEFAULT_TIMEOUT / 60 ))
189
209
190
210
pkg = pkg_resources .require ("cwltest" )
191
211
if pkg :
@@ -256,14 +276,14 @@ def main(): # type: () -> int
256
276
if test_number :
257
277
ntest .append (test_number )
258
278
else :
259
- _logger .error ('Test with short name "%s" not found ' % s )
279
+ _logger .error ('Test with short name "%s" not found ' , s )
260
280
return 1
261
281
else :
262
282
ntest = list (range (0 , len (tests )))
263
283
264
284
total = 0
265
285
with ThreadPoolExecutor (max_workers = args .j ) as executor :
266
- jobs = [executor .submit (run_test , args , i , tests , args .timeout )
286
+ jobs = [executor .submit (run_test , args , tests [ i ], i + 1 , len ( tests ) , args .timeout )
267
287
for i in ntest ]
268
288
try :
269
289
for i , job in zip (ntest , jobs ):
@@ -294,18 +314,19 @@ def main(): # type: () -> int
294
314
_logger .error ("Tests interrupted" )
295
315
296
316
if args .junit_xml :
297
- with open (args .junit_xml , 'w' ) as fp :
298
- junit_xml .TestSuite .to_file (fp , [report ])
317
+ with open (args .junit_xml , 'w' ) as xml :
318
+ junit_xml .TestSuite .to_file (xml , [report ])
299
319
300
320
if failures == 0 and unsupported == 0 :
301
321
_logger .info ("All tests passed" )
302
322
return 0
303
- elif failures == 0 and unsupported > 0 :
304
- _logger .warning ("%i tests passed, %i unsupported features" , total - unsupported , unsupported )
323
+ if failures == 0 and unsupported > 0 :
324
+ _logger .warning ("%i tests passed, %i unsupported features" ,
325
+ total - unsupported , unsupported )
305
326
return 0
306
- else :
307
- _logger . warning ( "%i tests passed, %i failures, %i unsupported features" , total - (failures + unsupported ), failures , unsupported )
308
- return 1
327
+ _logger . warning ( "%i tests passed, %i failures, %i unsupported features" ,
328
+ total - (failures + unsupported ), failures , unsupported )
329
+ return 1
309
330
310
331
311
332
if __name__ == "__main__" :
0 commit comments