mirror of
				https://github.com/netwide-assembler/nasm.git
				synced 2025-10-10 00:25:06 -04:00 
			
		
		
		
	The assembler can't know if something is a colonless label or a misspelled instruction, so print both when complaining about a missing instruction. Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
		
			
				
	
	
		
			575 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			575 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/python3
 | |
| 
 | |
| import subprocess
 | |
| import argparse
 | |
| import difflib
 | |
| import filecmp
 | |
| import fnmatch
 | |
| import json
 | |
| import sys
 | |
| import re
 | |
| import os
 | |
| 
 | |
| fmtr_class = argparse.ArgumentDefaultsHelpFormatter
 | |
| parser = argparse.ArgumentParser(prog = 'nasm-t.py',
 | |
|                                  formatter_class=fmtr_class)
 | |
| 
 | |
| parser.add_argument('-d', '--directory',
 | |
|                     dest = 'dir', default = './travis/test',
 | |
|                     help = 'Directory with tests')
 | |
| 
 | |
| parser.add_argument('--nasm',
 | |
|                     dest = 'nasm', default = './nasm',
 | |
|                     help = 'Nasm executable to use')
 | |
| 
 | |
| sp = parser.add_subparsers(dest = 'cmd')
 | |
| for cmd in ['run']:
 | |
|     spp = sp.add_parser(cmd, help = 'Run test cases')
 | |
|     spp.add_argument('-t', '--test',
 | |
|                      dest = 'test',
 | |
|                      help = 'Run the selected test only',
 | |
|                      required = False)
 | |
| 
 | |
| for cmd in ['new']:
 | |
|     spp = sp.add_parser(cmd, help = 'Add a new test case')
 | |
|     spp.add_argument('--description',
 | |
|                      dest = 'description', default = "Description of a test",
 | |
|                      help = 'Description of a test',
 | |
|                      required = False)
 | |
|     spp.add_argument('--id',
 | |
|                      dest = 'id',
 | |
|                      help = 'Test identifier/name',
 | |
|                      required = True)
 | |
|     spp.add_argument('--format',
 | |
|                      dest = 'format', default = 'bin',
 | |
|                      help = 'Output format',
 | |
|                      required = False)
 | |
|     spp.add_argument('--source',
 | |
|                      dest = 'source',
 | |
|                      help = 'Source file',
 | |
|                      required = False)
 | |
|     spp.add_argument('--option',
 | |
|                      dest = 'option',
 | |
|                      default = '-Ox',
 | |
|                      help = 'NASM options',
 | |
|                      required = False)
 | |
|     spp.add_argument('--ref',
 | |
|                      dest = 'ref',
 | |
|                      help = 'Test reference',
 | |
|                      required = False)
 | |
|     spp.add_argument('--error',
 | |
|                      dest = 'error',
 | |
|                      help = '"y" if test is supposed to fail or "i" to ignore',
 | |
|                      required = False)
 | |
|     spp.add_argument('--output',
 | |
|                      dest = 'output', default = 'y',
 | |
|                      help = 'Output (compiled) file name (or "y")',
 | |
|                      required = False)
 | |
|     spp.add_argument('--stdout',
 | |
|                      dest = 'stdout', default = 'y',
 | |
|                      help = 'Filename of stdout file (or "y")',
 | |
|                      required = False)
 | |
|     spp.add_argument('--stderr',
 | |
|                      dest = 'stderr', default = 'y',
 | |
|                      help = 'Filename of stderr file (or "y")',
 | |
|                      required = False)
 | |
| 
 | |
| for cmd in ['list']:
 | |
|     spp = sp.add_parser(cmd, help = 'List test cases')
 | |
| 
 | |
| for cmd in ['update']:
 | |
|     spp = sp.add_parser(cmd, help = 'Update test cases with new compiler')
 | |
|     spp.add_argument('-t', '--test',
 | |
|                      dest = 'test',
 | |
|                      help = 'Update the selected test only',
 | |
|                      required = False)
 | |
| 
 | |
