mirror of https://github.com/Desuuuu/klipper.git
test: Add basic klippy regression tests
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
780e3c3022
commit
eb801631b9
|
@ -0,0 +1,138 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Test cases
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
class error(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestCase:
|
||||||
|
def __init__(self, fname, dictdir, tempdir):
|
||||||
|
self.fname = fname
|
||||||
|
self.dictdir = dictdir
|
||||||
|
self.tempdir = tempdir
|
||||||
|
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
|
||||||
|
config_fname = gcode_fname = dict_fname = None
|
||||||
|
should_fail = multi_tests = False
|
||||||
|
gcode = []
|
||||||
|
f = open(self.fname, 'rb')
|
||||||
|
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
|
||||||
|
self.launch_test(config_fname, dict_fname,
|
||||||
|
gcode_fname, gcode, should_fail)
|
||||||
|
config_fname = self.relpath(parts[1])
|
||||||
|
if multi_tests:
|
||||||
|
self.launch_test(config_fname, dict_fname,
|
||||||
|
gcode_fname, gcode, should_fail)
|
||||||
|
elif parts[0] == "DICTIONARY":
|
||||||
|
dict_fname = self.relpath(parts[1], 'dict')
|
||||||
|
elif parts[0] == "GCODE":
|
||||||
|
gcode_fname = self.relpath(parts[1])
|
||||||
|
elif parts[0] == "SHOULD_FAIL":
|
||||||
|
should_fail = True
|
||||||
|
else:
|
||||||
|
gcode.append(line)
|
||||||
|
f.close()
|
||||||
|
if not multi_tests:
|
||||||
|
self.launch_test(config_fname, dict_fname,
|
||||||
|
gcode_fname, gcode, should_fail)
|
||||||
|
def launch_test(self, config_fname, dict_fname, gcode_fname, gcode,
|
||||||
|
should_fail):
|
||||||
|
gcode_is_temp = False
|
||||||
|
if gcode_fname is None:
|
||||||
|
gcode_fname = self.relpath("_test_.gcode", 'temp')
|
||||||
|
gcode_is_temp = True
|
||||||
|
f = open(gcode_fname, 'wb')
|
||||||
|
f.write('\n'.join(gcode))
|
||||||
|
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")
|
||||||
|
if dict_fname is None:
|
||||||
|
raise error("data dictionary file not specified")
|
||||||
|
# Call klippy
|
||||||
|
sys.stderr.write("\n Starting %s (%s)\n" % (
|
||||||
|
self.fname, os.path.basename(config_fname)))
|
||||||
|
args = [sys.executable, './klippy/klippy.py', config_fname,
|
||||||
|
'-i', gcode_fname, '-o', '/dev/null',
|
||||||
|
'-d', dict_fname
|
||||||
|
]
|
||||||
|
res = subprocess.call(args)
|
||||||
|
if should_fail:
|
||||||
|
if not res:
|
||||||
|
raise error("Test failed to raise and error")
|
||||||
|
else:
|
||||||
|
if res:
|
||||||
|
raise error("Error during test")
|
||||||
|
# Do cleanup
|
||||||
|
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"
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# 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")
|
||||||
|
options, args = opts.parse_args()
|
||||||
|
if len(args) < 1:
|
||||||
|
opts.error("Incorrect number of arguments")
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
dictdir = options.dictdir
|
||||||
|
tempdir = options.tempdir
|
||||||
|
|
||||||
|
# Run each test
|
||||||
|
for fname in args:
|
||||||
|
tc = TestCase(fname, dictdir, tempdir)
|
||||||
|
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()
|
|
@ -39,6 +39,5 @@ mkdir -p ${HOSTDIR}
|
||||||
|
|
||||||
echo "travis_fold:start:klippy"
|
echo "travis_fold:start:klippy"
|
||||||
echo "=============== Test invoke klippy"
|
echo "=============== Test invoke klippy"
|
||||||
$PYTHON klippy/klippy.py config/example.cfg -i /dev/null -o ${HOSTDIR}/output -v -d ${DICTDIR}/atmega2560-16mhz.dict
|
$PYTHON scripts/test_klippy.py -d ${DICTDIR} test/klippy/*.test
|
||||||
$PYTHON klippy/parsedump.py ${DICTDIR}/atmega2560-16mhz.dict ${HOSTDIR}/output > ${HOSTDIR}/output-parsed
|
|
||||||
echo "travis_fold:end:klippy"
|
echo "travis_fold:end:klippy"
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Test case for basic cartesian movement
|
||||||
|
CONFIG ../../config/example.cfg
|
||||||
|
DICTIONARY atmega2560-16mhz.dict
|
||||||
|
GCODE move.gcode
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Test case for basic corexy movement
|
||||||
|
CONFIG ../../config/example-corexy.cfg
|
||||||
|
DICTIONARY atmega2560-16mhz.dict
|
||||||
|
GCODE move.gcode
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Test case for basic movement on delta printers
|
||||||
|
CONFIG ../../config/example-delta.cfg
|
||||||
|
DICTIONARY atmega2560-16mhz.dict
|
||||||
|
|
||||||
|
# Start by homing the printer. Also tests Z moves.
|
||||||
|
G28
|
||||||
|
|
||||||
|
# Perform an XY+Z move with infintesimal XY component
|
||||||
|
G1 x0 y0 z15
|
||||||
|
|
||||||
|
# Perform an XY move along Y axis (aligned with rear tower)
|
||||||
|
G1 x0 y5 z15
|
||||||
|
|
||||||
|
# Perform an XY+Z move along Y axis
|
||||||
|
G1 x0 y-5 z10
|
||||||
|
|
||||||
|
# Perform a Z move
|
||||||
|
G1 x0 y-5 z15
|
||||||
|
|
||||||
|
# Perform an XY move across all three towers
|
||||||
|
G1 x2 y2 z10
|
||||||
|
|
||||||
|
# Perform an XY+Z move with tiny Z movement
|
||||||
|
G1 x2 y-10 z10.1
|
||||||
|
|
||||||
|
# Move to far away position
|
||||||
|
G1 x140 y0
|
||||||
|
|
||||||
|
# Move to extreme position
|
||||||
|
G1 x145 y0
|
||||||
|
|
||||||
|
# Move to another extreme position
|
||||||
|
G1 x145 y5
|
|
@ -0,0 +1,22 @@
|
||||||
|
; Simple movement tests
|
||||||
|
|
||||||
|
; Start by homing the printer.
|
||||||
|
G28
|
||||||
|
G1 F6000
|
||||||
|
|
||||||
|
; Z / X / Y moves
|
||||||
|
G1 Z1
|
||||||
|
G1 X1
|
||||||
|
G1 Y1
|
||||||
|
|
||||||
|
; diagonal moves
|
||||||
|
G1 X0 Y0
|
||||||
|
G1 X1 Z2
|
||||||
|
G1 X0 Y1 Z1
|
||||||
|
|
||||||
|
; extrude only moves
|
||||||
|
G1 E1
|
||||||
|
G1 E0
|
||||||
|
|
||||||
|
; regular extrude move
|
||||||
|
G1 X0 Y0 E.01
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Test that basic bounds checks work
|
||||||
|
CONFIG ../../config/example.cfg
|
||||||
|
DICTIONARY atmega2560-16mhz.dict
|
||||||
|
SHOULD_FAIL
|
||||||
|
|
||||||
|
# Home the printer, and then attempt to move to an obviously bad location
|
||||||
|
G28
|
||||||
|
G1 Y9999
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Basic sanity checks on the example printer config files
|
||||||
|
GCODE move.gcode
|
||||||
|
|
||||||
|
# Printers using the atmega2560
|
||||||
|
DICTIONARY atmega2560-16mhz.dict
|
||||||
|
CONFIG ../../config/generic-einsy-rambo.cfg
|
||||||
|
CONFIG ../../config/generic-mini-rambo.cfg
|
||||||
|
CONFIG ../../config/generic-rambo.cfg
|
||||||
|
CONFIG ../../config/generic-ramps.cfg
|
||||||
|
CONFIG ../../config/generic-rumba.cfg
|
||||||
|
CONFIG ../../config/printer-anycubic-i3-mega-2017.cfg
|
||||||
|
CONFIG ../../config/printer-anycubic-kossel-2016.cfg
|
||||||
|
CONFIG ../../config/printer-creality-cr10s-2017.cfg
|
||||||
|
CONFIG ../../config/printer-lulzbot-taz6-2017.cfg
|
||||||
|
CONFIG ../../config/printer-makergear-m2-2012.cfg
|
||||||
|
CONFIG ../../config/printer-seemecnc-rostock-max-v2-2015.cfg
|
||||||
|
CONFIG ../../config/printer-wanhao-duplicator-i3-plus-2017.cfg
|
||||||
|
|
||||||
|
# Printers using the atmega1284p
|
||||||
|
DICTIONARY atmega1284p.dict
|
||||||
|
CONFIG ../../config/generic-melzi.cfg
|
||||||
|
CONFIG ../../config/printer-anet-a8-2017.cfg
|
||||||
|
CONFIG ../../config/printer-anet-e10-2018.cfg
|
||||||
|
CONFIG ../../config/printer-creality-cr10-2017.cfg
|
||||||
|
CONFIG ../../config/printer-creality-cr10mini-2017.cfg
|
||||||
|
CONFIG ../../config/printer-creality-ender3-2018.cfg
|
||||||
|
CONFIG ../../config/printer-tronxy-x5s-2018.cfg
|
||||||
|
CONFIG ../../config/printer-wanhao-duplicator-i3-v2.1-2017.cfg
|
||||||
|
|
||||||
|
# Printers using the at90usb1286
|
||||||
|
DICTIONARY at90usb1286.dict
|
||||||
|
CONFIG ../../config/generic-printrboard.cfg
|
||||||
|
|
||||||
|
# Printers using the sam3x8e
|
||||||
|
DICTIONARY sam3x8e.dict
|
||||||
|
CONFIG ../../config/generic-radds.cfg
|
||||||
|
|
||||||
|
# Printers using the lpc176x
|
||||||
|
DICTIONARY lpc176x.dict
|
||||||
|
CONFIG ../../config/generic-smoothieboard.cfg
|
Loading…
Reference in New Issue