# copyright 2010-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/>.from__future__importabsolute_importimportos,os.pathasospimporterrnofromtempfileimportmkdtempfromsubprocessimportPopen,PIPE,STDOUTfromsix.moves.queueimportQueue,Empty# imported by default to simplify further import statementsfromlogilab.common.testlibimportunittest_main,with_tempdir,InnerTest,Tagsimportwebtest.httpimportcubicwebfromcubicweb.viewimportViewfromcubicweb.web.controllerimportControllerfromcubicweb.web.views.staticcontrollersimportStaticFileController,STATIC_CONTROLLERSfromcubicweb.devtoolsimportwebtestascwwebtestclassFirefoxHelper(object):def__init__(self,url=None):self._process=Noneself._profile_dir=mkdtemp(prefix='cwtest-ffxprof-')self.firefox_cmd=['firefox','-no-remote']ifos.name=='posix':self.firefox_cmd=[osp.join(osp.dirname(__file__),'data','xvfb-run.sh'),'-a','-s','-noreset -screen 0 800x600x24']+self.firefox_cmddeftest(self):try:proc=Popen(['firefox','--help'],stdout=PIPE,stderr=STDOUT)stdout,_=proc.communicate()returnproc.returncode==0,stdoutexceptOSErrorasexc:ifexc.errno==errno.ENOENT:msg='[%s] %s'%(errno.errorcode[exc.errno],exc.strerror)returnFalse,msgraisedefstart(self,url):self.stop()cmd=self.firefox_cmd+['-silent','--profile',self._profile_dir,'-url',url]withopen(os.devnull,'w')asfnull:self._process=Popen(cmd,stdout=fnull,stderr=fnull)defstop(self):ifself._processisnotNone:assertself._process.returncodeisNone,self._process.returncodeself._process.terminate()self._process.wait()self._process=Nonedef__del__(self):self.stop()classQUnitTestCase(cwwebtest.CubicWebTestTC):tags=cwwebtest.CubicWebTestTC.tags|Tags(('qunit',))# testfile, (dep_a, dep_b)all_js_tests=()defsetUp(self):super(QUnitTestCase,self).setUp()self.test_queue=Queue()classMyQUnitResultController(QUnitResultController):tc=selftest_queue=self.test_queueself._qunit_controller=MyQUnitResultControllerself.webapp.app.appli.vreg.register(MyQUnitResultController)self.webapp.app.appli.vreg.register(QUnitView)self.webapp.app.appli.vreg.register(CWDevtoolsStaticController)self.server=webtest.http.StopableWSGIServer.create(self.webapp.app)self.config.global_set_option('base-url',self.server.application_url)deftearDown(self):self.server.shutdown()self.webapp.app.appli.vreg.unregister(self._qunit_controller)self.webapp.app.appli.vreg.unregister(QUnitView)self.webapp.app.appli.vreg.unregister(CWDevtoolsStaticController)super(QUnitTestCase,self).tearDown()deftest_javascripts(self):forargsinself.all_js_tests:self.assertIn(len(args),(1,2))test_file=args[0]iflen(args)>1:depends=args[1]else:depends=()forjs_testinself._test_qunit(test_file,depends):yieldjs_test@with_tempdirdef_test_qunit(self,test_file,depends=(),timeout=10):QUnitView.test_file=test_fileQUnitView.depends=dependswhilenotself.test_queue.empty():self.test_queue.get(False)browser=FirefoxHelper()isavailable,reason=browser.test()ifnotisavailable:self.fail('firefox not available or not working properly (%s)'%reason)browser.start(self.config['base-url']+"?vid=qunit")test_count=0error=Falsedefraise_exception(cls,*data):raisecls(*data)whilenoterror:try:result,test_name,msg=self.test_queue.get(timeout=timeout)test_name='%s (%s)'%(test_name,test_file)self.set_description(test_name)ifresultisNone:breaktest_count+=1ifresult:yieldInnerTest(test_name,lambda:1)else:yieldInnerTest(test_name,self.fail,msg)exceptEmpty:error=Truemsg='%s inactivity timeout (%is). %i test results received'yieldInnerTest(test_file,raise_exception,RuntimeError,msg%(test_file,timeout,test_count))browser.stop()iftest_count<=0andnoterror:yieldInnerTest(test_name,raise_exception,RuntimeError,'No test yielded by qunit for %s'%test_file)classQUnitResultController(Controller):__regid__='qunit_result'# Class variables to circumvent the instantiation of a new Controller for each request._log_stack=[]# store QUnit log messages_current_module_name=''# store the current QUnit module namedefpublish(self,rset=None):event=self._cw.form['event']getattr(self,'handle_%s'%event)()returnb''defhandle_module_start(self):self.__class__._current_module_name=self._cw.form.get('name','')defhandle_test_done(self):name='%s // %s'%(self._current_module_name,self._cw.form.get('name',''))failures=int(self._cw.form.get('failures',0))total=int(self._cw.form.get('total',0))self._log_stack.append('%i/%i assertions failed'%(failures,total))msg='\n'.join(self._log_stack)iffailures:self.tc.test_queue.put((False,name,msg))else:self.tc.test_queue.put((True,name,msg))self._log_stack[:]=[]defhandle_done(self):self.tc.test_queue.put((None,None,None))defhandle_log(self):result=self._cw.form['result']message=self._cw.form.get('message','<no message>')actual=self._cw.form.get('actual')expected=self._cw.form.get('expected')source=self._cw.form.get('source')log='%s: %s'%(result,message)ifresult=='false'andactualisnotNoneandexpectedisnotNone:log+=' (got: %s, expected: %s)'%(actual,expected)ifsourceisnotNone:log+='\n'+sourceself._log_stack.append(log)classQUnitView(View):__regid__='qunit'templatable=Falsedepends=Nonetest_file=Nonedefcall(self,**kwargs):w=self.wreq=self._cww(u'''<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="application/html; charset=UTF-8"/> <!-- JS lib used as testing framework --> <link rel="stylesheet" type="text/css" media="all" href="/devtools/qunit.css" /> <script src="/data/jquery.js" type="text/javascript"></script> <script src="/devtools/cwmock.js" type="text/javascript"></script> <script src="/devtools/qunit.js" type="text/javascript"></script>''')w(u'<!-- result report tools -->')w(u'<script type="text/javascript">')w(u"var BASE_URL = '%s';"%req.base_url())w(u''' QUnit.moduleStart(function (details) { jQuery.ajax({ url: BASE_URL + 'qunit_result', data: {"event": "module_start", "name": details.name}, async: false}); }); QUnit.testDone(function (details) { jQuery.ajax({ url: BASE_URL + 'qunit_result', data: {"event": "test_done", "name": details.name, "failures": details.failed, "total": details.total}, async: false}); }); QUnit.done(function (details) { jQuery.ajax({ url: BASE_URL + 'qunit_result', data: {"event": "done", "failures": details.failed, "total": details.total}, async: false}); }); QUnit.log(function (details) { jQuery.ajax({ url: BASE_URL + 'qunit_result', data: {"event": "log", "result": details.result, "actual": details.actual, "expected": details.expected, "source": details.source, "message": details.message}, async: false}); });''')w(u'</script>')w(u'<!-- Test script dependencies (tested code for example) -->')fordepinself.depends:w(u' <script src="%s" type="text/javascript"></script>\n'%dep)w(u' <!-- Test script itself -->')w(u' <script src="%s" type="text/javascript"></script>'%self.test_file)w(u''' </head> <body> <div id="qunit-fixture"></div> <div id="qunit"></div> </body> </html>''')classCWDevtoolsStaticController(StaticFileController):__regid__='devtools'defpublish(self,rset=None):staticdir=osp.join(osp.dirname(__file__),'data')relpath=self.relpath[len(self.__regid__)+1:]returnself.static_file(osp.join(staticdir,relpath))STATIC_CONTROLLERS.append(CWDevtoolsStaticController)if__name__=='__main__':unittest_main()