13 fmtr_class
= argparse
.ArgumentDefaultsHelpFormatter
14 parser
= argparse
.ArgumentParser(prog
= 'nasm-t.py',
15 formatter_class
=fmtr_class
)
17 parser
.add_argument('-d', '--directory',
18 dest
= 'dir', default
= './travis/test',
19 help = 'Directory with tests')
21 parser
.add_argument('--nasm',
22 dest
= 'nasm', default
= './nasm',
23 help = 'Nasm executable to use')
25 parser
.add_argument('--hexdump',
26 dest
= 'hexdump', default
= '/usr/bin/hexdump',
27 help = 'Hexdump executable to use')
29 sp
= parser
.add_subparsers(dest
= 'cmd')
31 spp
= sp
.add_parser(cmd
, help = 'Run test cases')
32 spp
.add_argument('-t', '--test',
34 help = 'Run the selected test only',
38 spp
= sp
.add_parser(cmd
, help = 'List test cases')
40 for cmd
in ['update']:
41 spp
= sp
.add_parser(cmd
, help = 'Update test cases with new compiler')
42 spp
.add_argument('-t', '--test',
44 help = 'Update the selected test only',
47 args
= parser
.parse_args()
53 def read_stdfile(path
):
54 with
open(path
, "rb") as f
:
55 data
= f
.read().decode("utf-8").strip("\n")
60 # Check if descriptor has mandatory fields
61 def is_valid_desc(desc
):
64 if 'description' not in desc
:
66 if desc
['description'] == "":
71 # Expand ref/id in descriptors array
72 def expand_templates(desc_array
):
77 for i
, d
in enumerate(desc_array
):
78 if 'ref' in d
and d
['ref'] in desc_ids
:
79 ref
= desc_ids
[d
['ref']]
81 desc_array
[i
] = ref
.copy()
82 for k
, v
in own
.items():
84 del desc_array
[i
]['id']
87 def prepare_desc(desc
, basedir
, name
, path
):
88 if not is_valid_desc(desc
):
92 desc
['_base-dir'] = basedir
93 desc
['_json-file'] = name
94 desc
['_json-path'] = path
95 desc
['_test-name'] = basedir
+ os
.sep
+ name
[:-5]
97 # If no target provided never update
98 if 'target' not in desc
:
100 desc
['update'] = 'false'
102 # Which code to expect when nasm finishes
105 if desc
['error'] == 'expected':
108 # Walk over targets and generate match templates
109 # if were not provided yet
110 for d
in desc
['target']:
111 if 'output' in d
and not 'match' in d
:
112 d
['match'] = d
['output'] + ".t"
118 with
open(path
, "rb") as f
:
120 desc
= json
.loads(f
.read().decode("utf-8").strip("\n"))
129 def read_desc(basedir
, name
):
130 path
= basedir
+ os
.sep
+ name
131 desc
= read_json(path
)
133 if type(desc
) == dict:
134 if prepare_desc(desc
, basedir
, name
, path
) == True:
136 elif type(desc
) == list:
137 expand_templates(desc
)
139 if prepare_desc(de
, basedir
, name
, path
) == True:
143 def collect_test_desc_from_file(path
):
144 if not fnmatch
.fnmatch(path
, '*.json'):
146 basedir
= os
.path
.dirname(path
)
147 filename
= os
.path
.basename(path
)
148 return read_desc(basedir
, filename
)
150 def collect_test_desc_from_dir(basedir
):
152 if os
.path
.isdir(basedir
):
153 for filename
in os
.listdir(basedir
):
154 if os
.path
.isdir(basedir
+ os
.sep
+ filename
):
155 desc_array
+= collect_test_desc_from_dir(basedir
+ os
.sep
+ filename
)
156 elif fnmatch
.fnmatch(filename
, '*.json'):
157 desc
= read_desc(basedir
, filename
)
161 desc_array
.sort(key
=lambda x
: x
['_test-name'])
164 if args
.cmd
== 'list':
165 fmt_entry
= '%-32s %s'
166 desc_array
= collect_test_desc_from_dir(args
.dir)
167 print(fmt_entry
% ('Name', 'Description'))
168 for desc
in desc_array
:
169 print(fmt_entry
% (desc
['_test-name'], desc
['description']))
171 def test_abort(test
, message
):
172 print("\t%s: %s" % (test
, message
))
173 print("=== Test %s ABORT ===" % (test
))
177 def test_fail(test
, message
):
178 print("\t%s: %s" % (test
, message
))
179 print("=== Test %s FAIL ===" % (test
))
182 def test_skip(test
, message
):
183 print("\t%s: %s" % (test
, message
))
184 print("=== Test %s SKIP ===" % (test
))
188 print("=== Test %s ERROR OVER ===" % (test
))
192 print("=== Test %s PASS ===" % (test
))
195 def test_updated(test
):
196 print("=== Test %s UPDATED ===" % (test
))
199 def run_hexdump(path
):
200 p
= subprocess
.Popen([args
.hexdump
, "-C", path
],
201 stdout
= subprocess
.PIPE
,
207 def show_std(stdname
, data
):
208 print("\t--- %s" % (stdname
))
209 for i
in data
.split("\n"):
213 def cmp_std(from_name
, from_data
, match_name
, match_data
):
214 if from_data
!= match_data
:
215 print("\t--- %s" % (from_name
))
216 for i
in from_data
.split("\n"):
218 print("\t--- %s" % (match_name
))
219 for i
in match_data
.split("\n"):
222 diff
= difflib
.unified_diff(from_data
.split("\n"), match_data
.split("\n"),
223 fromfile
= from_name
, tofile
= match_name
)
225 print("\t%s" % i
.strip("\n"))
230 def show_diff(test
, patha
, pathb
):
231 pa
= run_hexdump(patha
)
232 pb
= run_hexdump(pathb
)
233 if pa
== None or pb
== None:
234 return test_fail(test
, "Can't create dumps")
235 sa
= pa
.stdout
.read().decode("utf-8").strip("\n")
236 sb
= pb
.stdout
.read().decode("utf-8").strip("\n")
237 print("\t--- hexdump %s" % (patha
))
238 for i
in sa
.split("\n"):
240 print("\t--- hexdump %s" % (pathb
))
241 for i
in sb
.split("\n"):
246 diff
= difflib
.unified_diff(sa
.split("\n"), sb
.split("\n"),
247 fromfile
= patha
, tofile
= pathb
)
249 print("\t%s" % i
.strip("\n"))
253 def prepare_run_opts(desc
):
257 opts
+= ['-f', desc
['format']]
259 opts
+= desc
['option'].split(" ")
260 for t
in desc
['target']:
263 opts
+= t
['option'].split(" ") + [desc
['_base-dir'] + os
.sep
+ t
['output']]
265 opts
+= ['-o', desc
['_base-dir'] + os
.sep
+ t
['output']]
266 if 'stdout' in t
or 'stderr' in t
:
268 opts
+= t
['option'].split(" ")
270 opts
+= [desc
['_base-dir'] + os
.sep
+ desc
['source']]
274 print("\tProcessing %s" % (desc
['_test-name']))
275 opts
= [args
.nasm
] + prepare_run_opts(desc
)
277 nasm_env
= os
.environ
.copy()
278 nasm_env
['NASM_TEST_RUN'] = 'y'
280 desc_env
= desc
.get('environ')
285 nasm_env
[v
[0]] = v
[1]
287 nasm_env
[v
[0]] = None
289 print("\tExecuting %s" % (" ".join(opts
)))
290 pnasm
= subprocess
.Popen(opts
,
291 stdout
= subprocess
.PIPE
,
292 stderr
= subprocess
.PIPE
,
296 test_fail(desc
['_test-name'], "Unable to execute test")
299 stderr
= pnasm
.stderr
.read(4194304).decode("utf-8").strip("\n")
300 stdout
= pnasm
.stdout
.read(4194304).decode("utf-8").strip("\n")
305 wait_rc
= pnasm
.wait();
307 if desc
['_wait'] != wait_rc
:
309 show_std("stdout", stdout
)
311 show_std("stderr", stderr
)
312 test_fail(desc
['_test-name'],
313 "Unexpected ret code: " + str(wait_rc
))
314 return None, None, None
315 return pnasm
, stdout
, stderr
318 print("=== Running %s ===" % (desc
['_test-name']))
320 pnasm
, stdout
, stderr
= exec_nasm(desc
)
324 for t
in desc
['target']:
326 output
= desc
['_base-dir'] + os
.sep
+ t
['output']
327 match
= desc
['_base-dir'] + os
.sep
+ t
['match']
328 if desc
['_wait'] == 1:
330 print("\tComparing %s %s" % (output
, match
))
331 if filecmp
.cmp(match
, output
) == False:
332 show_diff(desc
['_test-name'], match
, output
)
333 return test_fail(desc
['_test-name'], match
+ " and " + output
+ " files are different")
335 print("\tComparing stdout")
336 match
= desc
['_base-dir'] + os
.sep
+ t
['stdout']
337 match_data
= read_stdfile(match
)
338 if match_data
== None:
339 return test_fail(test
, "Can't read " + match
)
340 if cmp_std(match
, match_data
, 'stdout', stdout
) == False:
341 return test_fail(desc
['_test-name'], "Stdout mismatch")
345 print("\tComparing stderr")
346 match
= desc
['_base-dir'] + os
.sep
+ t
['stderr']
347 match_data
= read_stdfile(match
)
348 if match_data
== None:
349 return test_fail(test
, "Can't read " + match
)
350 if cmp_std(match
, match_data
, 'stderr', stderr
) == False:
351 return test_fail(desc
['_test-name'], "Stderr mismatch")
356 show_std("stdout", stdout
)
357 return test_fail(desc
['_test-name'], "Stdout is not empty")
360 show_std("stderr", stderr
)
361 return test_fail(desc
['_test-name'], "Stderr is not empty")
363 return test_pass(desc
['_test-name'])
366 # Compile sources and generate new targets
367 def test_update(desc
):
368 print("=== Updating %s ===" % (desc
['_test-name']))
370 if 'update' in desc
and desc
['update'] == 'false':
371 return test_skip(desc
['_test-name'], "No output provided")
373 pnasm
, stdout
, stderr
= exec_nasm(desc
)
377 for t
in desc
['target']:
379 output
= desc
['_base-dir'] + os
.sep
+ t
['output']
380 match
= desc
['_base-dir'] + os
.sep
+ t
['match']
381 print("\tMoving %s to %s" % (output
, match
))
382 os
.rename(output
, match
)
384 match
= desc
['_base-dir'] + os
.sep
+ t
['stdout']
385 print("\tMoving %s to %s" % ('stdout', match
))
386 with
open(match
, "wb") as f
:
387 f
.write(stdout
.encode("utf-8"))
390 match
= desc
['_base-dir'] + os
.sep
+ t
['stderr']
391 print("\tMoving %s to %s" % ('stderr', match
))
392 with
open(match
, "wb") as f
:
393 f
.write(stderr
.encode("utf-8"))
396 return test_updated(desc
['_test-name'])
398 if args
.cmd
== 'run':
400 if args
.test
== None:
401 desc_array
= collect_test_desc_from_dir(args
.dir)
403 desc_array
= collect_test_desc_from_file(args
.test
)
404 if len(desc_array
) == 0:
405 test_abort(args
.test
, "Can't obtain test descriptors")
407 for desc
in desc_array
:
408 if test_run(desc
) == False:
409 if 'error' in desc
and desc
['error'] == 'over':
410 test_over(desc
['_test-name'])
412 test_abort(desc
['_test-name'], "Error detected")
414 if args
.cmd
== 'update':
416 if args
.test
== None:
417 desc_array
= collect_test_desc_from_dir(args
.dir)
419 desc_array
= collect_test_desc_from_file(args
.test
)
420 if len(desc_array
) == 0:
421 test_abort(args
.test
, "Can't obtain a test descriptors")
423 for desc
in desc_array
:
424 if test_update(desc
) == False:
425 if 'error' in desc
and desc
['error'] == 'over':
426 test_over(desc
['_test-name'])
428 test_abort(desc
['_test-name'], "Error detected")