[book] add doc about request, session and connection management you need to know when writing tests
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.""" Usage: %s [OPTIONS] <instance id> <queries file>Stress test a CubicWeb repositoryOPTIONS: -h / --help Display this help message and exit. -u / --user <user> Connect as <user> instead of being prompted to give it. -p / --password <password> Automatically give <password> for authentication instead of being prompted to give it. -n / --nb-times <num> Repeat queries <num> times. -t / --nb-threads <num> Execute queries in <num> parallel threads. -P / --profile <prof_file> dumps profile results (hotshot) in <prof_file> -o / --report-output <filename> Write profiler report into <filename> rather than on stdoutCopyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.http://www.logilab.fr/ -- mailto:contact@logilab.fr"""importosimportsysimportthreadingimportgetoptimporttracebackfromgetpassimportgetpassfromos.pathimportbasenamefromtimeimportclockfromlogilab.common.fileutilsimportlinesfromlogilab.common.ureportsimportTable,TextWriterfromcubicweb.server.repositoryimportRepositoryfromcubicweb.dbapiimportConnectionTB_LOCK=threading.Lock()classQueryExecutor:def__init__(self,cursor,times,queries,reporter=None):self._cursor=cursorself._times=timesself._queries=queriesself._reporter=reporterdefrun(self):cursor=self._cursortimes=self._timeswhiletimes:forindex,queryinenumerate(self._queries):start=clock()try:cursor.execute(query)exceptException:TB_LOCK.acquire()traceback.print_exc()TB_LOCK.release()returnifself._reporterisnotNone:self._reporter.add_proftime(clock()-start,index)times-=1defusage(status=0):"""print usage string and exit"""print__doc__%basename(sys.argv[0])sys.exit(status)classProfileReporter:"""a profile reporter gathers all profile informations from several threads and can write a report that summarizes all profile informations """profiler_lock=threading.Lock()def__init__(self,queries):self._queries=tuple(queries)self._profile_results=[(0.,0)]*len(self._queries)# self._table_report = Table(3, rheaders = True)len_max=max([len(query)forqueryinself._queries])+5self._query_fmt='%%%ds'%len_maxdefadd_proftime(self,elapsed_time,query_index):"""add a new time measure for query"""ProfileReporter.profiler_lock.acquire()cumul_time,times=self._profile_results[query_index]cumul_time+=elapsed_timetimes+=1.self._profile_results[query_index]=(cumul_time,times)ProfileReporter.profiler_lock.release()defdump_report(self,output=sys.stdout):"""dump report in 'output'"""table_elems=['RQL Query','Times','Avg Time']total_time=0.forquery,(cumul_time,times)inzip(self._queries,self._profile_results):avg_time=cumul_time/float(times)table_elems+=[str(query),'%f'%times,'%f'%avg_time]total_time+=cumul_timetable_elems.append('Total time :')table_elems.append(str(total_time))table_elems.append(' ')table_layout=Table(3,rheaders=True,children=table_elems)TextWriter().format(table_layout,output)# output.write('\n'.join(tmp_output))defrun(args):"""run the command line tool"""try:opts,args=getopt.getopt(args,'hn:t:u:p:P:o:',['help','user=','password=','nb-times=','nb-threads=','profile','report-output=',])exceptException,ex:printexusage(1)repeat=100threads=1user=os.environ.get('USER',os.environ.get('LOGNAME'))password=Nonereport_output=sys.stdoutprof_file=Noneforopt,valinopts:ifoptin('-h','--help'):usage()ifoptin('-u','--user'):user=valelifoptin('-p','--password'):password=valelifoptin('-n','--nb-times'):repeat=int(val)elifoptin('-t','--nb-threads'):threads=int(val)elifoptin('-P','--profile'):prof_file=valelifoptin('-o','--report-output'):report_output=file(val,'w')iflen(args)!=2:usage(1)queries=[queryforqueryinlines(args[1])ifnotquery.startswith('#')]ifuserisNone:user=raw_input('login: ')ifpasswordisNone:password=getpass('password: ')fromcubicweb.cwconfigimportinstance_configurationconfig=instance_configuration(args[0])# get local access to the repositoryprint"Creating repo",prof_filerepo=Repository(config,prof_file)cnxid=repo.connect(user,password=password)# connection to the CubicWeb repositoryrepo_cnx=Connection(repo,cnxid)repo_cursor=repo_cnx.cursor()reporter=ProfileReporter(queries)ifthreads>1:executors=[]whilethreads:qe=QueryExecutor(repo_cursor,repeat,queries,reporter=reporter)executors.append(qe)thread=threading.Thread(target=qe.run)qe.thread=threadthread.start()threads-=1forqeinexecutors:qe.thread.join()## for qe in executors:## print qe.thread, repeat - qe._times, 'times'else:QueryExecutor(repo_cursor,repeat,queries,reporter=reporter).run()reporter.dump_report(report_output)if__name__=='__main__':run(sys.argv[1:])