| map_fmt_ext = {
 | |
|         'bin':      '.bin',
 | |
|         'elf':      '.o',
 | |
|         'elf64':    '.o',
 | |
|         'elf32':    '.o',
 | |
|         'elfx32':   '.o',
 | |
|         'ith':      '.ith',
 | |
|         'srec':     '.srec',
 | |
|         'obj':      '.obj',
 | |
|         'win32':    '.obj',
 | |
|         'win64':    '.obj',
 | |
|         'coff':     '.obj',
 | |
|         'macho':    '.o',
 | |
|         'macho32':  '.o',
 | |
|         'macho64':  '.o',
 | |
|         'aout':     '.out',
 | |
|         'aoutb':    '.out',
 | |
|         'as86':     '.o',
 | |
|         'rdf':      '.rdf',
 | |
| }
 | |
| 
 | |
| args = parser.parse_args()
 | |
| 
 | |
| if args.cmd == None:
 | |
|     parser.print_help()
 | |
|     sys.exit(1)
 | |
| 
 | |
| def read_stdfile(path):
 | |
|     with open(path, "rb") as f:
 | |
|         data = f.read().decode("utf-8","replace")
 | |
|         f.close()
 | |
|         return data
 | |
| 
 | |
| #
 | |
| # Check if descriptor has mandatory fields
 | |
| def is_valid_desc(desc):
 | |
|     if desc == None:
 | |
|         return False
 | |
|     if 'description' not in desc:
 | |
|         return False
 | |
|     if desc['description'] == "":
 | |
|         return False
 | |
|     return True
 | |
| 
 | |
| #
 | |
| # Expand ref/id in descriptors array
 | |
| def expand_templates(desc_array):
 | |
|     desc_ids = { }
 | |
|     for d in desc_array:
 | |
|         if 'id' in d:
 | |
|             desc_ids[d['id']] = d
 | |
|     for i, d in enumerate(desc_array):
 | |
|         if 'ref' in d and d['ref'] in desc_ids:
 | |
|             ref = desc_ids[d['ref']]
 | |
|             own = d.copy()
 | |
|             desc_array[i] = ref.copy()
 | |
|             for k, v in own.items():
 | |
|                 desc_array[i][k] = v
 | |
|             del desc_array[i]['id']
 | |
|     return desc_array
 | |
| 
 | |
| def prepare_desc(desc, basedir, name, path):
 | |
|     if not is_valid_desc(desc):
 | |
|         return False
 | |
|     #
 | |
|     # Put private fields
 | |
|     desc['_base-dir'] = basedir
 | |
|     desc['_json-file'] = name
 | |
|     desc['_json-path'] = path
 | |
|     desc['_test-name'] = basedir + os.sep + name[:-5]
 | |
|     #
 | |
|     # If no target provided never update
 | |
|     if 'target' not in desc:
 | |
|         desc['target'] = []
 | |
|         desc['update'] = 'false'
 | |
|     #
 | |
|     # Which code to expect when nasm finishes
 | |
|     desc['_wait'] = 0
 | |
|     if 'error' in desc:
 | |
|         if desc['error'] == 'expected':
 | |
|             desc['_wait'] = 1
 | |
|     #
 | |
|     # Walk over targets and generate match templates
 | |
|     # if were not provided yet
 | |
|     for d in desc['target']:
 | |
|         if 'output' in d and not 'match' in d:
 | |
|             d['match'] = d['output'] + ".t"
 | |
|     return True
 | |
| 
 | |
| def read_json(path):
 | |
|     desc = None
 | |
|     try:
 | |
|         with open(path, "rb") as f:
 | |
|             try:
 | |
|                 desc = json.loads(f.read().decode("utf-8","replace"))
 | |
|             except:
 | |
|                 desc = None
 | |
|             finally:
 | |
|                 f.close()
 | |
|     except:
 | |
|         pass
 | |
|     return desc
 | |
| 
 | |
| def read_desc(basedir, name):
 | |
|     path = basedir + os.sep + name
 | |
|     desc = read_json(path)
 | |
|     desc_array = []
 | |
|     if type(desc) == dict:
 | |
|         if prepare_desc(desc, basedir, name, path) == True:
 | |
|             desc_array += [desc]
 | |
|     elif type(desc) == list:
 | |
|         expand_templates(desc)
 | |
|         for de in desc:
 | |
|             if prepare_desc(de, basedir, name, path) == True:
 | |
|                 desc_array += [de]
 | |
|     return desc_array
 | |
| 
 | |
| def collect_test_desc_from_file(path):
 | |
|     if not fnmatch.fnmatch(path, '*.json'):
 | |
|         path += '.json'
 | |
