#!/usr/bin/env python1.5 import string, os, re, sys, time def safe_strip(str): if str[-1]=="\n": return str[:-1] else: return str def shell_quote(str): return "'%s'"%string.replace(str,"'","'\"'\"'") def get_tmp_filename(): result=os.path.expanduser("%s_%f_%d"%(os.path.basename(sys.argv[0]), time.time(), os.getpid())) return os.path.abspath(result) def system(command,dir=".",pass_errors=0): show=1 if show: print command, sys.stdout.flush() cap=get_tmp_filename() time_cap=get_tmp_filename() assert cap!=time_cap whole_command=("cd %s;/usr/bin/time -f '%%e %%S %%U %%x' "+ "-o %s sh -c '%s' >%s 2>&1")%(shell_quote(dir), shell_quote(time_cap), command, shell_quote(cap)) #print whole_command os.system(whole_command) result={} file=open(cap,"r") result["output"]=file.read() file.close() os.remove(cap) file=open(time_cap,"r") time_line=map(safe_strip,file.readlines())[-1] file.close() os.remove(time_cap) time_line=string.split(time_line," ") result["wall"]=float(time_line[0]) result["system"]=float(time_line[1]) result["user"]=float(time_line[2]) result["error"]=int(time_line[3]) result["times"]="w=%ss, s=%ss, u=%ss"%(result["wall"],result["system"],result["user"]) if show: print result["times"], "e=%d"%result["error"] if result["error"]!=0 and not pass_errors: print command print result["output"] assert 0 return result # Use a class to get some kind of global namespace class TestOne: def __init__(self): self.parse_args() self.init() system("rm -rf %s"%shell_quote(self.build)) system("cp -ar %s %s"%(shell_quote(self.base_tree), shell_quote(self.build))) self.patch() self.configure() self.make() self.build_rc() self.report.write("Runs:\n") for i in range(self.runs): self.run(i) self.report.write("\n") self.plot() self.check_savegames() def check_savegames(self): md5sums=[] for run in range(self.runs): md5sums.append(system("md5sum civgame* | md5sum", self.run_dir(run))["output"]) m={} for i in md5sums: m[i]=1 if len(m)>1: self.report.write("Savegames doesn't match: %s\n"%repr(md5sums)) assert 0 else: self.report.write("Savegames are equal\n") def plot(self): plot=get_tmp_filename() for i in ["wall","system","user"]: sum=0.0 j=0 for line in open("%s/%s.times"%(self.report_dir,i)).readlines(): sum=sum+float(line) j=j+1 average=sum/j self.report.write("Average %s time: %f\n"%(i,average)) f=open(plot,"w") f.write(''' set terminal png color set output "%s" plot "%s.times" with lines, %f title "average=%f" '''%(self.report_dir+"/"+i+".png",i,average,average)) f.close() system("gnuplot %s"%shell_quote(plot), self.report_dir) os.remove(plot) def run_dir(self,run): return "%s/run_%02d"%(self.report_dir,run) def run(self,run): self.report.write(" %d: "%run) dir=self.run_dir(run) os.mkdir(dir) system("rm -f civgame* core gmon.out",self.build) t=system("LANG=C ./server/civserver -r %s -g %s -l %s"%( shell_quote(self.report_dir+"/startup.final"), shell_quote(dir+"/gamelog"), shell_quote(dir+"/log")), self.build) system("mv civgame* %s"%shell_quote(dir),self.build,1) for i in ["wall","system","user"]: f=open("%s/%s.times"%(self.report_dir,i), "a") f.write("%s\n"%t[i]) f.close() self.report.write("%s\n"%t["times"]) if self.profile: t=system("gprof server/civserver",self.build) open(dir+"/profile","w").write(t["output"]) def build_rc(self): self.report.write("Startup script:\n") self.content(self.rc_templ) file=open(self.report_dir+"/startup.final","w") file.write(open(self.rc_templ).read()) file.write(""" set timeout -1 set savename civgame set gamelog 40 create Caesar start """) file.close() self.content(self.report_dir+"/startup.final") self.report.write("\n") def patch(self): self.report.write("Patches:\n") for p in self.patches: for i in [0,1]: t=system("patch --dry-run -p%d --batch <%s"%(i,p),self.build,1) if not t["error"]: system("patch -p%d --batch <%s"%(i,shell_quote(p)), self.build, 1) self.report.write(" %s applied with -p%d\n"%(p,i)) break else: self.report.write(" %s can't be applied\n"%p) assert 0 self.report.write("\n") def content(self,file): self.report.write("---START %s---\n%s---END---\n"%(file,open(file).read())) def configure(self): self.report.write("Configure command:\n") if not os.path.isfile(self.config_cmd): self.report.write(" %s not found\n"%self.config_cmd) return self.content(self.config_cmd) t=system(self.config_cmd,self.build) open(self.report_dir+"/configure_command.out","w").write(t["output"]) self.report.write(" %s\n\n"%t["times"]) def make(self): self.report.write("Make:\n") system("make clean", self.build) t=system('make CFLAGS="%s" CPPFLAGS="%s"'%(self.CFLAGS,self.CPPFLAGS), self.build) self.report.write(" %s\n\n"%t["times"]) def usage(self): print """\ test_one.py [-p] [CFLAGS=...] [CPPFLAGS=...] [] [] ... """ sys.exit(1) def init(self): self.build=os.path.abspath("BUILD") if self.profile: self.CFLAGS=self.CFLAGS+" -pg" self.report_dir=os.path.abspath(self.name) if os.path.isdir(self.report_dir): print "Warning: Removing old result dir %s"%self.report_dir system("rm -r %s"%shell_quote(self.report_dir)) os.mkdir(self.report_dir) self.report=open(self.report_dir+"/report","w") self.report.write("""\ Name: %(name)s Runs: %(runs)d Base-tree: %(base_tree)s RC-template: %(rc_templ)s Configure command: %(config_cmd)s Profile: %(profile)s CFLAGS: %(CFLAGS)s CPPFLAGS: %(CPPFLAGS)s """%self.__dict__) def parse_args(self): self.options={} args=sys.argv[:] del args[0] if len(args)<5: self.usage() if args[0]=="-p": self.profile=1 del args[0] else: self.profile=0 self.CFLAGS="-g -O2 -Wall" self.CPPFLAGS="" for i in ["CFLAGS", "CPPFLAGS"]: if string.find(args[0],i)==0: setattr(self, i, args[0]) del args[0] if len(args)<5: self.usage() self.name=args[0] self.runs=int(args[1]) self.base_tree=os.path.abspath(args[2]) self.rc_templ=os.path.abspath(args[3]) self.config_cmd=os.path.abspath(args[4]) args=args[5:] self.patches=map(os.path.abspath, args) TestOne()