#!/usr/bin/env python## runtest.py - wrapper script for running SCons tests## This script mainly exists to set PYTHONPATH to the right list of# directories to test the SCons modules.## By default, it directly uses the modules in the local tree:# ./src/ (source files we ship) and ./etc/ (other modules we don't).## HOWEVER, now that SCons has Repository support, we don't have# Aegis copy all of the files into the local tree. So if you're# using Aegis and want to run tests by hand using this script, you# must "aecp ." the entire source tree into your local directory# structure. When you're done with your change, you can then# "aecpu -unch ." to un-copy any files that you haven't changed.## When any -p option is specified, this script assumes it's in a# directory in which a build has been performed, and sets PYTHONPATH# so that it *only* references the modules that have unpacked from# the specified built package, to test whether the packages are good.## Options:## -a Run all tests; does a virtual 'find' for# all SCons tests under the current directory.## -d Debug. Runs the script under the Python# debugger (pdb.py) so you don't have to# muck with PYTHONPATH yourself.## -h Print the help and exit.## -o file Print test results to the specified file# in the format expected by aetest(5). This# is intended for use in the batch_test_command# field in the Aegis project config file.## -P Python Use the specified Python interpreter.## -p package Test against the specified package.## -q Quiet. By default, runtest.py prints the# command line it will execute before# executing it. This suppresses that print.## -X The scons "script" is an executable; don't# feed it to Python.## -x scons The scons script to use for tests.## (Note: There used to be a -v option that specified the SCons# version to be tested, when we were installing in a version-specific# library directory. If we ever resurrect that as the default, then# you can find the appropriate code in the 0.04 version of this script,# rather than reinventing that wheel.)#importgetoptimportglobimportosimportos.pathimportreimportstatimportstringimportsysall=0debug=''tests=[]printcmd=1package=Nonescons=Nonescons_exec=Noneoutput=Noneifos.name=='java':python=os.path.join(sys.prefix,'jython')else:python=sys.executablecwd=os.getcwd()ifsys.platform=='win32'oros.name=='java':lib_dir=os.path.join(sys.exec_prefix,"Lib")else:# The hard-coded "python" here is the directory name,# not an executable, so it's all right.lib_dir=os.path.join(sys.exec_prefix,"lib","python"+sys.version[0:3])helpstr="""\Usage: runtest.py [OPTIONS] [TEST ...]Options: -a, --all Run all tests. -d, --debug Run test scripts under the Python debugger. -h, --help Print this message and exit. -o FILE, --output FILE Print test results to FILE (Aegis format). -P Python Use the specified Python interpreter. -p PACKAGE, --package PACKAGE Test against the specified PACKAGE: deb Debian local-tar-gz .tar.gz standalone package local-zip .zip standalone package rpm Red Hat src-tar-gz .tar.gz source package src-zip .zip source package tar-gz .tar.gz distribution zip .zip distribution -q, --quiet Don't print the test being executed. -X Test script is executable, don't feed to Python. -x SCRIPT, --exec SCRIPT Test SCRIPT."""opts,args=getopt.getopt(sys.argv[1:],"adho:P:p:qXx:",['all','debug','help','output=','package=','python=','quiet','exec='])foro,ainopts:ifo=='-a'oro=='--all':all=1elifo=='-d'oro=='--debug':debug=os.path.join(lib_dir,"pdb.py")elifo=='-h'oro=='--help':printhelpstrsys.exit(0)elifo=='-o'oro=='--output':ifnotos.path.isabs(a):a=os.path.join(cwd,a)output=aelifo=='-P'oro=='--python':python=aelifo=='-p'oro=='--package':package=aelifo=='-q'oro=='--quiet':printcmd=0elifo=='-X':scons_exec=1elifo=='-x'oro=='--exec':scons=adefwhereis(file):fordirinstring.split(os.environ['PATH'],os.pathsep):f=os.path.join(dir,file)ifos.path.isfile(f):try:st=os.stat(f)except:continueifstat.S_IMODE(st[stat.ST_MODE])&0111:returnfreturnNoneaegis=whereis('aegis')spe=Noneifaegis:spe=os.popen("aesub '$spe' 2>/dev/null","r").read()[:-1]spe=string.split(spe,os.pathsep)classTest:def__init__(self,path,spe=None):self.path=pathself.abspath=os.path.abspath(path)ifspe:fordirinspe:f=os.path.join(dir,path)ifos.path.isfile(f):self.abspath=fbreakself.status=Noneifargs:ifspe:forainargs:ifos.path.isabs(a):forginglob.glob(a):tests.append(Test(g))else:fordirinspe:x=os.path.join(dir,a)globs=glob.glob(x)ifglobs:forginglobs:tests.append(Test(g))breakelse:forainargs:forginglob.glob(a):tests.append(Test(g))elifall:tdict={}deffind_Test_py(arg,dirname,names,tdict=tdict):forninfilter(lambdan:n[-8:]=="Tests.py",names):t=os.path.join(dirname,n)ifnottdict.has_key(t):tdict[t]=Test(t)os.path.walk('src',find_Test_py,0)deffind_py(arg,dirname,names,tdict=tdict):forninfilter(lambdan:n[-3:]==".py",names):t=os.path.join(dirname,n)ifnottdict.has_key(t):tdict[t]=Test(t)os.path.walk('test',find_py,0)ifaegis:cmd="aegis -list -unf pf 2>/dev/null"forlineinos.popen(cmd,"r").readlines():a=string.split(line)ifa[0]=="test"andnottdict.has_key(a[-1]):tdict[a[-1]]=Test(a[-1],spe)cmd="aegis -list -unf cf 2>/dev/null"forlineinos.popen(cmd,"r").readlines():a=string.split(line)ifa[0]=="test":ifa[1]=="remove":deltdict[a[-1]]elifnottdict.has_key(a[-1]):tdict[a[-1]]=Test(a[-1],spe)keys=tdict.keys()keys.sort()tests=map(tdict.get,keys)ifpackage:dir={'deb':'usr','local-tar-gz':None,'local-zip':None,'rpm':'usr','src-tar-gz':'','src-zip':'','tar-gz':'','zip':'',}# The hard-coded "python2.1" here is the library directory# name on Debian systems, not an executable, so it's all right.lib={'deb':os.path.join('python2.1','site-packages')}ifnotdir.has_key(package):sys.stderr.write("Unknown package '%s'\n"%package)sys.exit(2)test_dir=os.path.join(cwd,'build','test-%s'%package)ifdir[package]isNone:scons_script_dir=test_dirglobs=glob.glob(os.path.join(test_dir,'scons-local-*'))ifnotglobs:sys.stderr.write("No `scons-local-*' dir in `%s'\n"%test_dir)sys.exit(2)scons_lib_dir=Nonepythonpath_dir=globs[len(globs)-1]elifsys.platform=='win32':scons_script_dir=os.path.join(test_dir,dir[package],'Scripts')scons_lib_dir=os.path.join(test_dir,dir[package])pythonpath_dir=scons_lib_direlse:scons_script_dir=os.path.join(test_dir,dir[package],'bin')l=lib.get(package,'scons')scons_lib_dir=os.path.join(test_dir,dir[package],'lib',l)pythonpath_dir=scons_lib_direlse:sd=Noneld=None# XXX: Logic like the following will be necessary once# we fix runtest.py to run tests within an Aegis change# without symlinks back to the baseline(s).##if spe:# if not scons:# for dir in spe:# d = os.path.join(dir, 'src', 'script')# f = os.path.join(d, 'scons.py')# if os.path.isfile(f):# sd = d# scons = f# spe = map(lambda x: os.path.join(x, 'src', 'engine'), spe)# ld = string.join(spe, os.pathsep)scons_script_dir=sdoros.path.join(cwd,'src','script')scons_lib_dir=ldoros.path.join(cwd,'src','engine')pythonpath_dir=scons_lib_dirifscons:# Let the version of SCons that the -x option pointed to find# its own modules.os.environ['SCONS']=sconselifscons_lib_dir:# Because SCons is really aggressive about finding its modules,# it sometimes finds SCons modules elsewhere on the system.# This forces SCons to use the modules that are being tested.os.environ['SCONS_LIB_DIR']=scons_lib_dirifscons_exec:os.environ['SCONS_EXEC']='1'os.environ['PYTHONPATH']=pythonpath_dir+ \
os.pathsep+ \
os.path.join(cwd,'build','etc')+ \
os.pathsep+ \
os.path.join(cwd,'etc')os.chdir(scons_script_dir)classUnbuffered:def__init__(self,file):self.file=filedefwrite(self,arg):self.file.write(arg)self.file.flush()def__getattr__(self,attr):returngetattr(self.file,attr)sys.stdout=Unbuffered(sys.stdout)fortintests:cmd=string.join([python,debug,t.abspath]," ")ifprintcmd:sys.stdout.write(cmd+"\n")s=os.system(cmd)ifs>=256:s=s/256t.status=sifs<0ors>2:sys.stdout.write("Unexpected exit status %d\n"%s)fail=filter(lambdat:t.status==1,tests)no_result=filter(lambdat:t.status==2,tests)iflen(tests)!=1:iffail:iflen(fail)==1:sys.stdout.write("\nFailed the following test:\n")else:sys.stdout.write("\nFailed the following %d tests:\n"%len(fail))paths=map(lambdax:x.path,fail)sys.stdout.write("\t"+string.join(paths,"\n\t")+"\n")ifno_result:iflen(no_result)==1:sys.stdout.write("\nNO RESULT from the following test:\n")else:sys.stdout.write("\nNO RESULT from the following %d tests:\n"%len(no_result))paths=map(lambdax:x.path,no_result)sys.stdout.write("\t"+string.join(paths,"\n\t")+"\n")ifoutput:f=open(output,'w')f.write("test_result = [\n")fortintests:f.write(' { file_name = "%s";\n'%t.path)f.write(' exit_status = %d; },\n'%t.status)f.write("];\n")f.close()sys.exit(0)else:iflen(fail):sys.exit(1)eliflen(no_result):sys.exit(2)else:sys.exit(0)