"""Test tools for cubicweb:organization: Logilab:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"importosimportloggingfromdatetimeimporttimedeltafromos.pathimport(abspath,join,exists,basename,dirname,normpath,split,isfile,isabs)fromlogilab.common.dateimportstrptimefromcubicwebimportCW_SOFTWARE_ROOT,ConfigurationError,schema,cwconfigfromcubicweb.server.serverconfigimportServerConfigurationfromcubicweb.etwist.twconfigimportTwistedConfigurationcwconfig.CubicWebConfiguration.cls_adjust_sys_path()# db auto-population configuration #############################################SYSTEM_ENTITIES=schema.SCHEMA_TYPES|set(('CWGroup','CWUser','CWProperty','Workflow','State','BaseTransition','Transition','WorkflowTransition','TrInfo','SubWorkflowExitPoint',))SYSTEM_RELATIONS=schema.META_RTYPES|set((# workflow related'workflow_of','state_of','transition_of','initial_state','default_workflow','allowed_transition','destination_state','from_state','to_state','condition','subworkflow','subworkflow_state','subworkflow_exit','custom_workflow','in_state','wf_info_for',# cwproperty'for_user',# schema definition'specializes','relation_type','from_entity','to_entity','constrained_by','cstrtype','widget','read_permission','update_permission','delete_permission','add_permission',# permission'in_group','require_group','require_permission',# deducted from other relations'primary_email',))# content validation configuration ############################################## validators are used to validate (XML, DTD, whatever) view's content# validators availables are :# 'dtd' : validates XML + declared DTD# 'xml' : guarantees XML is well formed# None : do not try to validate anything# {'vid': validator}VIEW_VALIDATORS={}# cubicweb test configuration ##################################################BASE_URL='http://testing.fr/cubicweb/'DEFAULT_SOURCES={'system':{'adapter':'native','db-encoding':'UTF-8',#'ISO-8859-1','db-user':u'admin','db-password':'gingkow','db-name':'tmpdb','db-driver':'sqlite','db-host':None,},'admin':{'login':u'admin','password':u'gingkow',},}classTestServerConfiguration(ServerConfiguration):mode='test'set_language=Falseread_instance_schema=Falseinit_repository=Trueoptions=cwconfig.merge_options(ServerConfiguration.options+(('anonymous-user',{'type':'string','default':None,'help':'login of the CubicWeb user account to use for anonymous user (if you want to allow anonymous)','group':'main','inputlevel':1,}),('anonymous-password',{'type':'string','default':None,'help':'password of the CubicWeb user account matching login','group':'main','inputlevel':1,}),))def__init__(self,appid,log_threshold=logging.CRITICAL+10):ServerConfiguration.__init__(self,appid)self.init_log(log_threshold,force=True)# need this, usually triggered by cubicweb-ctlself.load_cwctl_plugins()anonymous_user=TwistedConfiguration.anonymous_user.im_func@propertydefapphome(self):ifexists(self.appid):returnabspath(self.appid)# cube testreturnabspath('..')appdatahome=apphomedefload_configuration(self):super(TestServerConfiguration,self).load_configuration()self.global_set_option('anonymous-user','anon')self.global_set_option('anonymous-password','anon')# no undo support in testsself.global_set_option('undo-support','')defmain_config_file(self):"""return instance's control configuration file"""returnjoin(self.apphome,'%s.conf'%self.name)definstance_md5_version(self):return''defbootstrap_cubes(self):try:super(TestServerConfiguration,self).bootstrap_cubes()exceptIOError:# no cubesself.init_cubes(())sourcefile=Nonedefsources_file(self):"""define in subclasses self.sourcefile if necessary"""ifself.sourcefile:print'Reading sources from',self.sourcefilesourcefile=self.sourcefileifnotisabs(sourcefile):sourcefile=join(self.apphome,sourcefile)else:sourcefile=super(TestServerConfiguration,self).sources_file()returnsourcefiledefsources(self):"""By default, we run tests with the sqlite DB backend. One may use its own configuration by just creating a 'sources' file in the test directory from wich tests are launched or by specifying an alternative sources file using self.sourcefile. """sources=super(TestServerConfiguration,self).sources()ifnotsources:sources=DEFAULT_SOURCESifsources['system']['db-driver']=='sqlite':# we need an abspath in case tests are changing the cwdsources['system']['db-name']=abspath(sources['system']['db-name'])returnsourcesclassBaseApptestConfiguration(TestServerConfiguration,TwistedConfiguration):repo_method='inmemory'options=cwconfig.merge_options(TestServerConfiguration.options+TwistedConfiguration.options)cubicweb_appobject_path=TestServerConfiguration.cubicweb_appobject_path|TwistedConfiguration.cubicweb_appobject_pathcube_appobject_path=TestServerConfiguration.cube_appobject_path|TwistedConfiguration.cube_appobject_pathdefavailable_languages(self,*args):return('en','fr','de')defext_resources_file(self):"""return instance's external resources file"""returnjoin(self.apphome,'data','external_resources')defpyro_enabled(self):# but export PYRO_MULTITHREAD=0 or you get problems with sqlite and threadsreturnTrueclassApptestConfiguration(BaseApptestConfiguration):def__init__(self,appid,log_threshold=logging.CRITICAL,sourcefile=None):BaseApptestConfiguration.__init__(self,appid,log_threshold=log_threshold)self.init_repository=sourcefileisNoneself.sourcefile=sourcefile# test database handling #######################################################definit_test_database(config=None,configdir='data'):"""init a test database for a specific driver"""fromcubicweb.dbapiimportin_memory_cnxconfig=configorTestServerConfiguration(configdir)sources=config.sources()driver=sources['system']['db-driver']ifdriver=='sqlite':init_test_database_sqlite(config)elifdriver=='postgres':init_test_database_postgres(config)elifdriver=='sqlserver2005':init_test_database_sqlserver2005(config,source)else:raiseValueError('no initialization function for driver %r'%driver)config._cubes=None# avoid assertion errorrepo,cnx=in_memory_cnx(config,unicode(sources['admin']['login']),password=sources['admin']['password']or'xxx')ifdriver=='sqlite':install_sqlite_patch(repo.querier)returnrepo,cnxdefreset_test_database(config):"""init a test database for a specific driver"""driver=config.sources()['system']['db-driver']ifdriver=='sqlite':reset_test_database_sqlite(config)else:raiseValueError('no reset function for driver %r'%driver)### postgres test database handling ############################################definit_test_database_postgres(config):"""initialize a fresh postgresql databse used for testing purpose"""ifconfig.init_repository:fromcubicweb.serverimportinit_repositoryinit_repository(config,interactive=False,drop=True)### sqlserver2005 test database handling ############################################definit_test_database_sqlserver2005(config):"""initialize a fresh sqlserver databse used for testing purpose"""ifconfig.init_repository:fromcubicweb.serverimportinit_repositoryinit_repository(config,interactive=False,drop=True,vreg=vreg)### sqlite test database handling ##############################################defcleanup_sqlite(dbfile,removetemplate=False):try:os.remove(dbfile)os.remove('%s-journal'%dbfile)exceptOSError:passifremovetemplate:try:os.remove('%s-template'%dbfile)exceptOSError:passdefreset_test_database_sqlite(config):importshutildbfile=config.sources()['system']['db-name']cleanup_sqlite(dbfile)template='%s-template'%dbfileifexists(template):shutil.copy(template,dbfile)returnTruereturnFalsedefinit_test_database_sqlite(config):"""initialize a fresh sqlite databse used for testing purpose"""# remove database file if it existsdbfile=config.sources()['system']['db-name']ifnotreset_test_database_sqlite(config):# initialize the databaseimportshutilfromcubicweb.serverimportinit_repositoryinit_repository(config,interactive=False)dbfile=config.sources()['system']['db-name']shutil.copy(dbfile,'%s-template'%dbfile)definstall_sqlite_patch(querier):"""This patch hotfixes the following sqlite bug : - http://www.sqlite.org/cvstrac/tktview?tn=1327,33 (some dates are returned as strings rather thant date objects) """ifhasattr(querier.__class__,'_devtools_sqlite_patched'):return# already monkey patcheddefwrap_execute(base_execute):defnew_execute(*args,**kwargs):rset=base_execute(*args,**kwargs)ifrset.description:found_date=Falseforrow,rowdescinzip(rset,rset.description):forcellindex,(value,vtype)inenumerate(zip(row,rowdesc)):ifvtypein('Date','Datetime')andtype(value)isunicode:found_date=Truevalue=value.rsplit('.',1)[0]try:row[cellindex]=strptime(value,'%Y-%m-%d %H:%M:%S')except:row[cellindex]=strptime(value,'%Y-%m-%d')ifvtype=='Time'andtype(value)isunicode:found_date=Truetry:row[cellindex]=strptime(value,'%H:%M:%S')except:# DateTime used as Time?row[cellindex]=strptime(value,'%Y-%m-%d %H:%M:%S')ifvtype=='Interval'andtype(value)isint:found_date=Truerow[cellindex]=timedelta(0,value,0)# XXX value is in number of seconds?ifnotfound_date:breakreturnrsetreturnnew_executequerier.__class__.execute=wrap_execute(querier.__class__.execute)querier.__class__._devtools_sqlite_patched=True