|     basedir = os.path.dirname(path)
 | |
|     filename = os.path.basename(path)
 | |
|     return read_desc(basedir, filename)
 | |
| 
 | |
| def collect_test_desc_from_dir(basedir):
 | |
|     desc_array = []
 | |
|     if os.path.isdir(basedir):
 | |
|         for filename in os.listdir(basedir):
 | |
|             if os.path.isdir(basedir + os.sep + filename):
 | |
|                 desc_array += collect_test_desc_from_dir(basedir + os.sep + filename)
 | |
|             elif fnmatch.fnmatch(filename, '*.json'):
 | |
|                 desc = read_desc(basedir, filename)
 | |
|                 if desc == None:
 | |
|                     continue
 | |
|                 desc_array += desc
 | |
|         desc_array.sort(key=lambda x: x['_test-name'])
 | |
|     return desc_array
 | |
| 
 | |
| if args.cmd == 'list':
 | |
|     fmt_entry = '%-32s %s'
 | |
|     desc_array = collect_test_desc_from_dir(args.dir)
 | |
|     print(fmt_entry % ('Name', 'Description'))
 | |
|     for desc in desc_array:
 | |
|         print(fmt_entry % (desc['_test-name'], desc['description']))
 | |
| 
 | |
| def test_abort(test, message):
 | |
|     print("\t%s: %s" % (test, message))
 | |
|     print("=== Test %s ABORT ===" % (test))
 | |
|     sys.exit(1)
 | |
|     return False
 | |
| 
 | |
| def test_fail(test, message):
 | |
|     print("\t%s: %s" % (test, message))
 | |
|     print("=== Test %s FAIL ===" % (test))
 | |
|     return False
 | |
| 
 | |
| def test_skip(test, message):
 | |
|     print("\t%s: %s" % (test, message))
 | |
|     print("=== Test %s SKIP ===" % (test))
 | |
|     return True
 | |
| 
 | |
| def test_over(test):
 | |
|     print("=== Test %s ERROR OVER ===" % (test))
 | |
|     return True
 | |
| 
 | |
| def test_pass(test):
 | |
|     print("=== Test %s PASS ===" % (test))
 | |
|     return True
 | |
| 
 | |
| def test_updated(test):
 | |
|     print("=== Test %s UPDATED ===" % (test))
 | |
|     return True
 | |
| 
 | |
| def hexdump(path):
 | |
|     dump = ''
 | |
|     addr = 0
 | |
|     with open(path, 'rb') as f:
 | |
|         while b := f.read(16):
 | |
|             dump += "%08x  " % (addr)
 | |
|             for i in range(16):
 | |
|                 if (i == 8):
 | |
|                     dump += " -"
 | |
|                 if (i >= len(b)):
 | |
|                     dump += "   "
 | |
|                 else:
 | |
|                     dump += " %02x" % b[i]
 | |
|             dump += "  |"
 | |
|             for i in range(16):
 | |
|                 if (i >= len(b)):
 | |
|                     c = ord(' ')
 | |
|                 else:
 | |
|                     c = b[i]
 | |
|                 if (c < 32 or c > 126):
 | |
|                     c = ord('.')
 | |
|                 dump += chr(c)
 | |
|             dump += "|\n";
 | |
|             addr += 16
 | |
|     return dump
 | |
| 
 | |
| def show_std(stdname, data):
 | |
|     print("\t--- %s" % (stdname))
 | |
|     for i in data.split("\n"):
 | |
|         print("\t%s" % i)
 | |
|     print("\t---")
 | |
| 
 | |
| def cmp_std(from_name, from_data, match_name, match_data):
 | |
|     if from_data != match_data:
 | |
|         print("\t--- %s" % (from_name))
 | |
|         for i in from_data.split("\n"):
 | |
|             print("\t%s" % i)
 | |
|         print("\t--- %s" % (match_name))
 | |
|         for i in match_data.split("\n"):
 | |
|             print("\t%s" % i)
 | |
| 
 | |
|         diff = difflib.unified_diff(from_data.split("\n"), match_data.split("\n"),
 | |
|                                     fromfile = from_name, tofile = match_name)
 | |
|         for i in diff:
 | |
|             print("\t%s" % i.strip("\n"))
 | |
|         print("\t---")
 | |
|         return False
 | |
|     return True
 | |
