2626import getopt
2727import datetime
2828import subprocess
29- from itertools import chain
3029import logging
3130import yaml
3231
@@ -60,7 +59,7 @@ def usage(file=sys.stdout):
6059""" , file = file )
6160
6261
63- def run_check_tool (executable , args , env = None , cwd = None ):
62+ def run_check_tool (executable , args , cwd = None ):
6463 """Run executable and return `CompletedProcess` instance"""
6564 if executable .startswith ("http://" ) or executable .startswith ("https://" ):
6665 # TODO: When we start supporting this, consider security concerns
@@ -80,7 +79,7 @@ def run_check_tool(executable, args, env=None, cwd=None):
8079 exe .insert (0 , sys .executable )
8180 return subprocess .run (
8281 exe , stdout = subprocess .PIPE , stderr = subprocess .PIPE ,
83- encoding = 'UTF-8' , check = False , env = env , cwd = cwd ,
82+ encoding = 'UTF-8' , check = False , cwd = cwd ,
8483 )
8584
8685
@@ -153,34 +152,8 @@ def select_valid(versions: list) -> list:
153152 return [version for version in versions if version ['_explicit_validity' ]]
154153
155154
156- def invoke_check_tool (exe , args , env , cwd ):
157- """run check tool and return invokation dict to use in the report"""
158- try :
159- compl = run_check_tool (exe , args , env , cwd )
160- except Exception as e :
161- invokation = {
162- "rc" : 127 ,
163- "stdout" : [],
164- "stderr" : [f"CRITICAL: { e !s} " ],
165- }
166- else :
167- invokation = {
168- "rc" : compl .returncode ,
169- "stdout" : compl .stdout .splitlines (),
170- "stderr" : compl .stderr .splitlines (),
171- }
172- for signal in ('info' , 'warning' , 'error' , 'critical' ):
173- invokation [signal ] = len ([
174- line
175- for line in chain (invokation ["stderr" ], invokation ["stdout" ])
176- if line .lower ().startswith (signal )
177- ])
178- return invokation
179-
180-
181- def compute_results (stdout , permissible_ids = ()):
155+ def compute_results (stdout , results , permissible_ids = ()):
182156 """pick out test results from stdout lines"""
183- result = {}
184157 for line in stdout :
185158 parts = line .rsplit (':' , 1 )
186159 if len (parts ) != 2 :
@@ -192,48 +165,40 @@ def compute_results(stdout, permissible_ids=()):
192165 if permissible_ids and testcase_id not in permissible_ids :
193166 logger .warning (f"ignoring invalid result id: { testcase_id } " )
194167 continue
195- result [testcase_id ] = value
196- return result
168+ results [testcase_id ] = value
197169
198170
199171class CheckRunner :
200- def __init__ (self , cwd , assignment , verbosity = 0 ):
172+ def __init__ (self , cwd , assignment , results , log , verbose = False ):
201173 self .cwd = cwd
202174 self .assignment = assignment
203175 self .num_abort = 0
204- self .num_error = 0
205- self .verbosity = verbosity
176+ self .verbose = verbose
206177 self .spamminess = 0
178+ self .results = results
179+ self .log = log
207180
208- def run (self , check , testcases = ()):
209- parameters = check .get ('parameters' , {})
210- assignment = {'testcases' : ' ' .join (testcases ), ** self .assignment , ** parameters }
181+ def run (self , check , testcases ):
182+ assignment = {'testcases' : ' ' .join (testcases ), ** self .assignment }
211183 args = check .get ('args' , '' ).format (** assignment )
212- env = {key : value .format (** assignment ) for key , value in check .get ('env' , {}).items ()}
213- env_str = " " .join (f"{ key } ={ value } " for key , value in env .items ())
214- cmd = f"{ env_str } { check ['executable' ]} { args } " .strip ()
184+ cmd = f"{ check ['executable' ]} { args } " .strip ()
215185 logger .debug (f"running { cmd !r} ..." )
216- check_env = {** os .environ , ** env }
217- invocation = invoke_check_tool (check ["executable" ], args , check_env , self .cwd )
218- invocation = {
219- 'id' : str (uuid .uuid4 ()),
220- 'cmd' : cmd ,
221- 'results' : compute_results (invocation ['stdout' ], permissible_ids = testcases ),
222- ** invocation
223- }
224- if self .verbosity > 1 and invocation ["stdout" ]:
225- print ("\n " .join (invocation ["stdout" ]))
226- self .spamminess += 1
227- # the following check used to be "> 0", but this is quite verbose...
228- if invocation ['rc' ] or self .verbosity > 1 and invocation ["stderr" ]:
229- print ("\n " .join (invocation ["stderr" ]))
186+ self .log .append (f"$ { cmd } " )
187+ try :
188+ compl = run_check_tool (check ["executable" ], args , self .cwd )
189+ except Exception as e :
190+ err_log = f"CRITICAL: { e !s} "
191+ rc = 1
192+ else :
193+ compute_results (compl .stdout .splitlines (), self .results , permissible_ids = testcases )
194+ err_log = compl .stderr .strip ()
195+ rc = compl .returncode
196+ self .num_abort += rc
197+ if rc or self .verbose and err_log :
198+ print (err_log , file = sys .stderr )
230199 self .spamminess += 1
231- logger .debug (f".. rc { invocation ['rc' ]} , { invocation ['critical' ]} critical, { invocation ['error' ]} error" )
232- self .num_abort += invocation ["critical" ]
233- self .num_error += invocation ["error" ]
234- # count failed testcases because they need not be reported redundantly on the error channel
235- self .num_error += len ([value for value in invocation ['results' ].values () if value < 0 ])
236- return invocation
200+ logger .debug (f'.. rc { rc } ' )
201+ self .log .extend (err_log .splitlines ())
237202
238203
239204def print_report (testcase_lookup : dict , tc_ids : list , results : dict , partial = False , verbose = False ):
@@ -267,11 +232,7 @@ def print_report(testcase_lookup: dict, tc_ids: list, results: dict, partial=Fal
267232 print (f" > { testcase ['url' ]} " )
268233
269234
270- def create_report (config , spec , invocations , results ):
271- log = ['running: ' + shlex .join (sys .argv )]
272- for invocation in invocations :
273- log .append (f"$ { invocation ['cmd' ]} " )
274- log .extend (invocation ['stderr' ])
235+ def create_report (config , spec , log , results ):
275236 return {
276237 "uuid" : str (uuid .uuid4 ()),
277238 "creator" : "SCS test suite; version=0.1.0" , # TODO put actual version of test suite here
@@ -294,6 +255,7 @@ def main(argv):
294255 logging .basicConfig (format = '%(levelname)s: %(message)s' , level = logging .INFO )
295256 config = Config ()
296257 config .apply_argv (argv )
258+ check_cwd = os .path .dirname (config .arg0 ) or os .getcwd ()
297259 if not config .subject :
298260 raise RuntimeError ("You need pass --subject=SUBJECT." )
299261 with open (config .arg0 , "r" , encoding = "UTF-8" ) as specfile :
@@ -313,8 +275,6 @@ def main(argv):
313275 raise RuntimeError (f"Requested version '{ config .version } ' not found" )
314276 if not versions :
315277 raise RuntimeError (f"No valid version found for { config .checkdate } " )
316- check_cwd = os .path .dirname (config .arg0 ) or os .getcwd ()
317- runner = CheckRunner (check_cwd , assignment , verbosity = config .verbose and 2 or not config .quiet )
318278 title , partial = spec ['name' ], False
319279 if config .sections :
320280 title += f" [sections: { ', ' .join (config .sections )} ]"
@@ -341,14 +301,13 @@ def main(argv):
341301 idx = script ['_idx' ]
342302 script_tc_ids [idx ].append (tc_id )
343303 # run scripts
344- invocations = [
345- runner .run (script , testcases = sorted (tc_ids ))
346- for script , tc_ids in zip (spec ['scripts' ], script_tc_ids )
347- if tc_ids
348- ]
304+ log = ['running: ' + shlex .join (sys .argv )]
349305 results = {}
350- for invocation in invocations :
351- results .update (invocation ['results' ])
306+ runner = CheckRunner (check_cwd , assignment , results , log , verbose = config .verbose )
307+ for script , tc_ids in zip (spec ['scripts' ], script_tc_ids ):
308+ if not tc_ids :
309+ continue
310+ runner .run (script , sorted (tc_ids ))
352311 # now report:
353312 if config .verbose or not config .output :
354313 # print a horizontal line if we had any script output
@@ -359,10 +318,11 @@ def main(argv):
359318 print (f"- { version ['version' ]} :" , end = ' ' )
360319 print_report (testcase_lookup , version ['testcase_ids' ], results , partial , config .verbose )
361320 if config .output :
362- report = create_report (config , spec , invocations , results )
321+ report = create_report (config , spec , log , results )
363322 with open (config .output , 'w' , encoding = 'UTF-8' ) as fileobj :
364323 yaml .safe_dump (report , fileobj , default_flow_style = False , sort_keys = False , explicit_start = True )
365- return min (127 , runner .num_abort + (0 if config .critical_only else runner .num_error ))
324+ num_error = len ([tc_id for tc_id , value in results .items () if value != 1 ])
325+ return min (127 , runner .num_abort + (0 if config .critical_only else num_error ))
366326
367327
368328if __name__ == "__main__" :
0 commit comments