From 1ed5a652b9a7a2379d2eee072eaa38a9db6f005d Mon Sep 17 00:00:00 2001 From: Michal Sojka Date: Thu, 2 Dec 2010 01:16:44 +0100 Subject: [PATCH] Add non-finished version of genhtml --- gw-tests/genhtml/Makefile | 2 + gw-tests/genhtml/genhtml.py | 307 ++++++++++++++++++++++++++++++++++++ gw-tests/genhtml/wvtest.py | 156 ++++++++++++++++++ gw-tests/genhtml/wvtestrun | 186 ++++++++++++++++++++++ 4 files changed, 651 insertions(+) create mode 100644 gw-tests/genhtml/Makefile create mode 100644 gw-tests/genhtml/genhtml.py create mode 100755 gw-tests/genhtml/wvtest.py create mode 100755 gw-tests/genhtml/wvtestrun diff --git a/gw-tests/genhtml/Makefile b/gw-tests/genhtml/Makefile new file mode 100644 index 0000000..6801004 --- /dev/null +++ b/gw-tests/genhtml/Makefile @@ -0,0 +1,2 @@ +test: + ./wvtestrun ./wvtest.py genhtml.py diff --git a/gw-tests/genhtml/genhtml.py b/gw-tests/genhtml/genhtml.py new file mode 100644 index 0000000..3a90ab5 --- /dev/null +++ b/gw-tests/genhtml/genhtml.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python + +import os; +import dircache; +import sys; +import urllib + +if __name__ == "__main__": + os.chdir(sys.argv[1]) +else: + from wvtest import * + +class DimValue: + def __init__(self, dim, value): + self.dim = dim + self.value = value + def __repr__(self): + return repr(self.value) + def htmlLabel(self): + return self.dim.htmlLabel(self.value) + +class Dimension(dict): + def __init__(self, atype, name=None): + self.type = atype + if (name): + self.name = name + else: + self.name = atype + + def __iter__(self): + keys = self.keys() + keys.sort() + for k in keys: + yield self[k] + + def addValue(self, *values): + for value in values: + if value not in self: + self[value] = DimValue(self, value) + def htmlLabel(self, v): + return v + +@wvtest +def Dimension_and_DimValue(): + d = Dimension('kern', 'Kernel') + d.addValue('2.6.31') + d.addValue('2.6.30') + l=[] + for v in d: + WVPASSEQ(v.__class__, DimValue) + l.append(v.value) + lsorted = ['2.6.30', '2.6.31'] + WVPASSEQ(l, lsorted) + WVPASSEQ(v.htmlLabel(), '2.6.31') + WVPASSEQ(d['2.6.31'].value, '2.6.31') + + +class DimensionKern(Dimension): + def __init__(self): + Dimension.__init__(self, 'kern', 'Kernel') + def htmlLabel(self, v): + i=v.find(":") + if i>0: kver=v[:i] + else: kver=v + return v+"
config"%(urllib.quote(kver)) + def versions(self): + for v in self.values: + i=v.find(":") + if i>0: kver=v[:i] + else: kver=v + yield kver + +class DimensionTest(Dimension): + def __init__(self): + Dimension.__init__(self, 'test', 'Test') + def htmlLabel(self, v): + return v+"
source"%(urllib.quote(v)) + +class Test: + @classmethod + def isOnPath(cls, path): + f = os.path.join(path, 'plot.gp') + return os.path.isfile(f) + def __init__(self, path): + self.path = path + +def iterDimValues(dimensions): + idx = [0 for i in xrange(len(dimensions))] + done=False + while not done: + values=[] + for i in xrange(len(dimensions)): + values.append(dimensions[i].values()[idx[i]]) + yield values + done=True + for i in xrange(len(dimensions)): + idx[i] += 1 + if idx[i] < len(dimensions[i]): + done=False + break + idx[i] = 0 + +class Tests(dict): + """Represents all tests organized along several dimensions""" + def __init__(self, rootpath, *dimensions): + dict.__init__(self) + self.dimensions = dimensions + if (rootpath): + self.populate(rootpath) + def getTest(self, key): + realkey=[] + for d in self.dimensions: + for i in key: + if i.dim == d: + realkey.append(i.value) + if len(realkey) != len(self.dimensions): + raise KeyError("The coordinates in key do not match dimensions") + return self[tuple(realkey)] + + def addTest(self, test, coordinates): + if len(coordinates) != len(self.dimensions): + raise KeyError("The number coordinates do not match the number of dimensions") + self[tuple(coordinates)] = test + for i in xrange(len(coordinates)): + self.dimensions[i].addValue(coordinates[i]) + + def populate(self, rootpath): + for root, dirs, files in os.walk(rootpath): + if (root.find(rootpath) == 0): + coordinates = root[len(rootpath):] + else: + coordinates = rootpath + if Test.isOnPath(root): + self.addTest(Test(root), coordinates.split("/")) + def iterDimensionPairs(self): + for i in xrange(len(self.dimensions)): + for j in xrange(i+1, len(self.dimensions)): + yield (self.dimensions[i], self.dimensions[j]) + yield (self.dimensions[j], self.dimensions[i]) + def iterRemainingDimensions(self, dimensionPair): + for d in self.dimensions: + if d not in dimensionPair: + yield d + def generateHtml(self): + for pair in self.iterDimensionPairs(): + remdims = self.iterRemainingDimensions(pair) + for vals in iterDimValues(remdims): + self.generatePage(pair, remdims, vals) + + def generatePage(self, dimpair, dimother, valsother): + dimy, dimx = dimpair + html = open("%s-vs-%s-%s.html"%(dimy.type, dimx.type, "-".join([v.value for v in valsother])), "w") + title = "CAN gateway benchmark for " + ", ".join([v.dim.name+" "+v.value for v in valsother]) + print >> html, """ + +%s + + + +