| 
 | |
| def show_diff(test, patha, pathb):
 | |
|     try:
 | |
|         sa = hexdump(patha)
 | |
|         sb = hexdump(pathb)
 | |
|     except OSError:
 | |
|         return test_fail(test, "Can't create dumps")
 | |
| 
 | |
|     print("\t--- hexdump %s" % (patha))
 | |
|     for i in sa.split("\n"):
 | |
|         print("\t%s" % i)
 | |
|     print("\t--- hexdump %s" % (pathb))
 | |
|     for i in sb.split("\n"):
 | |
|         print("\t%s" % i)
 | |
| 
 | |
|     diff = difflib.unified_diff(sa.split("\n"), sb.split("\n"),
 | |
|                                 fromfile = patha, tofile = pathb)
 | |
|     for i in diff:
 | |
|         print("\t%s" % i.strip("\n"))
 | |
|     print("\t---")
 | |
|     return True
 | |
| 
 | |
| def prepare_run_opts(desc):
 | |
|     opts = []
 | |
| 
 | |
|     if 'format' in desc:
 | |
|         opts += ['-f', desc['format']]
 | |
|     if 'option' in desc:
 | |
|         opts += desc['option'].split(" ")
 | |
|     for t in desc['target']:
 | |
|         if 'output' in t:
 | |
|             if 'option' in t:
 | |
|                 opts += t['option'].split(" ") + [desc['_base-dir'] + os.sep + t['output']]
 | |
|             else:
 | |
|                 opts += ['-o', desc['_base-dir'] + os.sep + t['output']]
 | |
|         if 'stdout' in t or 'stderr' in t:
 | |
|             if 'option' in t:
 | |
|                 opts += t['option'].split(" ")
 | |
|     if 'source' in desc:
 | |
|         opts += [desc['_base-dir'] + os.sep + desc['source']]
 | |
|     return opts
 | |
| 
 | |
| def exec_nasm(desc):
 | |
|     print("\tProcessing %s" % (desc['_test-name']))
 | |
|     opts = [args.nasm] + prepare_run_opts(desc)
 | |
| 
 | |
|     nasm_env = os.environ.copy()
 | |
|     nasm_env['NASMENV'] = '--reproducible'
 | |
| 
 | |
|     desc_env = desc.get('environ')
 | |
|     if desc_env:
 | |
|         for i in desc_env:
 | |
|             v = i.split('=')
 | |
|             if len(v) == 2:
 | |
|                 nasm_env[v[0]] = v[1]
 | |
|             else:
 | |
|                 nasm_env[v[0]] = None
 | |
| 
 | |
|     print("\tExecuting %s" % (" ".join(opts)))
 | |
|     pnasm = subprocess.Popen(opts,
 | |
|                              stdout = subprocess.PIPE,
 | |
|                              stderr = subprocess.PIPE,
 | |
|                              close_fds = True,
 | |
|                              env = nasm_env)
 | |
|     if pnasm == None:
 | |
|         test_fail(desc['_test-name'], "Unable to execute test")
 | |
|         return None
 | |
| 
 | |
|     #
 | |
|     # FIXME: For now 4M buffer is enough but
 | |
|     # better provide reading in a cycle.
 | |
|     stderr = pnasm.stderr.read(4194304).decode("utf-8","replace")
 | |
|     stdout = pnasm.stdout.read(4194304).decode("utf-8","replace")
 | |
| 
 | |
|     pnasm.stdout.close()
 | |
|     pnasm.stderr.close()
 | |
| 
 | |
|     wait_rc = pnasm.wait();
 | |
| 
 | |
|     if desc['_wait'] != wait_rc:
 | |
|         if stdout != "":
 | |
|             show_std("stdout", stdout)
 | |
|         if stderr != "":
 | |
|             show_std("stderr", stderr)
 | |
|         test_fail(desc['_test-name'],
 | |
|                   "Unexpected ret code: " + str(wait_rc))
 | |
|         return None, None, None
 | |
|     return pnasm, stdout, stderr
 | |
| 
 | |
| def test_run(desc):
 | |
|     print("=== Running %s ===" % (desc['_test-name']))
 | |
| 
 | |
|     if 'disable' in desc:
 | |
|         return test_skip(desc['_test-name'], desc["disable"])
 | |
| 
 | |
