diff --git a/risc/lib/risc.jar b/risc/lib/risc.jar new file mode 100644 index 0000000..1433aa6 --- /dev/null +++ b/risc/lib/risc.jar Binary files differ diff --git a/tests/5/arith.zwei b/tests/5/arith.zwei index b075d97..5ba9248 100644 --- a/tests/5/arith.zwei +++ b/tests/5/arith.zwei @@ -1,5 +1,7 @@ -//#isut +//#startut +//-12 +//#endut { -//#-12 printInt (-(3*5)+2-(-1)/(5%2)); -} \ No newline at end of file +} +//normal comment diff --git a/tests/5/cond_not.zwei b/tests/5/cond_not.zwei index 33d8850..234ae64 100644 --- a/tests/5/cond_not.zwei +++ b/tests/5/cond_not.zwei @@ -1,4 +1,4 @@ -//#isunittest +//#isut { //#1 diff --git a/tests/5/var.zwei b/tests/5/var.zwei index 7a1b6c9..536096e 100644 --- a/tests/5/var.zwei +++ b/tests/5/var.zwei @@ -1,10 +1,14 @@ +//#startut { Int a = 10; + a = 5; { Int b = 7; b = 8; - printInt(b); + printInt(b); + //8 + a = 6; }; - a = 5; printInt(a); + //6 } diff --git a/tests/5/while.zwei b/tests/5/while.zwei index 5767b2a..fb50346 100644 --- a/tests/5/while.zwei +++ b/tests/5/while.zwei @@ -1,8 +1,15 @@ +//#startut +//1\n +//2\n +//3\n4\n5\n6\n7\n8\n9\n10 +//\n +//#endut { Int a = 0; while ( a < 10 ) { a = a+1; printInt(a); + //newline printChar(10); } } diff --git a/unittest.py b/unittest.py index a990f08..3a10048 100644 --- a/unittest.py +++ b/unittest.py @@ -1,42 +1,145 @@ -#usage: python check.py --help +# usage: python check.py --help +# +#@author: michael.karlen@epfl.ch +#@version: 1.0 +#@date: 27. 1. 2006 +# + COMPILER = "scala -cp classes zweic.GeneratorTest" RISC = "java -cp risc/lib/risc.jar risc.emulator.Main" OUTDIR = "asm" +TABBING = '30' + +""" +DISCLAIMER: 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'//[^#]*#' \ - 'is(?:ut|unittest)', re.I) + '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'(?!is(?:ut|unittest))' \ + r'(?!isut)' \ r'(.*)', re.I) -class Checker: +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 - - self.fname = os.path.splitext(os.path.basename(spath))[0] - self.spath = spath + fname = os.path.splitext(os.path.basename(spath))[0] self.options = options - self.compile() + self.compile(spath, fname) - solution = self.parseUnitTest(self.spath) + solution = self.parseUnitTest(spath) - if self.isUT: - self.check(solution) - + if self.isUT: + self.check(fname, solution) + Checker.c_tested += 1 + else: + Checker.c_other += 1 print - def compile(self): - print '> compiling %s' % self.fname, + 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, self.spath, - os.path.join(self.options.outdir, self.fname+'.asm'))) + (self.options.compiler, spath, + os.path.join(self.options.outdir, fname+'.asm'))) errs = pipes[2].read() print '.' @@ -45,9 +148,9 @@ sys.exit(1) - def check(self, solution): + 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, self.fname)) + pipes = os.popen3(r'%s asm/%s.asm' % (self.options.risc, fname)) errs = pipes[2].read() if errs: print >>sys.stderr, errs @@ -55,36 +158,56 @@ answer = pipes[1].read() if solution == answer: - print "> checked: ok" + print "> tested: ok" else: - self.error(solution, answer) + Checker.c_broken += 1 + self.error(fname, solution, answer) def parseUnitTest(self, path): ret = [] for line in open(path): - if rx_isut.search(line): - #this is a file containing meta information - self.isUT = True + if rx_endut.search(line): + #this is a file width a meta header: stop + self.utDef = False - match = rx_tl.search(line) - if self.isUT and match: - #replace newlines - f = match.group(1).replace(r'\n','\n') - ret.append(f) + 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) return "".join(ret) - def error(self, expected, found): + def error(self, fname, expected, found): # error message for broken files expected = expected.split('\n') found = found.split('\n') - print >>sys.stderr, "* '%s' broken: " % self.fname - print >>sys.stderr, "%-50s %s" % ("expected", "found") + 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, "%-50s %s" % (e, f) + print >>sys.stderr, ("%-" + options.tabbing + "s %s") % (e, f) def xzip(l1, l2): @@ -115,13 +238,34 @@ 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", + help="Shows an extensive documentation of how to write unit tests.", + default=True) + (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__ + 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