%s

""" % (title, title) + for d in dimother: + pass +# print >>html, "View for %s: " % str(ps.pageclass.name) +# for v in ps.values: +# print >>html, "%s | "%(ps.values.type, urllib.quote(v), v) +# print >>html, "
" +# try: +# print >>html, d.htmlPreamble() +# except Exception: +# pass + + print >>html, "" + for x in dimx.values(): + print >>html, "" % x.htmlLabel() + print >>html, "" + for y in dimy.values(): + print >>html, "" % y.htmlLabel() + + for x in dimx.values(): + print >>html, "" + print >>html, "" + print >> html, """ +
%s
%s" + idx = valsother + idx.extend([x,y]) + test = self.getTest(idx) + + d="by-%s/%s/%s/%s/" % (self.prefix, self.value, y, x) + dthumb = d+"thumb" + try: + for img in dircache.listdir(dthumb): + print >>html, "" % (urllib.quote(d), img, urllib.quote(d), img) + except OSError: + print "warning: no images in %s?"%dthumb + print >>html, "
+ +""" + +@wvtest +def Two_Dimensions_in_Tests(): + t = Tests(None, DimensionTest(), DimensionKern()) + WVPASSEQ([(a.__class__.__name__, b.__class__.__name__) for a, b in t.iterDimensionPairs()], + [('DimensionTest', 'DimensionKern'), ('DimensionKern', 'DimensionTest')]) +@wvtest +def Three_Dimensions_in_Tests(): + dt = DimensionTest() + dk = DimensionKern() + dd = Dimension('tmp', "Tmp") + t = Tests(None, dt, dk, dd) + pairs = [(a.__class__.__name__, b.__class__.__name__) for a, b in t.iterDimensionPairs()] + WVPASSEQ(pairs[0], ('DimensionTest', 'DimensionKern')) + WVPASSEQ(pairs[1], ('DimensionKern', 'DimensionTest')) + WVPASSEQ(pairs[2], ('DimensionTest', 'Dimension')) + WVPASSEQ(pairs[3], ('Dimension', 'DimensionTest')) + WVPASSEQ(pairs[4], ('DimensionKern', 'Dimension')) + WVPASSEQ(pairs[5], ('Dimension', 'DimensionKern')) + WVPASSEQ(len(pairs), 6) + WVEXCEPT(KeyError, t.addTest, None, [1, 2]) + WVEXCEPT(KeyError, t.addTest, None, [1, 2, 3, 4]) + + class MyTest(Test): + pass + + t.addTest(MyTest, [1, 'a', 'A']); + + WVPASSEQ(dt.keys(), [1]) + WVPASSEQ(dk.keys(), ['a']) + WVPASSEQ(dd.keys(), ['A']) + + v1 = dt[1] + v2 = dk['a'] + v3 = dd['A'] + + WVPASSEQ(t.getTest((v1, v2, v3)), MyTest) + WVPASSEQ(t.getTest((v1, v3, v2)), MyTest) + WVPASSEQ(t.getTest((v3, v2, v1)), MyTest) + WVEXCEPT(KeyError, t.getTest, (v1, v1, v1)) + WVEXCEPT(KeyError, t.getTest, (v1, v2)) + WVEXCEPT(TypeError, t.getTest, v1) + + WVPASSEQ([d.__class__.__name__ for d in t.iterRemainingDimensions([dt])], ['DimensionKern', 'Dimension']) + WVPASSEQ([d.__class__.__name__ for d in t.iterRemainingDimensions([dt, dd])], ['DimensionKern']) + + WVPASSEQ(str([v for v in iterDimValues([dt, dk])]), str([[1,'a']])) + dt.addValue(2) + dk.addValue('b') + WVPASSEQ(str([v for v in iterDimValues([dt, dk])]), str([[1, 'a'], [2, 'a'], [1, 'b'], [2, 'b']])) + + +# tests = Tests("by-kern/", DimensionKern(), DimensionTest()) + +# sys.exit(0) + + + +# class Page: +# def __init__(self, tests, yaxis, xaxis=None): + +# class PageKern(Page): +# prefix = 'kern' +# name = 'kernel' +# def __init__(self, value, xvals, yvals): +# Page.__init__(self, value, xvals, yvals) +# def getPreambule(self): +# i=self.value.find(":") +# if i>0: kver=self.value[:i] +# else: kver=self.value +# return "

Kernel config

"%kver + +# class PageClck(Page): +# prefix = 'clck' +# name = 'CPU clock' +# def __init__(self, value, xvals, yvals): +# Page.__init__(self, value, xvals, yvals) + +# class PageTest(Page): +# prefix = 'test' +# name = 'test' +# def __init__(self, value, xvals, yvals): +# Page.__init__(self, value, xvals, yvals) +# def getPreambule(self): +# return "

Test source

"%(urllib.quote(self.value)) + + + +# pagesets = [ PageSet(PageKern, kernels, clocks, tests), +# PageSet(PageTest, tests, clocks, kernels)] + +# for ps in pagesets: +# for p in ps.getPages(): +# p.generate(pagesets) + +# try: +# os.remove("index.html") +# except OSError: pass + +# os.symlink("%s-%s.html"%(clocks.type, clocks[0]), "index.html") + +# os.system("source-highlight -d --output-dir=. ../*.sh") + + +# for v in kernels.versions(): +# os.system("cp /boot/config-%s ."%v) diff --git a/gw-tests/genhtml/wvtest.py b/gw-tests/genhtml/wvtest.py new file mode 100755 index 0000000..fd6994e --- /dev/null +++ b/gw-tests/genhtml/wvtest.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +import traceback +import os +import re +import sys + +if __name__ != "__main__": # we're imported as a module + _registered = [] + _tests = 0 + _fails = 0 + + def wvtest(func): + """ Use this decorator (@wvtest) in front of any function you want to run + as part of the unit test suite. Then run: + python wvtest.py path/to/yourtest.py + to run all the @wvtest functions in that file. + """ + _registered.append(func) + return func + + + def _result(msg, tb, code): + global _tests, _fails + _tests += 1 + if code != 'ok': + _fails += 1 + (filename, line, func, text) = tb + filename = os.path.basename(filename) + msg = re.sub(r'\s+', ' ', str(msg)) + sys.stderr.flush() + print '! %-70s %s' % ('%s:%-4d %s' % (filename, line, msg), + code) + sys.stdout.flush() + + + def _check(cond, msg = 'unknown', tb = None): + if tb == None: tb = traceback.extract_stack()[-3] + if cond: + _result(msg, tb, 'ok') + else: + _result(msg, tb, 'FAILED') + return cond + + + def _code(): + (filename, line, func, text) = traceback.extract_stack()[-3] + text = re.sub(r'^\w+\((.*)\)(\s*#.*)?$', r'\1', text); + return text + + + def WVPASS(cond = True): + ''' Counts a test failure unless cond is true. ''' + return _check(cond, _code()) + + def WVFAIL(cond = True): + ''' Counts a test failure unless cond is false. ''' + return _check(not cond, 'NOT(%s)' % _code()) + + def WVPASSEQ(a, b): + ''' Counts a test failure unless a == b. ''' + return _check(a == b, '%s == %s' % (repr(a), repr(b))) + + def WVPASSNE(a, b): + ''' Counts a test failure unless a != b. ''' + return _check(a != b, '%s != %s' % (repr(a), repr(b))) + + def WVPASSLT(a, b): + ''' Counts a test failure unless a < b. ''' + return _check(a < b, '%s < %s' % (repr(a), repr(b))) + + def WVPASSLE(a, b): + ''' Counts a test failure unless a <= b. ''' + return _check(a <= b, '%s <= %s' % (repr(a), repr(b))) + + def WVPASSGT(a, b): + ''' Counts a test failure unless a > b. ''' + return _check(a > b, '%s > %s' % (repr(a), repr(b))) + + def WVPASSGE(a, b): + ''' Counts a test failure unless a >= b. ''' + return _check(a >= b, '%s >= %s' % (repr(a), repr(b))) + + def WVEXCEPT(etype, func, *args, **kwargs): + ''' Counts a test failure unless func throws an 'etype' exception. + You have to spell out the function name and arguments, rather than + calling the function yourself, so that WVEXCEPT can run before + your test code throws an exception. + ''' + try: + func(*args, **kwargs) + except etype, e: + return _check(True, 'EXCEPT(%s)' % _code()) + except: + _check(False, 'EXCEPT(%s)' % _code()) + raise + else: + return _check(False, 'EXCEPT(%s)' % _code()) + +else: # we're the main program + # NOTE + # Why do we do this in such a convoluted way? Because if you run + # wvtest.py as a main program and it imports your test files, then + # those test files will try to import the wvtest module recursively. + # That actually *works* fine, because we don't run this main program + # when we're imported as a module. But you end up with two separate + # wvtest modules, the one that gets imported, and the one that's the + # main program. Each of them would have duplicated global variables + # (most importantly, wvtest._registered), and so screwy things could + # happen. Thus, we make the main program module *totally* different + # from the imported module. Then we import wvtest (the module) into + # wvtest (the main program) here and make sure to refer to the right + # versions of global variables. + # + # All this is done just so that wvtest.py can be a single file that's + # easy to import into your own applications. + import wvtest + + def _runtest(modname, fname, f): + print + print 'Testing "%s" in %s.py:' % (fname, modname) + sys.stdout.flush() + try: + f() + except Exception, e: + print + print traceback.format_exc() + tb = sys.exc_info()[2] + wvtest._result(e, traceback.extract_tb(tb)[1], + 'EXCEPTION') + + # main code + for modname in sys.argv[1:]: + if not os.path.exists(modname): + print 'Skipping: %s' % modname + continue + if modname.endswith('.py'): + modname = modname[:-3] + print 'Importing: %s' % modname + wvtest._registered = [] + oldwd = os.getcwd() + oldpath = sys.path + try: + modpath = os.path.abspath(modname).split('/')[:-1] + os.chdir('/'.join(modpath)) + sys.path += ['/'.join(modpath), + '/'.join(modpath[:-1])] + mod = __import__(modname.replace('/', '.'), None, None, []) + for t in wvtest._registered: + _runtest(modname, t.func_name, t) + print + finally: + os.chdir(oldwd) + sys.path = oldpath + + print + print 'WvTest: %d tests, %d failures.' % (wvtest._tests, wvtest._fails) diff --git a/gw-tests/genhtml/wvtestrun b/gw-tests/genhtml/wvtestrun new file mode 100755 index 0000000..fbc083d --- /dev/null +++ b/gw-tests/genhtml/wvtestrun @@ -0,0 +1,186 @@ +#!/usr/bin/perl -w +# +# WvTest: +# Copyright (C)2007-2009 Versabanq Innovations Inc. and contributors. +# Licensed under the GNU Library General Public License, version 2. +# See the included file named LICENSE for license information. +# +use strict; +use Time::HiRes qw(time); + +# always flush +$| = 1; + +if (@ARGV < 1) { + print STDERR "Usage: $0 \n"; + exit 127; +} + +print STDERR "Testing \"all\" in @ARGV:\n"; + +my $pid = open(my $fh, "-|"); +if (!$pid) { + # child + setpgrp(); + open STDERR, '>&STDOUT' or die("Can't dup stdout: $!\n"); + exec(@ARGV); + exit 126; # just in case +} + +my $istty = -t STDOUT && $ENV{'TERM'} ne "dumb"; +my @log = (); +my ($gpasses, $gfails) = (0,0); + +sub bigkill($) +{ + my $pid = shift; + + if (@log) { + print "\n" . join("\n", @log) . "\n"; + } + + print STDERR "\n! Killed by signal FAILED\n"; + + ($pid > 0) || die("pid is '$pid'?!\n"); + + local $SIG{CHLD} = sub { }; # this will wake us from sleep() faster + kill 15, $pid; + sleep(2); + + if ($pid > 1) { + kill 9, -$pid; + } + kill 9, $pid; + + exit(125); +} + +# parent +local $SIG{INT} = sub { bigkill($pid); }; +local $SIG{TERM} = sub { bigkill($pid); }; +local $SIG{ALRM} = sub { + print STDERR "Alarm timed out! No test results for too long.\n"; + bigkill($pid); +}; + +sub colourize($) +{ + my $result = shift; + my $pass = ($result eq "ok"); + + if ($istty) { + my $colour = $pass ? "\e[32;1m" : "\e[31;1m"; + return "$colour$result\e[0m"; + } else { + return $result; + } +} + +sub mstime($$$) +{ + my ($floatsec, $warntime, $badtime) = @_; + my $ms = int($floatsec * 1000); + my $str = sprintf("%d.%03ds", $ms/1000, $ms % 1000); + + if ($istty && $ms > $badtime) { + return "\e[31;1m$str\e[0m"; + } elsif ($istty && $ms > $warntime) { + return "\e[33;1m$str\e[0m"; + } else { + return "$str"; + } +} + +sub resultline($$) +{ + my ($name, $result) = @_; + return sprintf("! %-65s %s", $name, colourize($result)); +} + +my $allstart = time(); +my ($start, $stop); + +sub endsect() +{ + $stop = time(); + if ($start) { + printf " %s %s\n", mstime($stop - $start, 500, 1000), colourize("ok"); + } +} + +while (<$fh>) +{ + chomp; + s/\r//g; + + if (/^\s*Testing "(.*)" in (.*):\s*$/) + { + alarm(120); + my ($sect, $file) = ($1, $2); + + endsect(); + + printf("! %s %s: ", $file, $sect); + @log = (); + $start = $stop; + } + elsif (/^!\s*(.*?)\s+(\S+)\s*$/) + { + alarm(120); + + my ($name, $result) = ($1, $2); + my $pass = ($result eq "ok"); + + if (!$start) { + printf("\n! Startup: "); + $start = time(); + } + + push @log, resultline($name, $result); + + if (!$pass) { + $gfails++; + if (@log) { + print "\n" . join("\n", @log) . "\n"; + @log = (); + } + } else { + $gpasses++; + print "."; + } + } + else + { + push @log, $_; + } +} + +endsect(); + +my $newpid = waitpid($pid, 0); +if ($newpid != $pid) { + die("waitpid returned '$newpid', expected '$pid'\n"); +} + +my $code = $?; +my $ret = ($code >> 8); + +# return death-from-signal exits as >128. This is what bash does if you ran +# the program directly. +if ($code && !$ret) { $ret = $code | 128; } + +if ($ret && @log) { + print "\n" . join("\n", @log) . "\n"; +} + +if ($code != 0) { + print resultline("Program returned non-zero exit code ($ret)", "FAILED"); +} + +my $gtotal = $gpasses+$gfails; +printf("\nWvTest: %d test%s, %d failure%s, total time %s.\n", + $gtotal, $gtotal==1 ? "" : "s", + $gfails, $gfails==1 ? "" : "s", + mstime(time() - $allstart, 2000, 5000)); +print STDERR "\nWvTest result code: $ret\n"; +exit( $ret ? $ret : ($gfails ? 125 : 0) ); -- 2.39.2