# usage: python check.py --help
#
#@author: michael.karlen@epfl.ch
#@version: 1.1
#@date: 2. 2. 2006
#
#~iclamp/soft/bin/risc-emu
COMPILER = "scala -cp classes zweic.GeneratorTest"
#RISC = "java -cp risc/lib/risc.jar risc.emulator.Main"
RISC = "/home/iclamp/soft/bin/risc-emu"
OUTDIR = "asm"
TABBING = '30'
__doc__ = """
THIS CODE COMES WITH ABSOLUTELY NO WARRANTY
This script helps you create automated unit tests for your Zwei
parser. The idea is to put meta-comments into your zwei sources that
indicate the desired output of your program. The script then compiles
your script, executes it and compares stdout with your metainformation.
usage: python unittest.py --help
python unittest.py tests/5/*.zwei
EXECUTING:
----------
These two commands have to work in your shell:
scala -cp classes zweic.GeneratorTest
java -cp risc/lib/risc.jar risc.emulator.Main
risc.emulator.Main can be downloaded from the lamp homepage. If you
are using different paths, please adapt them either by command line
arguments or by changing the constants COMPILER and ASSEMBLER in the
beginning of the file.
The directory 'asm' will be created to store compiled sources. You
also can adapt this.
WARNING: Existing files will be replaced!
TESTFILE STRUCTURE:
-------------------
** Variant 1 **
As soon as a comment with the syntax
//#isut
is encountered (no space between '#' and 'isut'), all following
comments containing a '#' will be taken into consideration.
//#spam\n
for example expects 'spam' plus newline (printChar(10)) as output.
Before the has you are alwas allowed to put any normal comments.
//this is a comment #eggandbacon
//this is still a normal comment
//#ham
expects 'eggandbaconham' as output.
** Variant 2 **
Meta information is tagged by '//#startut ... //#endut'. All
comments figuring in between are expected as output of your code.
The '#endut' tag is optional.
//#startut
//scala\n
//genau
//#endut
//any normal comment here
expects 'scala' newline 'genau' as output of your script.
Please refer to the examples shipped with this code for reference.
KNOWN BUGS
----------
- readChar does not work.
ANYTHING?
---------
Suggestions are welcome: michael.karlen@epfl.ch
Please share your testcases!
happy coding.
"""
import sys, os, re
from optparse import OptionParser
rx_isut = re.compile(r'//[^#]*#' \
'isut', re.I)
rx_startut = re.compile(r'//[^#]*#' \
'startut', re.I)
rx_endut = re.compile(r'//[^#]*#' \
'endut', re.I)
rx_tl = re.compile(r'//[^#]*#' \
r'(?!isut)' \
r'(.*)', re.I)
rx_comment = re.compile(r'//(.*)', re.I)
class Checker:
c_tested = 0
c_broken = 0
c_other = 0
def __init__(self, spath, options):
self.spreadUT = False
self.utDef = False
self.isUT = False
fname = os.path.splitext(os.path.basename(spath))[0]
self.options = options
self.compile(spath, fname)
solution = self.parseUnitTest(spath)
if self.isUT:
self.check(fname, solution)
Checker.c_tested += 1
else:
Checker.c_other += 1
print
def compile(self, spath, fname):
print '> compiling %s' % fname,
# call compiler via internal shell and redirect output to file
pipes = os.popen3(r'%s %s > %s' %
(self.options.compiler, spath,
os.path.join(self.options.outdir, fname+'.asm')))
errs = pipes[2].read()
print '.'
if errs:
print >>sys.stderr, errs
print >>sys.stderr
#sys.exit(1)
def check(self, fname, solution):
# call risc emu via internal shell and pipe output
pipes = os.popen3(r'%s asm/%s.asm' % (self.options.risc, fname))
errs = pipes[2].read()
if errs:
print >>sys.stderr, errs
print >>sys.stderr
#sys.exit(2)
answer = pipes[1].read()
if solution == answer:
print "> tested: ok"
else:
Checker.c_broken += 1
self.error(fname, solution, answer)
def parseUnitTest(self, path):
ret = []
for line in open(path):
if rx_endut.search(line):
#this is a file width a meta header: stop
self.utDef = False
elif self.utDef == True:
#this is a file width a meta-header
match = rx_comment.search(line)
if match:
f = match.group(1).replace(r'\n','\n')
ret.append(f)
elif rx_isut.search(line):
#this is a file spread meta information
self.spreadUT = True
self.isUT = True
elif rx_startut.search(line):
#this is a file width a meta header: start
self.utDef = True
self.isUT = True
else:
match = rx_tl.search(line)
if self.spreadUT and match:
#replace newlines
f = match.group(1).replace(r'\n','\n')
ret.append(f)
ret = "".join(ret)
ret = ret.replace('\r', '')
return ret
def error(self, fname, expected, found):
# error message for broken files
expected = expected.split('\n')
found = found.split('\n')
print >>sys.stderr, "* '%s' broken: " % fname
print >>sys.stderr, ("%-" + options.tabbing + "s %s") \
% ("expected", "found")
for e, f in xzip(expected, found):
print >>sys.stderr, ("%-" + options.tabbing + "s %s") % (repr(e), repr(f))
def xzip(l1, l2):
for x in range(max(len(l1), len(l2))):
e = ''
f = ''
if len(l1) > x:
e = l1[x]
if len(l2) > x:
f = l2[x]
yield e, f
if __name__ == '__main__':
optpar = OptionParser()
optpar.add_option(
"-c", "--compiler", dest="compiler",
help="Command to execute the compiler, default: '%s'" % COMPILER,
default=COMPILER, metavar="COMPILER")
optpar.add_option(
"-a", "--risc", dest="risc",
help="Command to execute the risc emulator, default: '%s'" % RISC,
default=RISC, metavar="RISC")
optpar.add_option(
"-o", "--outdir", dest="outdir",
help="Path where to store compiled sources, default: '%s'" % OUTDIR,
default=OUTDIR, metavar="OUTDIR")
optpar.add_option(
"-t", "--tabbing", dest="tabbing",
help="Defines the indent of 'broken source file' messages"\
", default: %s" % TABBING,
default=TABBING, metavar="INT")
optpar.add_option(
"-d", "--doc", dest="doc", action="store_true",
help="Show the doc.",
default=False)
(options, args) = optpar.parse_args()
#create directory
try:
os.makedirs(options.outdir)
except Exception, (errno, strerror):
if not errno == 17:
print >>sys.stderr, strerror
sys.exit(errno)
if options.doc:
print __doc__
sys.exit(0)
for source in args:
Checker(source, options)
print >>sys.stderr, "-"*20
print >>sys.stderr, " tested: %s" % Checker.c_tested
print >>sys.stderr, " broken: %s" % Checker.c_broken
print >>sys.stderr, "untested: %s" % Checker.c_other