|     pnasm, stdout, stderr = exec_nasm(desc)
 | |
|     if pnasm == None:
 | |
|         return False
 | |
| 
 | |
|     for t in desc['target']:
 | |
|         if 'output' in t:
 | |
|             output = desc['_base-dir'] + os.sep + t['output']
 | |
|             match = desc['_base-dir'] + os.sep + t['match']
 | |
|             if desc['_wait'] == 1:
 | |
|                 continue
 | |
|             print("\tComparing %s %s" % (output, match))
 | |
|             if filecmp.cmp(match, output) == False:
 | |
|                 show_diff(desc['_test-name'], match, output)
 | |
|                 return test_fail(desc['_test-name'], match + " and " + output + " files are different")
 | |
|         elif 'stdout' in t:
 | |
|             print("\tComparing stdout")
 | |
|             match = desc['_base-dir'] + os.sep + t['stdout']
 | |
|             match_data = read_stdfile(match)
 | |
|             if match_data == None:
 | |
|                 return test_fail(test, "Can't read " + match)
 | |
|             if cmp_std(match, match_data, 'stdout', stdout) == False:
 | |
|                 return test_fail(desc['_test-name'], "Stdout mismatch")
 | |
|             else:
 | |
|                 stdout = ""
 | |
|         elif 'stderr' in t:
 | |
|             print("\tComparing stderr")
 | |
|             match = desc['_base-dir'] + os.sep + t['stderr']
 | |
|             match_data = read_stdfile(match)
 | |
|             if match_data == None:
 | |
|                 return test_fail(test, "Can't read " + match)
 | |
|             if cmp_std(match, match_data, 'stderr', stderr) == False:
 | |
|                 return test_fail(desc['_test-name'], "Stderr mismatch")
 | |
|             else:
 | |
|                 stderr = ""
 | |
| 
 | |
|     if stdout != "":
 | |
|         show_std("stdout", stdout)
 | |
|         return test_fail(desc['_test-name'], "Stdout is not empty")
 | |
| 
 | |
|     if stderr != "":
 | |
|         show_std("stderr", stderr)
 | |
|         return test_fail(desc['_test-name'], "Stderr is not empty")
 | |
| 
 | |
|     return test_pass(desc['_test-name'])
 | |
| 
 | |
| #
 | |
| # Compile sources and generate new targets
 | |
| def test_update(desc):
 | |
|     print("=== Updating %s ===" % (desc['_test-name']))
 | |
| 
 | |
|     if 'update' in desc and desc['update'] == 'false':
 | |
|         return test_skip(desc['_test-name'], "No output provided")
 | |
|     if 'disable' in desc:
 | |
|         return test_skip(desc['_test-name'], desc["disable"])
 | |
| 
 | |
|     pnasm, stdout, stderr = exec_nasm(desc)
 | |
|     if pnasm == None:
 | |
|         return False
 | |
| 
 | |
|     for t in desc['target']:
 | |
|         if 'output' in t:
 | |
|             output = desc['_base-dir'] + os.sep + t['output']
 | |
|             match = desc['_base-dir'] + os.sep + t['match']
 | |
|             print("\tMoving %s to %s" % (output, match))
 | |
|             os.rename(output, match)
 | |
|         if 'stdout' in t:
 | |
|             match = desc['_base-dir'] + os.sep + t['stdout']
 | |
|             print("\tMoving %s to %s" % ('stdout', match))
 | |
|             with open(match, "wb") as f:
 | |
|                 f.write(stdout.encode("utf-8"))
 | |
|                 f.close()
 | |
|         if 'stderr' in t:
 | |
|             match = desc['_base-dir'] + os.sep + t['stderr']
 | |
|             print("\tMoving %s to %s" % ('stderr', match))
 | |
|             with open(match, "wb") as f:
 | |
|                 f.write(stderr.encode("utf-8"))
 | |
|                 f.close()
 | |
| 
 | |
|     return test_updated(desc['_test-name'])
 | |
| 
 | |
| #
 | |
| # Create a new empty test case
 | |
| if args.cmd == 'new':
 | |
|     #
 | |
|     # If no source provided create one
 | |
|     # from (ID which is required)
 | |
|     if not args.source:
 | |
|         args.source = args.id + ".asm"
 | |
| 
 | |
|     #
 | |
|     # Emulate "touch" on source file
 | |
