2010-07-07 02:54:16 +04:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
#
|
|
|
|
# Regression test driver for cmd-line tools
|
|
|
|
#
|
2010-08-24 16:52:23 +04:00
|
|
|
# Usage: test_cmdline_tool.py [<options>] <tool> <arguments>
|
2010-07-07 02:54:16 +04:00
|
|
|
#
|
2010-07-11 23:06:14 +04:00
|
|
|
# If the -g option is given or the TEST_GENERATE environment variable is set to 1,
|
|
|
|
# *-expected.<suffix> files will be generated instead of running the tests.
|
2010-07-07 02:54:16 +04:00
|
|
|
#
|
2010-07-11 23:06:14 +04:00
|
|
|
# Any generated output is written to the file `basename <argument`-actual.<suffix>
|
2010-07-07 02:54:16 +04:00
|
|
|
# Any warning or errors are written to stderr.
|
|
|
|
#
|
|
|
|
# Returns 0 on passed test
|
|
|
|
# 1 on error
|
|
|
|
# 2 on invalid cmd-line options
|
|
|
|
#
|
|
|
|
# Author: Marius Kintel <marius@kintel.net>
|
|
|
|
#
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import glob
|
|
|
|
import subprocess
|
|
|
|
import re
|
|
|
|
import getopt
|
2011-09-22 05:11:01 +04:00
|
|
|
import shutil
|
|
|
|
import platform
|
2010-07-07 02:54:16 +04:00
|
|
|
|
|
|
|
def initialize_environment():
|
|
|
|
if not options.generate: options.generate = bool(os.getenv("TEST_GENERATE"))
|
|
|
|
return True
|
|
|
|
|
2010-09-07 04:04:38 +04:00
|
|
|
def init_expected_filename(testname, cmd):
|
|
|
|
global expecteddir, expectedfilename
|
|
|
|
expecteddir = os.path.join(options.regressiondir, os.path.split(cmd)[1])
|
2011-08-05 05:48:35 +04:00
|
|
|
expectedfilename = os.path.join(expecteddir, testname + "-expected." + options.suffix)
|
2010-09-07 04:04:38 +04:00
|
|
|
|
2010-08-24 16:52:23 +04:00
|
|
|
def verify_test(testname, cmd):
|
2010-09-07 04:04:38 +04:00
|
|
|
global expectedfilename
|
2010-07-07 02:54:16 +04:00
|
|
|
if not options.generate:
|
2010-08-24 16:52:23 +04:00
|
|
|
if not os.path.isfile(expectedfilename):
|
|
|
|
print >> sys.stderr, "Error: test '%s' is missing expected output in %s" % (testname, expectedfilename)
|
2010-07-07 02:54:16 +04:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def execute_and_redirect(cmd, params, outfile):
|
2011-09-18 01:13:37 +04:00
|
|
|
retval = -1
|
|
|
|
try:
|
|
|
|
proc = subprocess.Popen([cmd] + params, stdout=outfile)
|
|
|
|
retval = proc.wait()
|
2011-09-19 02:53:33 +04:00
|
|
|
except:
|
|
|
|
print >> sys.stderr, "Error running subprocess: ", sys.exc_info()[1]
|
2011-09-18 01:13:37 +04:00
|
|
|
print >> sys.stderr, " cmd:", cmd
|
|
|
|
print >> sys.stderr, " params:", params
|
|
|
|
print >> sys.stderr, " outfile:", outfile
|
2010-07-07 02:54:16 +04:00
|
|
|
return retval
|
|
|
|
|
|
|
|
def get_normalized_text(filename):
|
|
|
|
text = open(filename).read()
|
|
|
|
return text.strip("\r\n").replace("\r\n", "\n") + "\n"
|
|
|
|
|
|
|
|
def compare_text(expected, actual):
|
|
|
|
return get_normalized_text(expected) == get_normalized_text(actual)
|
|
|
|
|
2011-08-05 05:48:35 +04:00
|
|
|
def compare_default(resultfilename):
|
|
|
|
if not compare_text(expectedfilename, resultfilename):
|
|
|
|
execute_and_redirect("diff", [expectedfilename, resultfilename], sys.stderr)
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2011-09-22 05:11:01 +04:00
|
|
|
def append_html_output(expectedfilename, resultfilename):
|
2011-10-16 07:59:54 +04:00
|
|
|
# 1 if html directory & file not there, create them
|
|
|
|
# 2 copy expected filename image and result filename image to dir
|
|
|
|
# 3 append html to show differences
|
|
|
|
# 4 dump platform.platform()
|
2011-09-22 05:11:01 +04:00
|
|
|
expectedimg = os.path.basename(expectedfilename)
|
|
|
|
resultimg = os.path.basename(resultfilename)
|
|
|
|
template = '''
|
|
|
|
<p>
|
|
|
|
<div style="border:1px solid gray;padding:10px;">
|
|
|
|
Test command: <b>///TESTCMD///</b> Test name: <b>///TESTNAME///</b> <br/>
|
|
|
|
<div style="float: left; width: 50%;">
|
|
|
|
<br/>
|
|
|
|
Expected:<br/>
|
|
|
|
<img style="border:1px solid gray; width:90%;" src="///EXPECTED///"/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div style="float: right; width: 50%;">
|
|
|
|
Actual:<br/>
|
|
|
|
(Platform: ///PLATFORM///)<br/>
|
|
|
|
<img style="border:1px solid gray; width:90%;" src="///RESULT///"/>
|
|
|
|
</div>
|
|
|
|
<br style="clear:both;"/>
|
|
|
|
</div>
|
|
|
|
<p>
|
|
|
|
'''
|
|
|
|
html = template
|
|
|
|
html = html.replace('///EXPECTED///',expectedimg)
|
|
|
|
html = html.replace('///RESULT///',resultimg)
|
|
|
|
html = html.replace('///TESTCMD///',os.path.basename(options.cmd))
|
|
|
|
html = html.replace('///TESTNAME///',options.testname)
|
|
|
|
html = html.replace('///PLATFORM///',platform.platform())
|
|
|
|
try:
|
|
|
|
shutil.copy(expectedfilename,options.imgdiff_dir)
|
|
|
|
shutil.copy(resultfilename,options.imgdiff_dir)
|
|
|
|
f = open(options.imgdiff_htmlfile,'a')
|
|
|
|
f.write(html)
|
|
|
|
f.close()
|
|
|
|
print >> sys.stderr, "appended " + options.imgdiff_htmlfile
|
|
|
|
except:
|
|
|
|
print >> sys.stderr, "error appending " + options.imgdiff_htmlfile
|
|
|
|
print >> sys.stderr, sys.exc_info()
|
|
|
|
|
2011-08-05 05:48:35 +04:00
|
|
|
def compare_png(resultfilename):
|
2011-09-19 02:53:33 +04:00
|
|
|
if not resultfilename:
|
|
|
|
print >> sys.stderr, "Error: OpenSCAD did not generate an image"
|
|
|
|
return False
|
|
|
|
print >> sys.stderr, 'Yee image compare: ', expectedfilename, ' ', resultfilename
|
2011-10-10 08:45:53 +04:00
|
|
|
if execute_and_redirect("./yee_compare", [expectedfilename, resultfilename, "-downsample", "2"], sys.stderr) != 0:
|
2011-09-22 05:11:01 +04:00
|
|
|
append_html_output(expectedfilename, resultfilename)
|
2011-08-05 05:48:35 +04:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2010-09-07 04:04:38 +04:00
|
|
|
def compare_with_expected(resultfilename):
|
|
|
|
if not options.generate:
|
2011-08-05 05:48:35 +04:00
|
|
|
if "compare_" + options.suffix in globals(): return globals()["compare_" + options.suffix](resultfilename)
|
|
|
|
else: return compare_default(resultfilename)
|
2010-09-07 04:04:38 +04:00
|
|
|
return True
|
|
|
|
|
2010-08-24 16:52:23 +04:00
|
|
|
def run_test(testname, cmd, args):
|
2010-07-07 02:54:16 +04:00
|
|
|
cmdname = os.path.split(options.cmd)[1]
|
|
|
|
|
2010-08-31 01:57:47 +04:00
|
|
|
outputdir = os.path.join(os.getcwd(), cmdname + "-output")
|
2011-08-05 05:48:35 +04:00
|
|
|
actualfilename = os.path.join(outputdir, testname + "-actual." + options.suffix)
|
2011-10-16 07:59:54 +04:00
|
|
|
actualfilename = os.path.normpath(actualfilename)
|
2010-07-07 02:54:16 +04:00
|
|
|
|
|
|
|
if options.generate:
|
2010-07-07 03:44:41 +04:00
|
|
|
if not os.path.exists(expecteddir): os.makedirs(expecteddir)
|
2010-07-07 02:54:16 +04:00
|
|
|
outputname = expectedfilename
|
|
|
|
else:
|
|
|
|
if not os.path.exists(outputdir): os.makedirs(outputdir)
|
|
|
|
outputname = actualfilename
|
2011-10-16 07:59:54 +04:00
|
|
|
outputname = os.path.normpath( outputname )
|
|
|
|
|
2010-07-07 02:54:16 +04:00
|
|
|
outfile = open(outputname, "wb")
|
2010-08-28 17:16:03 +04:00
|
|
|
try:
|
2011-10-16 07:59:54 +04:00
|
|
|
proc = subprocess.Popen([cmd] + args + [outputname], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
2010-08-28 17:16:03 +04:00
|
|
|
errtext = proc.communicate()[1]
|
|
|
|
if errtext != None and len(errtext) > 0:
|
|
|
|
print >> sys.stderr, "Error output: " + errtext
|
|
|
|
outfile.close()
|
|
|
|
if proc.returncode != 0:
|
|
|
|
print >> sys.stderr, "Error: %s failed with return code %d" % (cmdname, proc.returncode)
|
2010-09-07 04:04:38 +04:00
|
|
|
return None
|
2010-08-28 17:16:03 +04:00
|
|
|
|
2010-09-07 04:04:38 +04:00
|
|
|
return outputname
|
2010-08-28 17:16:03 +04:00
|
|
|
except OSError, err:
|
|
|
|
print >> sys.stderr, "Error: %s \"%s\"" % (err.strerror, cmd)
|
2010-09-07 04:04:38 +04:00
|
|
|
return None
|
2010-07-07 02:54:16 +04:00
|
|
|
|
|
|
|
class Options:
|
|
|
|
def __init__(self):
|
|
|
|
self.__dict__['options'] = {}
|
|
|
|
def __setattr__(self, name, value):
|
|
|
|
self.options[name] = value
|
|
|
|
def __getattr__(self, name):
|
|
|
|
return self.options[name]
|
|
|
|
|
|
|
|
def usage():
|
|
|
|
print >> sys.stderr, "Usage: " + sys.argv[0] + " [<options>] <cmdline-tool> <argument>"
|
|
|
|
print >> sys.stderr, "Options:"
|
2010-08-24 16:52:23 +04:00
|
|
|
print >> sys.stderr, " -g, --generate Generate expected output for the given tests"
|
|
|
|
print >> sys.stderr, " -s, --suffix=<suffix> Write -expected and -actual files with the given suffix instead of .txt"
|
|
|
|
print >> sys.stderr, " -t, --test=<name> Specify test name instead of deducting it from the argument"
|
2010-07-07 02:54:16 +04:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
# Handle command-line arguments
|
|
|
|
try:
|
2010-08-24 16:52:23 +04:00
|
|
|
opts, args = getopt.getopt(sys.argv[1:], "gs:t:", ["generate", "suffix=", "test="])
|
2010-07-07 02:54:16 +04:00
|
|
|
except getopt.GetoptError, err:
|
|
|
|
usage()
|
|
|
|
sys.exit(2)
|
|
|
|
|
|
|
|
global options
|
|
|
|
options = Options()
|
2010-08-24 16:52:23 +04:00
|
|
|
options.regressiondir = os.path.join(os.path.split(sys.argv[0])[0], "regression")
|
2010-07-07 02:54:16 +04:00
|
|
|
options.generate = False
|
2011-08-05 05:48:35 +04:00
|
|
|
options.suffix = "txt"
|
2011-09-22 05:11:01 +04:00
|
|
|
|
|
|
|
options.imgdiff_dir = 'imgdiff-fail'
|
|
|
|
options.imgdiff_htmlfile = os.path.join(options.imgdiff_dir,'failed.html')
|
|
|
|
try:
|
|
|
|
if not os.path.isdir(options.imgdiff_dir):
|
|
|
|
os.mkdir(options.imgdiff_dir)
|
|
|
|
except:
|
|
|
|
print >> sys.stderr, "error creating " + options.imgdiff_dir, sys.exc_info()
|
|
|
|
|
2010-07-07 02:54:16 +04:00
|
|
|
for o, a in opts:
|
|
|
|
if o in ("-g", "--generate"): options.generate = True
|
2010-08-24 16:52:23 +04:00
|
|
|
elif o in ("-s", "--suffix"):
|
2011-08-05 05:48:35 +04:00
|
|
|
if a[0] == '.': options.suffix = a[1:]
|
|
|
|
else: options.suffix = a
|
2010-08-24 16:52:23 +04:00
|
|
|
elif o in ("-t", "--test"):
|
|
|
|
options.testname = a
|
2011-09-22 05:11:01 +04:00
|
|
|
|
2010-07-07 02:54:16 +04:00
|
|
|
# <cmdline-tool> and <argument>
|
2010-08-24 16:52:23 +04:00
|
|
|
if len(args) < 2:
|
2010-07-07 02:54:16 +04:00
|
|
|
usage()
|
|
|
|
sys.exit(2)
|
|
|
|
options.cmd = args[0]
|
2010-08-24 16:52:23 +04:00
|
|
|
|
|
|
|
# If only one test file, we can usually deduct the test name from the file
|
|
|
|
if len(args) == 2:
|
|
|
|
basename = os.path.splitext(args[1])[0]
|
|
|
|
path, options.testname = os.path.split(basename)
|
|
|
|
|
|
|
|
if not hasattr(options, "testname"):
|
|
|
|
print >> sys.stderr, "Test name cannot be deducted from arguments. Specify test name using the -t option"
|
|
|
|
sys.exit(2)
|
2010-07-07 02:54:16 +04:00
|
|
|
|
|
|
|
# Initialize and verify run-time environment
|
|
|
|
if not initialize_environment(): sys.exit(1)
|
|
|
|
|
2010-09-07 04:04:38 +04:00
|
|
|
init_expected_filename(options.testname, options.cmd)
|
|
|
|
|
2010-07-07 02:54:16 +04:00
|
|
|
# Verify test environment
|
2010-09-07 04:04:38 +04:00
|
|
|
verification = verify_test(options.testname, options.cmd)
|
|
|
|
|
|
|
|
resultfile = run_test(options.testname, options.cmd, args[1:])
|
2010-07-07 02:54:16 +04:00
|
|
|
|
2010-09-07 04:04:38 +04:00
|
|
|
if not verification or not compare_with_expected(resultfile): exit(1)
|