#!/usr/bin/env python
-### TODO: allow to run several tests in one directory - possible
-### runtest1, runtest2 etc. Use it for generated headers test to check
-### local and global config separately.
-
import os
import os.path
import sys
import subprocess
import time
from xml.sax.saxutils import escape
+import fnmatch
+
+invokeDir = os.getcwd();
+testsRoot = os.path.dirname(os.path.abspath(__file__))
+if not os.path.exists(os.path.join(testsRoot, "tester.py")): raise "Can't find tests root directory!"
+os.environ['OMK_TESTSROOT'] = testsRoot
-sys.path.append("..")
+sys.path.append(os.path.join(testsRoot, ".."))
import rulesdef
class Results(dict):
self.time = time.gmtime()
self.datetime = time.strftime("%Y-%m-%d %H:%M:%S +0000", self.time)
self.filename = "results-"+time.strftime("%Y%m%d-%H%M%S", self.time)+".html"
+ self.stats = None
+
+ def genStats(self):
+ self.stats = Stats(self)
def toHtml(self):
s="""
<title>OMK test report %(datetime)s</title>
</head>
<body>
-<h2>Summary</h2>TODO
+<h2>Summary</h2>
""" % self.__dict__
+ s+=self.stats.toHtml()
+ s+="""
+<h2>Chart by rules</h2>
+ """
+ s+=self.toHtmlByRules();
s+="""
-<h2>Chart</h2>
+<h2>Chart by testcase</h2>
<table cellpadding='2' border='1'>
<tbody>
- """ % self.__dict__
+ """
tests = sorted(self.keys())
for t in tests:
s+=self[t].toHtml()
</body></html>
"""
return s
-
+
+ def toHtmlByRules(self):
+ rules = sorted(rulesdef.rules.keys())
+ tests = sorted(self.keys())
+ s=''
+ for r in rules:
+ s+="""
+<a name='results-%(rules)s' />
+<h3>Rules: %(rules)s</h3>
+<table cellpadding='2' border='1'>
+<tbody>
+""" % { 'rules':r }
+ for t in tests:
+ if self[t].has_key(r):
+ s+=self[t][r].toHtml("<a href='%s'>/</a><a href='%s'>%s</a>" %
+ (self[t].tc.dirRelative, self[t].tc.scriptNameRelative, self[t].tc.name))
+ s+="""
+</tbody></table>
+"""
+ return s
+
def save(self):
f = file(self.filename, "w+")
f.write(self.toHtml())
print "Results written to "+self.filename
class TestCaseResult(dict):
- def __init__(self, tcname):
- self.tcname = tcname
+ def __init__(self, tc):
+ self.tc = tc
def toHtml(self):
rules = sorted(self.keys())
s="""
- <tr><td colspan='6'><strong>%s</strong></td></tr>
- """ % self.tcname
+ <tr><td colspan='5'><strong><a href='%s'>%s</a></strong></td></tr>
+ """ % (self.tc.scriptNameRelative, self.tc.name)
for r in rules:
- s+=self[r].toHtml()
+ s+=self[r].toHtml(r)
return s
def toHtmlOutputs(self):
rules = sorted(self.keys())
- s="<h3>Testcase: %s</h3>" % self.tcname
+ s="<h3>Testcase: %s</h3>" % self.tc.name
for r in rules:
s+=self[r].toHtmlOutputs()
return s
def __init__(self, tcname, rules):
self.tcname = tcname
self.rules = rules
+ self.canttest = 0
- def toHtml(self):
+ def toHtml(self, title):
if self.exitcode == 0: color=''
- else:
- if self.exitcode == 1: color=' bgcolor="red"'
- elif self.exitcode == 2: color=' bgcolor="yellow"'
- else: color=' bgcolor="gray"'
- if self.stdout: stdoutlink="<a href='#stdout-%(tcname)s-%(rules)s'>stdout</a>" % self.__dict__
+ elif self.canttest: color=' bgcolor="gold"'
+ else: color=' bgcolor="red"'
+ if self.stdout: stdoutlink="<a href='#output-%(tcname)s-%(rules)s'>output</a>" % self.__dict__
else: stdoutlink=''
- if self.stderr: stderrlink="<a href='#stderr-%(tcname)s-%(rules)s'>stderr</a>" % self.__dict__
- else: stderrlink=''
s="""
<tr%(color)s>
- <td>%(rules)s</td>
+ <td>%(title)s</td>
<td>%(exitcode)d</td>
<td>%(message)s</td>
<td>%(time).1f s</td>
<td>%(stdoutlink)s</td>
- <td>%(stderrlink)s</td>
</tr>
""" % {
'color' : color,
'tcname' : self.rules,
- 'rules' : self.rules,
+ 'title' : title,
'exitcode' : self.exitcode,
'message' : escape(self.message),
'time' : self.time,
'stdoutlink' : stdoutlink,
- 'stderrlink' : stderrlink,
}
return s
def toHtmlOutputs(self):
'tcname':self.tcname,
'rules':self.rules,
'stdout':escape(self.stdout),
- 'stderr':escape(self.stderr)
}
s=""
if self.stdout: s+="""
-<a name='stdout-%(tcname)s-%(rules)s'/>
-<h5>Test %(tcname)s, rules %(rules)s, stdout</h5>
+<a name='output-%(tcname)s-%(rules)s'/>
+<h5>Output of test: %(tcname)s, rules: %(rules)s</h5>
<pre>%(stdout)s</pre>""" % vals
- if self.stderr: s+="""
-<a name='stderr-%(tcname)s-%(rules)s'/>
-<h5>Test %(tcname)s, rules %(rules)s, stderr</h5>
-<pre>%(stderr)s</pre>""" % vals
return s
+class RulesStat:
+ def __init__(self, rules):
+ self.rules = rules
+ self.tests = 0
+ self.success = 0
+ self.errors = 0
+ self.canttest = 0
+ def update(self, testCaseResult):
+ try:
+ resultEntry = testCaseResult[self.rules]
+ self.tests+=1
+ if resultEntry.exitcode == 0: self.success+=1
+ elif resultEntry.canttest: self.canttest+=1
+ else: self.errors+=1
+ except KeyError:
+ pass
+ def toHtml(self):
+ if self.errors == 0 and self.canttest == 0: self.color=''
+ elif self.errors != 0: self.color=' bgcolor="red"'
+ else: self.color = ' bgcolor="gold"'
+ s="""
+ <tr%(color)s>
+ <td><a href='#results-%(rules)s'>%(rules)s</a></td>
+ <td>%(tests)d</td>
+ <td>%(success)d</td>
+ <td>%(errors)d</td>
+ <td>%(canttest)d</td>
+ </tr>
+ """ % self.__dict__
+ return s
+
+class Stats(dict):
+ def __init__(self, results):
+ rules = rulesdef.rules.keys()
+ for rule in rules:
+ rulesStat = RulesStat(rule)
+ self[rule]=rulesStat
+ for resultEntry in results.values():
+ rulesStat.update(resultEntry)
+
+ def toHtml(self):
+ s="""
+<table cellpadding='2' border='1'>
+<col />
+<col span='5' align='right' />
+<thead><tr>
+ <td>Rules</td>
+ <td>Total</td>
+ <td>Success</td>
+ <td>Errors</td>
+ <td>Can't test</td>
+</tr></thead>
+<tbody>
+ """
+ rules = sorted(self.keys())
+ for r in rules:
+ s+=self[r].toHtml()
+ s+="""
+</tbody></table>"""
+ return s;
+
class TestCase:
- def __init__(self, directory):
+ def __init__(self, directory, executable):
self.directory = directory # Absolute directory
+ self.executable = executable
self.name = self._getName()
+ self.scriptNameRelative = self._getScriptRelative();
+ self.dirRelative = os.path.dirname(self.scriptNameRelative)
self._whichRules()
def _getName(self):
name = self.directory
if name.startswith(testsRoot+"/"):
name = name[len(testsRoot)+1:]
+ testSuffix = re.match("^runtest[-_. :]*(.*)", self.executable).group(1)
+ if testSuffix:
+ name+=" "+testSuffix
return name
+ def _getScriptRelative(self):
+ script = os.path.join(self.directory, self.executable);
+ if script.startswith(invokeDir+"/"):
+ script = script[len(invokeDir)+1:]
+ return script
+
def _whichRules(self):
"""Reads the rules file and creates the self.rules list of all
rules to test"""
self.rules = []
try:
- f = open(os.path.join(self.directory, 'rules'))
+ f = open(os.path.join(self.directory, self.executable+'.rules'))
except IOError:
self.rules = rulesdef.rules.keys()
return
self.rules.append(r)
else:
# rule name
+ line = line.strip()
if line in rulesdef.rules: self.rules = [ line ]
#print self.rules
def run(self):
- self.results = TestCaseResult(self.name)
- print "Testing %s:" % self.name,
+ self.results = TestCaseResult(self)
+ print "Testing %s:\n" % self.name,
os.chdir(os.path.join(testsRoot, self.directory))
# if os.path.exists("Makefile.test"):
# self._exec = self._execMake
- if os.path.exists("runtest"):
+ if os.path.exists(self.executable):
self._exec = self._execRuntest
else: return
for rules in self.rules:
resultEntry = ResultEntry(self.name, rules)
self.results[rules] = resultEntry
- print rules,
os.environ['OMK_RULES'] = rules
filesBefore = self._getFileSet()
self._copyRules(rules)
def _execRuntest(self, log):
startTime = time.clock()
- pipe = subprocess.Popen("./runtest", stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
+ pipe = subprocess.Popen("./"+self.executable, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
+ (output, error) = pipe.communicate()
endTime = time.clock()
- (output, errors) = pipe.communicate()
ret = pipe.returncode
- log.exitcode = ret
log.time = endTime - startTime
+ log.exitcode = ret
log.stdout = output
- log.stderr = errors
- if ret != 0 and os.path.exists("_error"):
- log.message = file("_error").read()
- else: log.message = ''
-
+ log.message = ''
+ if os.path.exists("_canttest"):
+ log.message = file("_canttest").read()
+ log.canttest = 1
+ ret = 2 # If Makefile sets canttest, override the exit code which might be zero
+ log.exitcode = ret
+ elif ret != 0:
+ if os.path.exists("_error"):
+ log.message = file("_error").read()
return ret
def _copyRules(self, rules):
def _doRun(self, log):
"Runs the teset in current directory."
+ print " ",os.environ['OMK_RULES'],
+ sys.stdout.flush()
ret = self._exec(log)
- print ret,
+ if log.canttest: retstr = "--"
+ elif ret == 0: retstr = "OK"
+ else: retstr = "FAILED"
+ print "%*s%s" % (20-len(os.environ['OMK_RULES']), "", retstr)
-testsRoot = os.path.dirname(os.path.abspath(__file__))
-if not os.path.exists(os.path.join(testsRoot, "runtests.py")): raise "Can't find tests root directory!"
-os.environ['OMK_TESTSROOT'] = testsRoot
-
results = Results()
-for dirpath, dirnames, filenames in os.walk(testsRoot):
- if not ("Makefile.test" in filenames or \
- "runtest" in filenames):
- continue
- t = TestCase(dirpath)
- t.run()
- results[t.name] = t.results
+for dirpath, dirnames, filenames in os.walk(invokeDir):
+ executables = fnmatch.filter(filenames, "runtest*")
+ if not executables: continue
+ for exe in executables:
+ if exe[-1] == "~": continue
+ if re.search(".rules$", exe): continue
+ t = TestCase(dirpath, exe)
+ t.run()
+ results[t.name] = t.results
-os.chdir(testsRoot)
+os.chdir(invokeDir)
+results.genStats()
results.save()
# Local Variables:
-# compile-command: "python runtests.py"
+# compile-command: "python tester.py"
# End: