2017-09-12 02:53:32 +02:00
|
|
|
# Regression test helper script
|
|
|
|
#
|
|
|
|
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
|
|
|
#
|
|
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
import sys, os, optparse, logging, subprocess
|
|
|
|
|
2018-06-17 17:48:06 +02:00
|
|
|
TEMP_GCODE_FILE = "_test_.gcode"
|
|
|
|
TEMP_LOG_FILE = "_test_.log"
|
2018-06-29 19:52:23 +02:00
|
|
|
TEMP_OUTPUT_FILE = "_test_output"
|
2018-06-17 17:48:06 +02:00
|
|
|
|
2017-09-12 02:53:32 +02:00
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Test cases
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
class error(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class TestCase:
|
2018-06-30 18:20:42 +02:00
|
|
|
def __init__(self, fname, dictdir, tempdir, verbose, keepfiles):
|
2017-09-12 02:53:32 +02:00
|
|
|
self.fname = fname
|
|
|
|
self.dictdir = dictdir
|
|
|
|
self.tempdir = tempdir
|
2018-06-17 17:48:06 +02:00
|
|
|
self.verbose = verbose
|
2018-06-30 18:20:42 +02:00
|
|
|
self.keepfiles = keepfiles
|
2017-09-12 02:53:32 +02:00
|
|
|
def relpath(self, fname, rel='test'):
|
|
|
|
if rel == 'dict':
|
|
|
|
reldir = self.dictdir
|
|
|
|
elif rel == 'temp':
|
|
|
|
reldir = self.tempdir
|
|
|
|
else:
|
|
|
|
reldir = os.path.dirname(self.fname)
|
|
|
|
return os.path.join(reldir, fname)
|
|
|
|
def parse_test(self):
|
|
|
|
# Parse file into test cases
|
2018-06-29 19:52:23 +02:00
|
|
|
config_fname = gcode_fname = dict_fnames = None
|
2017-09-12 02:53:32 +02:00
|
|
|
should_fail = multi_tests = False
|
|
|
|
gcode = []
|
2020-06-12 04:48:18 +02:00
|
|
|
f = open(self.fname, 'r')
|
2017-09-12 02:53:32 +02:00
|
|
|
for line in f:
|
|
|
|
cpos = line.find('#')
|
|
|
|
if cpos >= 0:
|
|
|
|
line = line[:cpos]
|
|
|
|
parts = line.strip().split()
|
|
|
|
if not parts:
|
|
|
|
continue
|
|
|
|
if parts[0] == "CONFIG":
|
|
|
|
if config_fname is not None:
|
|
|
|
# Multiple tests in same file
|
|
|
|
if not multi_tests:
|
|
|
|
multi_tests = True
|
2018-06-29 19:52:23 +02:00
|
|
|
self.launch_test(config_fname, dict_fnames,
|
2017-09-12 02:53:32 +02:00
|
|
|
gcode_fname, gcode, should_fail)
|
|
|
|
config_fname = self.relpath(parts[1])
|
|
|
|
if multi_tests:
|
2018-06-29 19:52:23 +02:00
|
|
|
self.launch_test(config_fname, dict_fnames,
|
2017-09-12 02:53:32 +02:00
|
|
|
gcode_fname, gcode, should_fail)
|
|
|
|
elif parts[0] == "DICTIONARY":
|
2018-06-29 19:52:23 +02:00
|
|
|
dict_fnames = [self.relpath(parts[1], 'dict')]
|
|
|
|
for mcu_dict in parts[2:]:
|
|
|
|
mcu, fname = mcu_dict.split('=', 1)
|
|
|
|
dict_fnames.append('%s=%s' % (
|
|
|
|
mcu.strip(), self.relpath(fname.strip(), 'dict')))
|
2017-09-12 02:53:32 +02:00
|
|
|
elif parts[0] == "GCODE":
|
|
|
|
gcode_fname = self.relpath(parts[1])
|
|
|
|
elif parts[0] == "SHOULD_FAIL":
|
|
|
|
should_fail = True
|
|
|
|
else:
|
2018-06-17 02:42:13 +02:00
|
|
|
gcode.append(line.strip())
|
2017-09-12 02:53:32 +02:00
|
|
|
f.close()
|
|
|
|
if not multi_tests:
|
2018-06-29 19:52:23 +02:00
|
|
|
self.launch_test(config_fname, dict_fnames,
|
2017-09-12 02:53:32 +02:00
|
|
|
gcode_fname, gcode, should_fail)
|
2018-06-29 19:52:23 +02:00
|
|
|
def launch_test(self, config_fname, dict_fnames, gcode_fname, gcode,
|
2017-09-12 02:53:32 +02:00
|
|
|
should_fail):
|
|
|
|
gcode_is_temp = False
|
|
|
|
if gcode_fname is None:
|
2018-06-17 17:48:06 +02:00
|
|
|
gcode_fname = self.relpath(TEMP_GCODE_FILE, 'temp')
|
2017-09-12 02:53:32 +02:00
|
|
|
gcode_is_temp = True
|
2020-06-12 04:48:18 +02:00
|
|
|
f = open(gcode_fname, 'w')
|
2018-06-17 02:42:13 +02:00
|
|
|
f.write('\n'.join(gcode + ['']))
|
2017-09-12 02:53:32 +02:00
|
|
|
f.close()
|
|
|
|
elif gcode:
|
|
|
|
raise error("Can't specify both a gcode file and gcode commands")
|
|
|
|
if config_fname is None:
|
|
|
|
raise error("config file not specified")
|
2018-06-29 19:52:23 +02:00
|
|
|
if dict_fnames is None:
|
2017-09-12 02:53:32 +02:00
|
|
|
raise error("data dictionary file not specified")
|
|
|
|
# Call klippy
|
2018-06-17 17:48:06 +02:00
|
|
|
sys.stderr.write(" Starting %s (%s)\n" % (
|
2017-09-12 02:53:32 +02:00
|
|
|
self.fname, os.path.basename(config_fname)))
|
2018-06-29 19:52:23 +02:00
|
|
|
args = [ sys.executable, './klippy/klippy.py', config_fname,
|
|
|
|
'-i', gcode_fname, '-o', TEMP_OUTPUT_FILE, '-v' ]
|
|
|
|
for df in dict_fnames:
|
|
|
|
args += ['-d', df]
|
2018-06-17 17:48:06 +02:00
|
|
|
if not self.verbose:
|
|
|
|
args += ['-l', TEMP_LOG_FILE]
|
2017-09-12 02:53:32 +02:00
|
|
|
res = subprocess.call(args)
|
2018-06-17 17:48:06 +02:00
|
|
|
is_fail = (should_fail and not res) or (not should_fail and res)
|
|
|
|
if is_fail:
|
|
|
|
if not self.verbose:
|
|
|
|
self.show_log()
|
|
|
|
if should_fail:
|
2018-06-17 02:42:13 +02:00
|
|
|
raise error("Test failed to raise an error")
|
2018-06-17 17:48:06 +02:00
|
|
|
raise error("Error during test")
|
2017-09-12 02:53:32 +02:00
|
|
|
# Do cleanup
|
2018-06-30 18:20:42 +02:00
|
|
|
if self.keepfiles:
|
|
|
|
return
|
2018-06-29 19:52:23 +02:00
|
|
|
for fname in os.listdir(self.tempdir):
|
|
|
|
if fname.startswith(TEMP_OUTPUT_FILE):
|
|
|
|
os.unlink(fname)
|
2018-06-17 17:48:06 +02:00
|
|
|
if not self.verbose:
|
|
|
|
os.unlink(TEMP_LOG_FILE)
|
|
|
|
else:
|
|
|
|
sys.stderr.write('\n')
|
2017-09-12 02:53:32 +02:00
|
|
|
if gcode_is_temp:
|
|
|
|
os.unlink(gcode_fname)
|
|
|
|
def run(self):
|
|
|
|
try:
|
|
|
|
self.parse_test()
|
|
|
|
except error as e:
|
|
|
|
return str(e)
|
|
|
|
except Exception:
|
|
|
|
logging.exception("Unhandled exception during test run")
|
|
|
|
return "internal error"
|
|
|
|
return "success"
|
2018-06-17 17:48:06 +02:00
|
|
|
def show_log(self):
|
2020-06-12 04:48:18 +02:00
|
|
|
f = open(TEMP_LOG_FILE, 'r')
|
2018-06-17 17:48:06 +02:00
|
|
|
data = f.read()
|
|
|
|
f.close()
|
|
|
|
sys.stdout.write(data)
|
2017-09-12 02:53:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Startup
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def main():
|
|
|
|
# Parse args
|
|
|
|
usage = "%prog [options] <test cases>"
|
|
|
|
opts = optparse.OptionParser(usage)
|
|
|
|
opts.add_option("-d", "--dictdir", dest="dictdir", default=".",
|
|
|
|
help="directory for dictionary files")
|
|
|
|
opts.add_option("-t", "--tempdir", dest="tempdir", default=".",
|
|
|
|
help="directory for temporary files")
|
2018-06-30 18:20:42 +02:00
|
|
|
opts.add_option("-k", action="store_true", dest="keepfiles",
|
|
|
|
help="do not remove temporary files")
|
2018-06-17 17:48:06 +02:00
|
|
|
opts.add_option("-v", action="store_true", dest="verbose",
|
|
|
|
help="show all output from tests")
|
2017-09-12 02:53:32 +02:00
|
|
|
options, args = opts.parse_args()
|
|
|
|
if len(args) < 1:
|
|
|
|
opts.error("Incorrect number of arguments")
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
|
|
|
|
# Run each test
|
|
|
|
for fname in args:
|
2018-06-30 18:20:42 +02:00
|
|
|
tc = TestCase(fname, options.dictdir, options.tempdir, options.verbose,
|
|
|
|
options.keepfiles)
|
2017-09-12 02:53:32 +02:00
|
|
|
res = tc.run()
|
|
|
|
if res != 'success':
|
|
|
|
sys.stderr.write("\n\nTest case %s FAILED (%s)!\n\n" % (fname, res))
|
|
|
|
sys.exit(-1)
|
|
|
|
|
|
|
|
sys.stderr.write("\n All %d test cases passed\n" % (len(args),))
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|