Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 494 lines (413 sloc) 15.627 kb
#!/usr/bin/python
# test_pretty_print copyright 2012 don bright. released under the GPL 2, or
# later, as described in the file named 'COPYING' in OpenSCAD's project root.
# permission to change this license is given to Marius Kintel & Clifford Wolf
#
# This program 'pretty prints' the ctest output, namely
# files from builddir/Testing/Temporary.
# html & wiki output are produced in Testing/Temporary/sysid_report
#
# experimental wiki uploading is available by running
#
# python test_pretty_print.py --upload
#
# Design philosophy
#
# 1. parse the data (images, logs) into easy-to-use data structures
# 2. wikifiy the data
# 3. save the wikified data to disk
# todo
# deal better with the situation where Offscreen rendering fails
# do something if tests for GL extensions for OpenCSG fail (test fail, no image production)
# copy all images, sysinfo.txt to bundle for html/upload (images
# can be altered by subsequent runs)
# why is hash differing
# fix windows so that it won't keep asking 'this program crashed' over and over.
# (you can set this in the registry to never happen, but itd be better if the program
# itself was able to disable that temporarily in it's own process)
import string,sys,re,os,hashlib,subprocess,textwrap,time,platform
def tryread(filename):
data = None
try:
f = open(filename,'rb')
data = f.read()
f.close()
except:
print 'couldn\'t open ',filename
return data
def trysave(filename,data):
try:
if not os.path.isdir(os.path.dirname(filename)):
#print 'creating',os.path.dirname(filename)
os.mkdir(os.path.dirname(filename))
f=open(filename,'wb')
f.write(data)
f.close()
except:
print 'problem writing to',filename
return None
return True
def ezsearch(pattern,str):
x = re.search(pattern,str,re.DOTALL|re.MULTILINE)
if x and len(x.groups())>0: return x.group(1).strip()
return ''
def read_gitinfo():
# won't work if run from outside of branch.
data = subprocess.Popen(['git','remote','-v'],stdout=subprocess.PIPE).stdout.read()
origin = ezsearch('^origin *?(.*?)\(fetch.*?$',data)
upstream = ezsearch('^upstream *?(.*?)\(fetch.*?$',data)
data = subprocess.Popen(['git','branch'],stdout=subprocess.PIPE).stdout.read()
branch = ezsearch('^\*(.*?)$',data)
out = 'Git branch: ' + branch + ' from origin ' + origin + '\n'
out += 'Git upstream: ' + upstream + '\n'
return out
def read_sysinfo(filename):
data = tryread(filename)
if not data:
sinfo = platform.sys.platform
sinfo += '\nsystem cannot create offscreen GL framebuffer object'
sinfo += '\nsystem cannot create images'
sysid = platform.sys.platform+'_no_images'
return sinfo, sysid
machine = ezsearch('Machine:(.*?)\n',data)
machine = machine.replace(' ','-').replace('/','-')
osinfo = ezsearch('OS info:(.*?)\n',data)
osplain = osinfo.split(' ')[0].strip().replace('/','-')
if 'windows' in osinfo.lower(): osplain = 'win'
renderer = ezsearch('GL Renderer:(.*?)\n',data)
tmp = renderer.split(' ')
tmp = string.join(tmp[0:3],'-')
tmp = tmp.split('/')[0]
renderer = tmp
data += read_gitinfo()
data += 'Image comparison: ImageMagick'
data = data.strip()
# create 4 letter hash and stick on end of sysid
nondate_data = re.sub("\n.*?ompile date.*?\n","\n",data).strip()
hexhash = hashlib.md5()
hexhash.update(nondate_data)
hexhash = hexhash.hexdigest()[-4:].upper()
hash = ''
for c in hexhash: hash += chr(ord(c)+97-48)
sysid = osplain + '_' + machine + '_' + renderer + '_' + hash
sysid = sysid.lower()
return data, sysid
class Test:
def __init__(self,fullname,time,passed,output,type,actualfile,expectedfile,scadfile,log):
self.fullname,self.time,self.passed,self.output = \
fullname, time, passed, output
self.type, self.actualfile, self.expectedfile, self.scadfile = \
type, actualfile, expectedfile, scadfile
self.fulltestlog = log
def __str__(self):
x = 'fullname: ' + self.fullname
x+= '\nactualfile: ' + self.actualfile
x+= '\nexpectedfile: ' + self.expectedfile
x+= '\ntesttime: ' + self.time
x+= '\ntesttype: ' + self.type
x+= '\npassed: ' + str(self.passed)
x+= '\nscadfile: ' + self.scadfile
x+= '\noutput bytes: ' + str(len(self.output))
x+= '\ntestlog bytes: ' + str(len(self.fulltestlog))
x+= '\n'
return x
def parsetest(teststring):
patterns = ["Test:(.*?)\n", # fullname
"Test time =(.*?) sec\n",
"Test time.*?Test (Passed)", # pass/fail
"Output:(.*?)<end of output>",
'Command:.*?-s" "(.*?)"', # type
"^ actual .*?:(.*?)\n",
"^ expected .*?:(.*?)\n",
'Command:.*?(testdata.*?)"' # scadfile
]
hits = map( lambda pattern: ezsearch(pattern,teststring), patterns )
test = Test(hits[0],hits[1],hits[2]=='Passed',hits[3],hits[4],hits[5],hits[6],hits[7],teststring)
if len(test.actualfile) > 0: test.actualfile_data = tryread(test.actualfile)
if len(test.expectedfile) > 0: test.expectedfile_data = tryread(test.expectedfile)
return test
def parselog(data):
startdate = ezsearch('Start testing: (.*?)\n',data)
enddate = ezsearch('End testing: (.*?)\n',data)
pattern = '([0-9]*/[0-9]* Testing:.*?time elapsed.*?\n)'
test_chunks = re.findall(pattern,data,re.S)
tests = map( parsetest, test_chunks )
tests = sorted(tests, key = lambda t:t.passed)
return startdate, tests, enddate
def load_makefiles(builddir):
filelist = []
for root, dirs, files in os.walk(builddir):
for fname in files: filelist += [ os.path.join(root, fname) ]
files = filter(lambda x: 'build.make' in os.path.basename(x), filelist)
files += filter(lambda x: 'flags.make' in os.path.basename(x), filelist)
files = filter(lambda x: 'esting' not in x and 'emporary' not in x, files)
result = {}
for fname in files:
result[fname.replace(builddir,'')] = open(fname,'rb').read()
return result
def wikify_filename(fname, wiki_rootpath, sysid):
wikifname = fname.replace('/','_').replace('\\','_').strip('.')
return wiki_rootpath + '_' + sysid + '_' + wikifname
def towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles):
wiki_template = """
<h3>[[WIKI_ROOTPATH]] test run report</h3>
'''Sysid''': SYSID
'''Result summary''': NUMPASSED / NUMTESTS tests passed ( PERCENTPASSED % ) <br>
'''System info''':
<pre>
SYSINFO
</pre>
start time: STARTDATE <br>
end time : ENDDATE <br>
'''Image tests'''
<REPEAT1>
{| border=1 cellspacing=0 cellpadding=1
|-
| colspan=2 | FTESTNAME
|-
| Expected image || Actual image
|-
| [[File:EXPECTEDFILE|250px]] || ACTUALFILE_WIKI
|}
<pre>
TESTLOG
</pre>
</REPEAT1>
'''Text tests'''
<REPEAT2>
{|border=1 cellspacing=0 cellpadding=1
|-
| FTESTNAME
|}
<pre>
TESTLOG
</pre>
</REPEAT2>
'''build.make and flags.make'''
<REPEAT3>
*[[MAKEFILE_NAME]]
</REPEAT3>
"""
txtpages = {}
imgs = {}
passed_tests = filter(lambda x: x.passed, tests)
failed_tests = filter(lambda x: not x.passed, tests)
tests_to_report = failed_tests
if include_passed: tests_to_report = tests
try: percent = str(int(100.0*len(passed_tests) / len(tests)))
except ZeroDivisionError: percent = 'n/a'
s = wiki_template
repeat1 = ezsearch('(<REPEAT1>.*?</REPEAT1>)',s)
repeat2 = ezsearch('(<REPEAT2>.*?</REPEAT2>)',s)
repeat3 = ezsearch('(<REPEAT3>.*?</REPEAT3>)',s)
dic = { 'STARTDATE': startdate, 'ENDDATE': enddate, 'WIKI_ROOTPATH': wiki_rootpath,
'SYSINFO': sysinfo, 'SYSID':sysid,
'NUMTESTS':len(tests), 'NUMPASSED':len(passed_tests), 'PERCENTPASSED':percent }
for key in dic.keys():
s = s.replace(key,str(dic[key]))
for t in tests_to_report:
if t.type=='txt':
newchunk = re.sub('FTESTNAME',t.fullname,repeat2)
newchunk = newchunk.replace('TESTLOG',t.fulltestlog)
s = s.replace(repeat2, newchunk+repeat2)
elif t.type=='png':
tmp = t.actualfile.replace(builddir,'')
wikiname_a = wikify_filename(tmp,wiki_rootpath,sysid)
tmp = t.expectedfile.replace(os.path.dirname(builddir),'')
wikiname_e = wikify_filename(tmp,wiki_rootpath,sysid)
if hasattr(t, 'expectedfile_data'):
imgs[wikiname_e] = t.expectedfile_data
if t.actualfile:
actualfile_wiki = '[[File:'+wikiname_a+'|250px]]'
if hasattr(t, 'actualfile_data'):
imgs[wikiname_a] = t.actualfile_data
else:
actualfile_wiki = 'No image generated.'
newchunk = re.sub('FTESTNAME',t.fullname,repeat1)
newchunk = newchunk.replace('ACTUALFILE_WIKI',actualfile_wiki)
newchunk = newchunk.replace('EXPECTEDFILE',wikiname_e)
newchunk = newchunk.replace('TESTLOG',t.fulltestlog)
s = s.replace(repeat1, newchunk+repeat1)
makefiles_wikinames = {}
for mf in sorted(makefiles.keys()):
tmp = mf.replace('CMakeFiles','').replace('.dir','')
wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
newchunk = re.sub('MAKEFILE_NAME',wikiname,repeat3)
s = s.replace(repeat3, newchunk+repeat3)
makefiles_wikinames[mf] = wikiname
s = s.replace(repeat1,'')
s = s.replace(repeat2,'')
s = s.replace(repeat3,'')
s = re.sub('<REPEAT.*?>\n','',s)
s = re.sub('</REPEAT.*?>','',s)
mainpage_wikiname = wiki_rootpath + '_' + sysid + '_test_report'
txtpages[ mainpage_wikiname ] = s
for mf in sorted(makefiles.keys()):
txtpages[ makefiles_wikinames[ mf ] ] = '\n*Subreport from [['+mainpage_wikiname+']]\n\n\n<pre>\n'+makefiles[mf]+'\n</pre>'
return imgs, txtpages
def tohtml(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles):
# kludge. assume wiki stuff has alreayd run and dumped files properly
head = '<html><head><title>'+wiki_rootpath+' test run for '+sysid +'</title></head><body>'
tail = '</body></html>'
passed_tests = filter(lambda x: x.passed, tests)
failed_tests = filter(lambda x: not x.passed, tests)
try: percent = str(int(100.0*len(passed_tests) / len(tests)))
except ZeroDivisionError: percent = 'n/a'
tests_to_report = failed_tests
if include_passed: tests_to_report = tests
s=''
s+= '\n<pre>'
s+= '\nSYSINFO\n'+ sysinfo
s+= '\n</pre><p>'
s+= '\n<pre>'
s+= '\nSTARTDATE: '+ startdate
s+= '\nENDDATE: '+ enddate
s+= '\nWIKI_ROOTPATH: '+ wiki_rootpath
s+= '\nSYSID: '+sysid
s+= '\nNUMTESTS: '+str(len(tests))
s+= '\nNUMPASSED: '+str(len(passed_tests))
s+= '\nPERCENTPASSED: '+ percent
s+= '\n</pre>'
for t in tests_to_report:
if t.type=='txt':
s+='\n<pre>'+t.fullname+'</pre>\n'
s+='<p><pre>'+t.fulltestlog+'</pre>\n\n'
elif t.type=='png':
tmp = t.actualfile.replace(builddir,'')
wikiname_a = wikify_filename(tmp,wiki_rootpath,sysid)
tmp = t.expectedfile.replace(os.path.dirname(builddir),'')
wikiname_e = wikify_filename(tmp,wiki_rootpath,sysid)
s+='<table>'
s+='\n<tr><td colspan=2>'+t.fullname
s+='\n<tr><td>Expected<td>Actual'
s+='\n<tr><td><img src='+wikiname_e+' width=250/>'
s+='\n <td><img src='+wikiname_a+' width=250/>'
s+='\n</table>'
s+='\n<pre>'
s+=t.fulltestlog
s+='\n</pre>'
s+='\n\n<p>\n\n'
makefiles_wikinames = {}
for mf in sorted(makefiles.keys()):
tmp = mf.replace('CMakeFiles','').replace('.dir','')
wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
s += '\n<a href='+wikiname+'>'+wikiname+'</a><br>'
s+='\n'
return head + s + tail
def wiki_login(wikiurl,api_php_path,botname,botpass):
site = mwclient.Site(wikiurl,api_php_path)
site.login(botname,botpass)
return site
def wiki_upload(wikiurl,api_php_path,botname,botpass,filedata,wikipgname):
counter = 0
done = False
descrip = 'test'
time.sleep(1)
while not done:
try:
print 'login',botname,'to',wikiurl
site = wiki_login(wikiurl,api_php_path,botname,botpass)
print 'uploading...',
if wikipgname.endswith('png'):
site.upload(filedata,wikipgname,descrip,ignore=True)
else:
page = site.Pages[wikipgname]
text = page.edit()
page.save(filedata)
done = True
print 'transfer ok'
except Exception, e:
print 'Error:', type(e),e
counter += 1
if counter>maxretry:
print 'giving up. please try a different wiki site'
done = True
else:
print 'wiki',wikiurl,'down. retrying in 15 seconds'
time.sleep(15)
def upload(wikiurl,api_php_path='/',wiki_rootpath='test', sysid='null', botname='cakebaby',botpass='anniew',wikidir='.',dryrun=True):
wetrun = not dryrun
if dryrun: print 'dry run'
try:
global mwclient
import mwclient
except:
print 'please download mwclient 0.6.5 and unpack here:', os.getcwd()
sys.exit()
if wetrun: site = wiki_login(wikiurl,api_php_path,botname,botpass)
wikifiles = os.listdir(wikidir)
testreport_page = filter( lambda x: 'test_report' in x, wikifiles )
if (len(testreport_page)>1):
print 'multiple test reports found, please clean dir',wikidir
sys.exit()
rootpage = testreport_page[0]
print 'add',rootpage,' to main report page ',wiki_rootpath
if wetrun:
page = site.Pages[wiki_rootpath]
text = page.edit()
if not '[['+rootpage+']]' in text:
page.save(text +'\n*[['+rootpage+']]\n')
wikifiles = os.listdir(wikidir)
wikifiles = filter(lambda x: not x.endswith('html'), wikifiles)
print 'upload wiki pages:'
for wikiname in wikifiles:
filename = os.path.join(wikidir,wikiname)
filedata = tryread(filename)
print 'upload',len(filedata),'bytes from',wikiname
if wetrun and len(filedata)>0:
wiki_upload(wikiurl,api_php_path,botname,botpass,filedata,wikiname)
if len(filedata)==0:
print 'cancelling empty upload'
def findlogfile(builddir):
logpath = os.path.join(builddir,'Testing','Temporary')
logfilename = os.path.join(logpath,'LastTest.log.tmp')
if not os.path.isfile(logfilename):
logfilename = os.path.join(logpath,'LastTest.log')
if not os.path.isfile(logfilename):
print 'cant find and/or open logfile',logfilename
sys.exit()
return logpath, logfilename
def main():
dry = False
if verbose: print 'running test_pretty_print'
if '--dryrun' in sys.argv: dry=True
suffix = ezsearch('--suffix=(.*?) ',string.join(sys.argv)+' ')
builddir = ezsearch('--builddir=(.*?) ',string.join(sys.argv)+' ')
if builddir=='': builddir=os.getcwd()
if verbose: print 'build dir set to', builddir
sysinfo, sysid = read_sysinfo(os.path.join(builddir,'sysinfo.txt'))
makefiles = load_makefiles(builddir)
logpath, logfilename = findlogfile(builddir)
testlog = tryread(logfilename)
startdate, tests, enddate = parselog(testlog)
if verbose:
print 'found sysinfo.txt,',
print 'found', len(makefiles),'makefiles,',
print 'found', len(tests),'test results'
imgs, txtpages = towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles)
wikidir = os.path.join(logpath,sysid+'_report')
if verbose: print 'erasing files in',wikidir
try: map(lambda x:os.remove(os.path.join(wikidir,x)), os.listdir(wikidir))
except: pass
print 'writing',len(imgs),'images, ',len(txtpages)-1,'text pages, and index.html to:\n', ' .'+wikidir.replace(os.getcwd(),'')
for pgname in sorted(imgs): trysave( os.path.join(wikidir,pgname), imgs[pgname])
for pgname in sorted(txtpages): trysave( os.path.join(wikidir,pgname), txtpages[pgname])
htmldata = tohtml(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles)
trysave( os.path.join(wikidir,'index.html'), htmldata )
if '--upload' in sys.argv:
upload(wikisite,wiki_api_path,wiki_rootpath,sysid,'openscadbot',
'tobdacsnepo',wikidir,dryrun=dry)
print 'upload attempt complete'
if verbose: print 'test_pretty_print complete'
#wikisite = 'cakebaby.referata.com'
#wiki_api_path = ''
wikisite = 'cakebaby.wikia.com'
wiki_api_path = '/'
wiki_rootpath = 'OpenSCAD'
builddir = os.getcwd() # os.getcwd()+'/build'
verbose = False
maxretry = 10
if bool(os.getenv("TEST_GENERATE")): sys.exit(0)
include_passed = False
if '--include-passed' in sys.argv: include_passed = True
main()
Jump to Line
Something went wrong with that request. Please try again.