|     path_asm = args.dir + os.sep + args.source
 | |
|     print("\tCreating %s" % (path_asm))
 | |
|     open(path_asm, 'a').close()
 | |
| 
 | |
|     #
 | |
|     # Fill the test descriptor
 | |
|     #
 | |
|     # FIXME: We should probably use Jinja
 | |
|     path_json = args.dir + os.sep + args.id + ".json"
 | |
|     print("\tFilling descriptor %s" % (path_json))
 | |
|     with open(path_json, 'wb') as f:
 | |
|         f.write("[\n\t{\n".encode("utf-8"))
 | |
|         acc = []
 | |
|         if args.description:
 | |
|             acc.append("\t\t\"description\": \"{}\"".format(args.description))
 | |
|         acc.append("\t\t\"id\": \"{}\"".format(args.id))
 | |
|         if args.format:
 | |
|             acc.append("\t\t\"format\": \"{}\"".format(args.format))
 | |
|         acc.append("\t\t\"source\": \"{}\"".format(args.source))
 | |
|         if args.option:
 | |
|             acc.append("\t\t\"option\": \"{}\"".format(args.option))
 | |
|         if args.ref:
 | |
|             acc.append("\t\t\"ref\": \"{}\"".format(args.ref))
 | |
|         if args.error == 'y':
 | |
|             acc.append("\t\t\"error\": \"expected\"")
 | |
|         elif args.error == 'i':
 | |
|             acc.append("\t\t\"error\": \"over\"")
 | |
|         f.write(",\n".join(acc).encode("utf-8"))
 | |
|         if args.output or args.stdout or args.stderr:
 | |
|             acc = []
 | |
|             if args.output:
 | |
|                 if args.output == 'y':
 | |
|                     if args.format in map_fmt_ext:
 | |
|                         args.output = args.id + map_fmt_ext[args.format]
 | |
|                 acc.append("\t\t\t{{ \"output\": \"{}\" }}".format(args.output))
 | |
|             if args.stdout:
 | |
|                 if args.stdout == 'y':
 | |
|                     args.stdout = args.id + '.stdout'
 | |
|                 acc.append("\t\t\t{{ \"stdout\": \"{}\" }}".format(args.stdout))
 | |
|             if args.stderr:
 | |
|                 if args.stderr == 'y':
 | |
|                     args.stderr = args.id + '.stderr'
 | |
|                 acc.append("\t\t\t{{ \"stderr\": \"{}\" }}".format(args.stderr))
 | |
|             f.write(",\n".encode("utf-8"))
 | |
|             f.write("\t\t\"target\": [\n".encode("utf-8"))
 | |
|             f.write(",\n".join(acc).encode("utf-8"))
 | |
|             f.write("\n\t\t]".encode("utf-8"))
 | |
|         f.write("\n\t}\n]\n".encode("utf-8"))
 | |
|         f.close()
 | |
| 
 | |
| if args.cmd == 'run':
 | |
|     desc_array = []
 | |
|     if args.test == None:
 | |
|         desc_array = collect_test_desc_from_dir(args.dir)
 | |
|     else:
 | |
|         desc_array = collect_test_desc_from_file(args.test)
 | |
|         if len(desc_array) == 0:
 | |
|             test_abort(args.test, "Can't obtain test descriptors")
 | |
| 
 | |
|     for desc in desc_array:
 | |
|         if test_run(desc) == False:
 | |
|             if 'error' in desc and desc['error'] == 'over':
 | |
|                 test_over(desc['_test-name'])
 | |
|             else:
 | |
|                 test_abort(desc['_test-name'], "Error detected")
 | |
| 
 | |
| if args.cmd == 'update':
 | |
|     desc_array = []
 | |
|     if args.test == None:
 | |
|         desc_array = collect_test_desc_from_dir(args.dir)
 | |
|     else:
 | |
|         desc_array = collect_test_desc_from_file(args.test)
 | |
|         if len(desc_array) == 0:
 | |
|             test_abort(args.test, "Can't obtain a test descriptors")
 | |
| 
 | |
|     for desc in desc_array:
 | |
|         if test_update(desc) == False:
 | |
|             if 'error' in desc and desc['error'] == 'over':
 | |
|                 test_over(desc['_test-name'])
 | |
|             else:
 | |
|                 test_abort(desc['_test-name'], "Error detected")
 |