From ad5b9ceed5acde6f66bc44b6e0b0dacd08b36a69 Mon Sep 17 00:00:00 2001 From: Tom Alcorn Date: Wed, 27 May 2020 17:31:13 -0700 Subject: [PATCH] Add failing test case --- .gitignore | 2 +- sbysrc/sby.py | 314 +++++++++++++++++++++++---------------------- sbysrc/sby_core.py | 1 + sbysrc/test_sby.py | 40 ++++++ 4 files changed, 202 insertions(+), 155 deletions(-) create mode 100644 sbysrc/test_sby.py diff --git a/.gitignore b/.gitignore index e374fdc..9e9ff5b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /docs/build /sbysrc/demo[0-9] /sbysrc/__pycache__ - +*.pyc diff --git a/sbysrc/sby.py b/sbysrc/sby.py index 5c8c797..aeb1441 100644 --- a/sbysrc/sby.py +++ b/sbysrc/sby.py @@ -28,126 +28,129 @@ class DictAction(argparse.Action): name = option_string.lstrip(parser.prefix_chars).replace("-", "_") getattr(namespace, self.dest)[name] = values -parser = argparse.ArgumentParser(prog="sby", - usage="%(prog)s [options] [.sby [tasknames] | ]") -parser.set_defaults(exe_paths=dict()) +if __name__ == '__main__': + parser = argparse.ArgumentParser(prog="sby", + usage="%(prog)s [options] [.sby [tasknames] | ]") + parser.set_defaults(exe_paths=dict()) -parser.add_argument("-d", metavar="", dest="workdir", - help="set workdir name. default: or _") -parser.add_argument("-f", action="store_true", dest="force", - help="remove workdir if it already exists") -parser.add_argument("-b", action="store_true", dest="backup", - help="backup workdir if it already exists") -parser.add_argument("-t", action="store_true", dest="tmpdir", - help="run in a temporary workdir (remove when finished)") -parser.add_argument("-T", metavar="", action="append", dest="tasknames", default=list(), - help="add taskname (useful when sby file is read from stdin)") -parser.add_argument("-E", action="store_true", dest="throw_err", - help="throw an exception (incl stack trace) for most errors") + parser.add_argument("-d", metavar="", dest="workdir", + help="set workdir name. default: or _") + parser.add_argument("-f", action="store_true", dest="force", + help="remove workdir if it already exists") + parser.add_argument("-b", action="store_true", dest="backup", + help="backup workdir if it already exists") + parser.add_argument("-t", action="store_true", dest="tmpdir", + help="run in a temporary workdir (remove when finished)") + parser.add_argument("-T", metavar="", action="append", dest="tasknames", default=list(), + help="add taskname (useful when sby file is read from stdin)") + parser.add_argument("-E", action="store_true", dest="throw_err", + help="throw an exception (incl stack trace) for most errors") -parser.add_argument("--yosys", metavar="", - action=DictAction, dest="exe_paths") -parser.add_argument("--abc", metavar="", - action=DictAction, dest="exe_paths") -parser.add_argument("--smtbmc", metavar="", - action=DictAction, dest="exe_paths") -parser.add_argument("--suprove", metavar="", - action=DictAction, dest="exe_paths") -parser.add_argument("--aigbmc", metavar="", - action=DictAction, dest="exe_paths") -parser.add_argument("--avy", metavar="", - action=DictAction, dest="exe_paths") -parser.add_argument("--btormc", metavar="", - action=DictAction, dest="exe_paths") -parser.add_argument("--cosa2", metavar="", - action=DictAction, dest="exe_paths", - help="configure which executable to use for the respective tool") -parser.add_argument("--dumpcfg", action="store_true", dest="dump_cfg", - help="print the pre-processed configuration file") -parser.add_argument("--dumptasks", action="store_true", dest="dump_tasks", - help="print the list of tasks") -parser.add_argument("--dumpfiles", action="store_true", dest="dump_files", - help="print the list of source files") -parser.add_argument("--setup", action="store_true", dest="setupmode", - help="set up the working directory and exit") + parser.add_argument("--yosys", metavar="", + action=DictAction, dest="exe_paths") + parser.add_argument("--abc", metavar="", + action=DictAction, dest="exe_paths") + parser.add_argument("--smtbmc", metavar="", + action=DictAction, dest="exe_paths") + parser.add_argument("--suprove", metavar="", + action=DictAction, dest="exe_paths") + parser.add_argument("--aigbmc", metavar="", + action=DictAction, dest="exe_paths") + parser.add_argument("--avy", metavar="", + action=DictAction, dest="exe_paths") + parser.add_argument("--btormc", metavar="", + action=DictAction, dest="exe_paths") + parser.add_argument("--cosa2", metavar="", + action=DictAction, dest="exe_paths", + help="configure which executable to use for the respective tool") + parser.add_argument("--dumpcfg", action="store_true", dest="dump_cfg", + help="print the pre-processed configuration file") + parser.add_argument("--dumptasks", action="store_true", dest="dump_tasks", + help="print the list of tasks") + parser.add_argument("--dumpfiles", action="store_true", dest="dump_files", + help="print the list of source files") + parser.add_argument("--setup", action="store_true", dest="setupmode", + help="set up the working directory and exit") -parser.add_argument("--init-config-file", dest="init_config_file", - help="create a default .sby config file") -parser.add_argument("sbyfile", metavar=".sby | ", nargs="?", - help=".sby file OR directory containing config.sby file") -parser.add_argument("arg_tasknames", metavar="tasknames", nargs="*", - help="tasks to run (only valid when .sby is used)") + parser.add_argument("--init-config-file", dest="init_config_file", + help="create a default .sby config file") + parser.add_argument("sbyfile", metavar=".sby | ", nargs="?", + help=".sby file OR directory containing config.sby file") + parser.add_argument("arg_tasknames", metavar="tasknames", nargs="*", + help="tasks to run (only valid when .sby is used)") -args = parser.parse_args() + args = parser.parse_args() -sbyfile = args.sbyfile -workdir = args.workdir -tasknames = args.arg_tasknames + args.tasknames -opt_force = args.force -opt_backup = args.backup -opt_tmpdir = args.tmpdir -exe_paths = args.exe_paths -throw_err = args.throw_err -dump_cfg = args.dump_cfg -dump_tasks = args.dump_tasks -dump_files = args.dump_files -reusedir = False -setupmode = args.setupmode -init_config_file = args.init_config_file + sbyfile = args.sbyfile + workdir = args.workdir + tasknames = args.arg_tasknames + args.tasknames + opt_force = args.force + opt_backup = args.backup + opt_tmpdir = args.tmpdir + exe_paths = args.exe_paths + throw_err = args.throw_err + dump_cfg = args.dump_cfg + dump_tasks = args.dump_tasks + dump_files = args.dump_files + reusedir = False + setupmode = args.setupmode + init_config_file = args.init_config_file -if sbyfile is not None: - if os.path.isdir(sbyfile): - if workdir is not None: - print("ERROR: Can't use -d when running in existing directory.", file=sys.stderr) + if sbyfile is not None: + if os.path.isdir(sbyfile): + if workdir is not None: + print("ERROR: Can't use -d when running in existing directory.", file=sys.stderr) + sys.exit(1) + workdir = sbyfile + sbyfile += "/config.sby" + reusedir = True + if not opt_force and os.path.exists(workdir + "/model"): + print("ERROR: Use -f to re-run in existing directory.", file=sys.stderr) + sys.exit(1) + if tasknames: + print("ERROR: Can't use tasks when running in existing directory.", file=sys.stderr) + sys.exit(1) + if setupmode: + print("ERROR: Can't use --setup with existing directory.", file=sys.stderr) + sys.exit(1) + if opt_force: + for f in "PASS FAIL UNKNOWN ERROR TIMEOUT".split(): + if os.path.exists(workdir + "/" + f): + os.remove(workdir + "/" + f) + elif not sbyfile.endswith(".sby"): + print("ERROR: Sby file does not have .sby file extension.", file=sys.stderr) sys.exit(1) - workdir = sbyfile - sbyfile += "/config.sby" - reusedir = True - if not opt_force and os.path.exists(workdir + "/model"): - print("ERROR: Use -f to re-run in existing directory.", file=sys.stderr) - sys.exit(1) - if tasknames: - print("ERROR: Can't use tasks when running in existing directory.", file=sys.stderr) - sys.exit(1) - if setupmode: - print("ERROR: Can't use --setup with existing directory.", file=sys.stderr) - sys.exit(1) - if opt_force: - for f in "PASS FAIL UNKNOWN ERROR TIMEOUT".split(): - if os.path.exists(workdir + "/" + f): - os.remove(workdir + "/" + f) - elif not sbyfile.endswith(".sby"): - print("ERROR: Sby file does not have .sby file extension.", file=sys.stderr) - sys.exit(1) -elif init_config_file is not None: - sv_file = init_config_file + ".sv" - sby_file = init_config_file + ".sby" - with open(sby_file, 'w') as config: - config.write("""[options] -mode bmc + elif init_config_file is not None: + sv_file = init_config_file + ".sv" + sby_file = init_config_file + ".sby" + with open(sby_file, 'w') as config: + config.write("""[options] + mode bmc -[engines] -smtbmc + [engines] + smtbmc -[script] -read -formal {0} -prep -top top + [script] + read -formal {0} + prep -top top -[files] -{0} -""".format(sv_file)) + [files] + {0} + """.format(sv_file)) - print("sby config written to {}".format(sby_file), file=sys.stderr) - sys.exit(0) + print("sby config written to {}".format(sby_file), file=sys.stderr) + sys.exit(0) + + early_logmsgs = list() -early_logmsgs = list() def early_log(workdir, msg): tm = localtime() early_logmsgs.append("SBY {:2d}:{:02d}:{:02d} [{}] {}".format(tm.tm_hour, tm.tm_min, tm.tm_sec, workdir, msg)) print(early_logmsgs[-1]) + def read_sbyconfig(sbydata, taskname): cfgdata = list() tasklist = list() @@ -269,63 +272,65 @@ def read_sbyconfig(sbydata, taskname): return cfgdata, tasklist -sbydata = list() -with (open(sbyfile, "r") if sbyfile is not None else sys.stdin) as f: - for line in f: - sbydata.append(line) +if __name__ == '__main__': + sbydata = list() + with (open(sbyfile, "r") if sbyfile is not None else sys.stdin) as f: + for line in f: + sbydata.append(line) -if dump_cfg: - assert len(tasknames) < 2 - sbyconfig, _ = read_sbyconfig(sbydata, tasknames[0] if len(tasknames) else None) - print("\n".join(sbyconfig)) - sys.exit(0) + if dump_cfg: + assert len(tasknames) < 2 + sbyconfig, _ = read_sbyconfig(sbydata, tasknames[0] if len(tasknames) else None) + print("\n".join(sbyconfig)) + sys.exit(0) -if dump_files: - file_set = set() + if dump_files: + file_set = set() - def find_files(taskname): - sbyconfig, _ = read_sbyconfig(sbydata, taskname) + def find_files(taskname): + sbyconfig, _ = read_sbyconfig(sbydata, taskname) - start_index = -1 - for i in range(len(sbyconfig)): - if sbyconfig[i].strip() == "[files]": - start_index = i - break + start_index = -1 + for i in range(len(sbyconfig)): + if sbyconfig[i].strip() == "[files]": + start_index = i + break - if start_index == -1: - return + if start_index == -1: + return - for line in sbyconfig[start_index+1:]: - line = line.strip() - if line.startswith("["): - break - if line == "" or line.startswith("#"): - continue - filename = line.split()[-1] - file_set.add(process_filename(filename)) + for line in sbyconfig[start_index+1:]: + line = line.strip() + if line.startswith("["): + break + if line == "" or line.startswith("#"): + continue + filename = line.split()[-1] + file_set.add(process_filename(filename)) - if len(tasknames): - for taskname in tasknames: - find_files(taskname) - else: - find_files(None) - print("\n".join(file_set)) - sys.exit(0) + if len(tasknames): + for taskname in tasknames: + find_files(taskname) + else: + find_files(None) + print("\n".join(file_set)) + sys.exit(0) -if len(tasknames) == 0: - _, tasknames = read_sbyconfig(sbydata, None) if len(tasknames) == 0: - tasknames = [None] + _, tasknames = read_sbyconfig(sbydata, None) + if len(tasknames) == 0: + tasknames = [None] -if dump_tasks: - for task in tasknames: - if task is not None: - print(task) - sys.exit(0) + if dump_tasks: + for task in tasknames: + if task is not None: + print(task) + sys.exit(0) + + if (workdir is not None) and (len(tasknames) != 1): + print("ERROR: Exactly one task is required when workdir is specified.", file=sys.stderr) + sys.exit(1) -if (workdir is not None) and (len(tasknames) != 1): - print("ERROR: Exactly one task is required when workdir is specified.", file=sys.stderr) - sys.exit(1) def run_job(taskname): my_workdir = workdir @@ -425,12 +430,13 @@ def run_job(taskname): return job.retcode -retcode = 0 -for t in tasknames: - retcode |= run_job(t) +if __name__ == '__main__': + retcode = 0 + for t in tasknames: + retcode |= run_job(t) -if retcode and (len(tasknames) > 1 or tasknames[0] is not None): - tm = localtime() - print("SBY {:2d}:{:02d}:{:02d} One or more tasks produced a non-zero return code.".format(tm.tm_hour, tm.tm_min, tm.tm_sec)) + if retcode and (len(tasknames) > 1 or tasknames[0] is not None): + tm = localtime() + print("SBY {:2d}:{:02d}:{:02d} One or more tasks produced a non-zero return code.".format(tm.tm_hour, tm.tm_min, tm.tm_sec)) -sys.exit(retcode) + sys.exit(retcode) diff --git a/sbysrc/sby_core.py b/sbysrc/sby_core.py index 972e047..6ea3736 100644 --- a/sbysrc/sby_core.py +++ b/sbysrc/sby_core.py @@ -499,6 +499,7 @@ class SbyJob: for line in f: raw_line = line if mode in ["options", "engines", "files"]: + # delete trailing whitespace and comments line = re.sub(r"\s*(\s#.*)?$", "", line) if line == "" or line[0] == "#": continue diff --git a/sbysrc/test_sby.py b/sbysrc/test_sby.py new file mode 100644 index 0000000..1b882ce --- /dev/null +++ b/sbysrc/test_sby.py @@ -0,0 +1,40 @@ +import unittest + +from sby import * + + +class TestSby(unittest.TestCase): + def test_read_sbyconfig(self): + cfg = ''' +[tasks] +a +b + +[options] +a: +mode prove +depth 100 + +b: +mode cover +depth 100 + +[engines] +smtbmc + +[script] +read -formal foo.v +prep -top foo + +[files] +foo.v +''' + sbydata = cfg.split('\n') + cfgdata, tasklist = read_sbyconfig(sbydata, 'a') + + self.assertIn('[engines]', cfgdata) + i = cfgdata.index('[engines]') + self.assertSequenceEqual(['[engines]', 'smtbmc'], cfgdata[index:index+1]) + print(cfgdata) + print(tasklist) + \ No newline at end of file