--- a/devtools/__init__.py Fri Oct 08 17:07:46 2010 +0200
+++ b/devtools/__init__.py Sat Oct 09 00:05:49 2010 +0200
@@ -179,7 +179,7 @@
# threads
return True
-
+# XXX merge with BaseApptestConfiguration ?
class ApptestConfiguration(BaseApptestConfiguration):
def __init__(self, appid, log_threshold=logging.CRITICAL, sourcefile=None):
@@ -187,6 +187,7 @@
self.init_repository = sourcefile is None
self.sourcefile = sourcefile
+
class RealDatabaseConfiguration(ApptestConfiguration):
"""configuration class for tests to run on a real database.
@@ -209,6 +210,7 @@
db_require_setup = False # skip init_db / reset_db steps
read_instance_schema = True # read schema from database
+
# test database handling #######################################################
def init_test_database(config=None, configdir='data'):
@@ -231,7 +233,6 @@
install_sqlite_patch(repo.querier)
return repo, cnx
-
def reset_test_database(config):
"""init a test database for a specific driver"""
if not config.db_require_setup:
@@ -331,7 +332,6 @@
dbfile = config.sources()['system']['db-name']
shutil.copy(dbfile, '%s-template' % dbfile)
-
def install_sqlite_patch(querier):
"""This patch hotfixes the following sqlite bug :
- http://www.sqlite.org/cvstrac/tktview?tn=1327,33
--- a/devtools/cwwindmill.py Fri Oct 08 17:07:46 2010 +0200
+++ b/devtools/cwwindmill.py Sat Oct 09 00:05:49 2010 +0200
@@ -25,6 +25,7 @@
import os, os.path as osp
+from logging import getLogger, ERROR
import sys
# imported by default to simplify further import statements
@@ -32,6 +33,7 @@
import windmill
from windmill.dep import functest
+from windmill.bin.admin_lib import configure_global_settings, setup, teardown
from cubicweb.devtools.httptest import CubicWebServerTC
@@ -44,46 +46,81 @@
unittestreporter = UnitTestReporter()
functest.reports.register_reporter(unittestreporter)
-class WindmillUnitTestCase(TestCase):
+
+# Windmill use case are written with no anonymous user
+from cubicweb.devtools.httptest import CubicWebServerConfig
+CubicWebServerConfig.anonymous_logged = False
+
+class CubicWebWindmillUseCase(CubicWebServerTC):
+ """basic class for Windmill use case tests
+
+ If you want to change cubicweb test server parameters, define a new
+ :class:`CubicWebServerConfig` and override the :var:`configcls`
+ attribute:
+
+ configcls = CubicWebServerConfig
+
+ From Windmill configuration:
+
+ .. attribute:: browser
+ identification string (firefox|ie|safari|chrome) (firefox by default)
+ .. attribute :: edit_test
+ load and edit test for debugging (False by default)
+ .. attribute:: test_dir (optional)
+ testing file path or directory (windmill directory under your unit case
+ file by default)
+
+ Examples:
+
+ browser = 'firefox'
+ test_dir = osp.join(__file__, 'windmill')
+ edit_test = False
+
+ If you prefer, you can put here the use cases recorded by windmill GUI
+ (services transformer) instead of the windmill sub-directory
+ You can change `test_dir` as following:
+
+ test_dir = __file__
+
+ Instead of toggle `edit_test` value, try `pytest -i`
+ """
+ browser = 'firefox'
+ edit_test = "-i" in sys.argv # detection for pytest invocation
+
+ def _test_dir(self):
+ """access to class attribute if possible or make assumption
+ of expected directory"""
+ try:
+ return getattr(self, 'test_dir')
+ except AttributeError:
+ if os.path.basename(sys.argv[0]) == "pytest":
+ test_dir = os.getcwd()
+ else:
+ import inspect
+ test_dir = os.path.dirname(inspect.stack()[-1][1])
+ return osp.join(test_dir, 'windmill')
+
def setUp(self):
+ # Start CubicWeb session before running the server to populate self.vreg
+ CubicWebServerTC.setUp(self)
+ # XXX reduce log output (should be done in a cleaner way)
+ # windmill fu** up our logging configuration
+ for logkey in ('windmill', 'logilab', 'cubicweb'):
+ getLogger(logkey).setLevel(ERROR)
+ self.test_dir = self._test_dir()
+ msg = "provide a valid 'test_dir' as the given test file/dir (current: %s)"
+ assert os.path.exists(self.test_dir), (msg % self.test_dir)
+ # windmill setup
windmill.stdout, windmill.stdin = sys.stdout, sys.stdin
- from windmill.bin.admin_lib import configure_global_settings, setup
configure_global_settings()
- windmill.settings['TEST_URL'] = self.test_url
+ windmill.settings['TEST_URL'] = self.config['base-url']
if hasattr(self,"windmill_settings"):
for (setting,value) in self.windmill_settings.iteritems():
windmill.settings[setting] = value
self.windmill_shell_objects = setup()
def tearDown(self):
- from windmill.bin.admin_lib import teardown
teardown(self.windmill_shell_objects)
-
-
-class CubicWebWindmillUseCase(CubicWebServerTC, WindmillUnitTestCase):
- """basic class for Windmill use case tests
-
- :param browser: browser identification string (firefox|ie|safari|chrome) (firefox by default)
- :param test_dir: testing file path or directory (./windmill by default)
- :param edit_test: load and edit test for debugging (False by default)
- """
- browser = 'firefox'
- test_dir = osp.join(os.getcwd(), 'windmill')
- edit_test = "-i" in sys.argv # detection for pytest invocation
-
- def setUp(self):
- # reduce log output
- from logging import getLogger, ERROR
- getLogger('cubicweb').setLevel(ERROR)
- getLogger('logilab').setLevel(ERROR)
- getLogger('windmill').setLevel(ERROR)
- # Start CubicWeb session before running the server to populate self.vreg
- CubicWebServerTC.setUp(self)
- assert os.path.exists(self.test_dir), "provide 'test_dir' as the given test file/dir"
- WindmillUnitTestCase.setUp(self)
-
- def tearDown(self):
- WindmillUnitTestCase.tearDown(self)
CubicWebServerTC.tearDown(self)
def testWindmill(self):
@@ -91,8 +128,8 @@
# see windmill.bin.admin_options.Firebug
windmill.settings['INSTALL_FIREBUG'] = 'firebug'
windmill.settings.setdefault('MOZILLA_PLUGINS', []).extend(
- '/usr/share/mozilla-extensions/',
- '/usr/share/xul-ext/')
+ ['/usr/share/mozilla-extensions/',
+ '/usr/share/xul-ext/'])
controller = self.windmill_shell_objects['start_' + self.browser]()
self.windmill_shell_objects['do_test'](self.test_dir,
load=self.edit_test,
--- a/devtools/httptest.py Fri Oct 08 17:07:46 2010 +0200
+++ b/devtools/httptest.py Sat Oct 09 00:05:49 2010 +0200
@@ -25,11 +25,13 @@
import threading
import socket
import httplib
+from urlparse import urlparse
from twisted.internet import reactor, error
from cubicweb.etwist.server import run
from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.devtools import ApptestConfiguration
def get_available_port(ports_scan):
@@ -43,6 +45,8 @@
:type ports_range: list
:param ports_range: range of ports to test
:rtype: int
+
+ .. see:: :func:`test.test_support.bind_port`
"""
for port in ports_scan:
try:
@@ -56,40 +60,52 @@
raise RuntimeError('get_available_port([ports_range]) cannot find an available port')
-class CubicWebServerTC(CubicWebTC):
- """basic class for running test server
+class CubicWebServerConfig(ApptestConfiguration):
+ """basic configuration class for configuring test server
:param ports_range: range of http ports to test (range(7000, 8000) by default)
:type ports_range: iterable
:param anonymous_logged: is anonymous user logged by default ? (True by default)
:type anonymous_logged: bool
- :param test_url: base url used by server
- :param test_host: server host
- :param test_port: server port
+ :param port: server port (optional, used to force value)
+ :type port: int
The first port found as available in `ports_range` will be used to launch
the test server
"""
- ports_range = range(7000, 8000)
# anonymous is logged by default in cubicweb test cases
anonymous_logged = True
- test_host='127.0.0.1'
+ ports_range = range(7000, 8000)
+
+ def default_base_url(self):
+ port = self['port'] or get_available_port(self.ports_range)
+ self.global_set_option('port', port) # force rewrite here
+ return 'http://127.0.0.1:%d/' % self['port']
+
+ def pyro_enabled(self):
+ return False
+ def load_configuration(self):
+ super(CubicWebServerConfig, self).load_configuration()
+ self.global_set_option('base-url', self.default_base_url())
+ if not self.anonymous_logged:
+ self.global_set_option('anonymous-user', None)
+ else:
+ self.global_set_option('anonymous-user', 'anon')
+ self.global_set_option('anonymous-password', 'anon')
+ self.global_set_option('force-html-content-type', True)
+ # no undo support in tests
+ self.global_set_option('undo-support', '')
- @property
- def test_url(self):
- return 'http://%s:%d/' % (self.test_host, self.test_port)
+class CubicWebServerTC(CubicWebTC):
+ """class for running test server
- def init_server(self):
- self.test_port = get_available_port(self.ports_range)
- self.config['port'] = self.test_port
- self.config['base-url'] = self.test_url
- self.config['force-html-content-type'] = True
- self.config['pyro-server'] = False
+ :cvar: :ref:`CubicWebServerConfig` class
+ """
+ configcls = CubicWebServerConfig
def start_server(self):
- self.config.pyro_enabled = lambda : False
# use a semaphore to avoid starting test while the http server isn't
# fully initilialized
semaphore = threading.Semaphore(0)
@@ -103,15 +119,16 @@
t = threading.Thread(target=safe_run, name='cubicweb_test_web_server',
args=(self.config, self.vreg, True))
self.web_thread = t
- if not self.anonymous_logged:
- self.config.global_set_option('anonymous-user', None)
t.start()
semaphore.acquire()
if not self.web_thread.isAlive():
# XXX race condition with actual thread death
raise RuntimeError('Could not start the web server')
#pre init utils connection
- self._web_test_cnx = httplib.HTTPConnection(self.test_host, self.test_port)
+ parseurl = urlparse(self.config['base-url'])
+ assert parseurl.port == self.config['port']
+ self._web_test_cnx = httplib.HTTPConnection(parseurl.hostname,
+ parseurl.port)
self._ident_cookie = None
def stop_server(self, timeout=15):
@@ -169,7 +186,6 @@
def setUp(self):
CubicWebTC.setUp(self)
- self.init_server()
self.start_server()
def tearDown(self):
@@ -179,4 +195,3 @@
# Server could be launched manually
print err
CubicWebTC.tearDown(self)
-
--- a/devtools/qunit.py Fri Oct 08 17:07:46 2010 +0200
+++ b/devtools/qunit.py Sat Oct 09 00:05:49 2010 +0200
@@ -106,8 +106,6 @@
return osp.abspath(osp.join(dirname,path))
return path
-
-
def test_javascripts(self):
for args in self.all_js_tests:
test_file = self.abspath(args[0])
@@ -130,12 +128,11 @@
for data in data_files:
assert osp.exists(data), data
-
# generate html test file
jquery_dir = 'file://' + self.config.locate_resource('jquery.js')[0]
html_test_file = NamedTemporaryFile(suffix='.html')
html_test_file.write(make_qunit_html(test_file, depends,
- server_data=(self.test_host, self.test_port),
+ base_url=self.config['base-url'],
web_data_path=jquery_dir))
html_test_file.flush()
# copying data file
@@ -210,21 +207,19 @@
self._log_stack.append('%s: %s' % (result, message))
-
def cw_path(*paths):
return file_path(osp.join(cubicweb.CW_SOFTWARE_ROOT, *paths))
def file_path(path):
return 'file://' + osp.abspath(path)
-def build_js_script( host, port):
+def build_js_script(host):
return """
var host = '%s';
- var port = '%s';
QUnit.moduleStart = function (name) {
jQuery.ajax({
- url: 'http://'+host+':'+port+'/qunit_result',
+ url: host+'/qunit_result',
data: {"event": "module_start",
"name": name},
async: false});
@@ -232,7 +227,7 @@
QUnit.testDone = function (name, failures, total) {
jQuery.ajax({
- url: 'http://'+host+':'+port+'/qunit_result',
+ url: host+'/qunit_result',
data: {"event": "test_done",
"name": name,
"failures": failures,
@@ -242,7 +237,7 @@
QUnit.done = function (failures, total) {
jQuery.ajax({
- url: 'http://'+host+':'+port+'/qunit_result',
+ url: host+'/qunit_result',
data: {"event": "done",
"failures": failures,
"total":total},
@@ -252,15 +247,15 @@
QUnit.log = function (result, message) {
jQuery.ajax({
- url: 'http://'+host+':'+port+'/qunit_result',
+ url: host+'/qunit_result',
data: {"event": "log",
"result": result,
"message": message},
async: false});
}
- """ % (host, port)
+ """ % host
-def make_qunit_html(test_file, depends=(), server_data=None,
+def make_qunit_html(test_file, depends=(), base_url=None,
web_data_path=cw_path('web', 'data')):
""""""
data = {
@@ -276,11 +271,10 @@
<script src="%(web_test)s/cwmock.js" type="text/javascript"></script>
<script src="%(web_test)s/qunit.js" type="text/javascript"></script>'''
% data]
- if server_data is not None:
- host, port = server_data
+ if base_url is not None:
html.append('<!-- result report tools -->')
html.append('<script type="text/javascript">')
- html.append(build_js_script(host, port))
+ html.append(build_js_script(base_url))
html.append('</script>')
html.append('<!-- Test script dependencies (tested code for example) -->')
@@ -303,9 +297,5 @@
-
-
-
-
if __name__ == '__main__':
unittest_main()
--- a/devtools/repotest.py Fri Oct 08 17:07:46 2010 +0200
+++ b/devtools/repotest.py Sat Oct 09 00:05:49 2010 +0200
@@ -158,7 +158,10 @@
ExecutionPlan._check_permissions = _dummy_check_permissions
rqlannotation._select_principal = _select_principal
if self.backend is not None:
- dbhelper = get_db_helper(self.backend)
+ try:
+ dbhelper = get_db_helper(self.backend)
+ except ImportError, ex:
+ self.skipTest(str(ex))
self.o = SQLGenerator(self.schema, dbhelper)
def tearDown(self):
--- a/devtools/test/unittest_httptest.py Fri Oct 08 17:07:46 2010 +0200
+++ b/devtools/test/unittest_httptest.py Sat Oct 09 00:05:49 2010 +0200
@@ -1,8 +1,7 @@
-from logilab.common.testlib import TestCase, unittest_main, tag
-from cubicweb.devtools.httptest import CubicWebServerTC
+import httplib
-import httplib
-from os import path as osp
+from cubicweb.devtools.httptest import CubicWebServerTC
+from cubicweb.devtools.httptest import CubicWebServerConfig
class TwistedCWAnonTC(CubicWebServerTC):
@@ -17,15 +16,16 @@
response = self.web_get()
self.assertEqual(response.status, httplib.OK)
-
def test_base_url(self):
- if self.test_url not in self.web_get().read():
+ if self.config['base-url'] not in self.web_get().read():
self.fail('no mention of base url in retrieved page')
class TwistedCWIdentTC(CubicWebServerTC):
- anonymous_logged = False
+ def setUp(self):
+ CubicWebServerConfig.anonymous_logged = False
+ CubicWebServerTC.setUp(self)
def test_response_denied(self):
response = self.web_get()
@@ -48,4 +48,5 @@
if __name__ == '__main__':
+ from logilab.common.testlib import unittest_main
unittest_main()
--- a/devtools/testlib.py Fri Oct 08 17:07:46 2010 +0200
+++ b/devtools/testlib.py Sat Oct 09 00:05:49 2010 +0200
@@ -71,7 +71,6 @@
after = before
return center - before <= line_no <= center + after
-
def unprotected_entities(schema, strict=False):
"""returned a set of each non final entity type, excluding "system" entities
(eg CWGroup, CWUser...)
@@ -82,7 +81,6 @@
protected_entities = yams.schema.BASE_TYPES.union(SYSTEM_ENTITIES)
return set(schema.entities()) - protected_entities
-
def refresh_repo(repo, resetschema=False, resetvreg=False):
for pool in repo.pools:
pool.close(True)
@@ -144,6 +142,7 @@
cwconfig.SMTP = MockSMTP
+
class TestCaseConnectionProxy(object):
"""thin wrapper around `cubicweb.dbapi.Connection` context-manager
used in CubicWebTC (cf. `cubicweb.devtools.testlib.CubicWebTC.login` method)
@@ -192,8 +191,9 @@
@classproperty
def config(cls):
- """return the configuration object. Configuration is cached on the test
- class.
+ """return the configuration object
+
+ Configuration is cached on the test class.
"""
try:
return cls.__dict__['_config']
@@ -204,7 +204,12 @@
@classmethod
def init_config(cls, config):
- """configuration initialization hooks. You may want to override this."""
+ """configuration initialization hooks.
+
+ You may only want to override here the configuraton logic.
+
+ Otherwise, consider to use a different :class:`ApptestConfiguration`
+ defined in the `configcls` class attribute"""
source = config.sources()['system']
cls.admlogin = unicode(source['db-user'])
cls.admpassword = source['db-password']
@@ -226,7 +231,7 @@
config.global_set_option('default-dest-addrs', send_to)
config.global_set_option('sender-name', 'cubicweb-test')
config.global_set_option('sender-addr', 'cubicweb-test@logilab.fr')
- config.global_set_option('base-url', BASE_URL)
+
# web resources
try:
config.global_set_option('embed-allowed', re.compile('.*'))
--- a/doc/book/en/tutorials/tools/windmill.rst Fri Oct 08 17:07:46 2010 +0200
+++ b/doc/book/en/tutorials/tools/windmill.rst Sat Oct 09 00:05:49 2010 +0200
@@ -100,6 +100,23 @@
Integrate Windmill tests into CubicWeb
======================================
+Set environment
+---------------
+
+You have to create a new unit test file and a `windmill` directory and copy all
+your windmill use case into it.
+
+.. sourcecode:: python
+
+ # test_windmill.py
+
+ # Run all scenarii found in windmill directory
+ from cubicweb.devtools.cwwindmill import (CubicWebWindmillUseCase,
+ unittest_main)
+
+ if __name__ == '__main__':
+ unittest_main()
+
Run your tests
--------------
@@ -114,9 +131,34 @@
to run test instance server on localhost. In the general case, You've no need
to change anything.
-Check the :class:`cubicweb.devtools.cwwindmill.CubicWebServerTC` class for server
-parameters and :class:`cubicweb.devtools.cwwindmill.CubicWebWindmillUseCase` for
-Windmill configuration.
+Check :class:`cubicweb.devtools.cwwindmill.CubicWebWindmillUseCase` for
+Windmill configuration. You can edit windmill settings with following class attributes:
+
+* browser
+ identification string (firefox|ie|safari|chrome) (firefox by default)
+* test_dir
+ testing file path or directory (windmill directory under your unit case
+ file by default)
+* edit_test
+ load and edit test for debugging (False by default)
+
+Examples:
+
+ browser = 'firefox'
+ test_dir = osp.join(__file__, 'windmill')
+ edit_test = False
+
+If you want to change cubicweb test server parameters, you can check class
+variables from :class:`CubicWebServerConfig` or inherit it with overriding the
+:var:`configcls` attribute in :class:`CubicWebServerTC` ::
+
+.. sourcecode:: python
+
+ class OtherCubicWebServerConfig(CubicWebServerConfig):
+ port = 9999
+
+ class NewCubicWebServerTC(CubicWebServerTC):
+ configcls = OtherCubicWebServerConfig
For instance, CubicWeb framework windmill tests can be manually run by::
--- a/entity.py Fri Oct 08 17:07:46 2010 +0200
+++ b/entity.py Sat Oct 09 00:05:49 2010 +0200
@@ -1108,7 +1108,7 @@
def __get__(self, eobj, eclass):
if eobj is None:
- raise AttributeError('%s cannot be only be accessed from instances'
+ raise AttributeError('%s can only be accessed from instances'
% self._rtype)
return eobj.related(self._rtype, self._role, entities=True)
--- a/web/test/test_windmill.py Fri Oct 08 17:07:46 2010 +0200
+++ b/web/test/test_windmill.py Sat Oct 09 00:05:49 2010 +0200
@@ -1,48 +1,6 @@
-import os, os.path as osp
-
-from cubicweb.devtools import cwwindmill
-
-
-class CubicWebWindmillUseCase(cwwindmill.CubicWebWindmillUseCase):
- """class for windmill use case tests
-
- From test server parameters:
-
- :params ports_range: range of http ports to test (range(7000, 8000) by default)
- :type ports_range: iterable
- :param anonymous_logged: is anonymous user logged by default ?
- :type anonymous_logged: bool
-
- The first port found as available in `ports_range` will be used to launch
- the test server
-
- Instead of toggle `edit_test` value, try `pytest -i`
-
- From Windmill configuration:
-
- :param browser: browser identification string (firefox|ie|safari|chrome) (firefox by default)
- :param test_dir: testing file path or directory (./windmill by default)
- :param edit_test: load and edit test for debugging (False by default)
- """
- #ports_range = range(7000, 8000)
- anonymous_logged = False
- #browser = 'firefox'
- #test_dir = osp.join(os.getcwd(), 'windmill')
- #edit_test = False
-
- # If you prefer, you can put here the use cases recorded by windmill GUI
- # (services transformer) instead of the windmill sub-directory
- # You can change `test_dir` as following:
- #test_dir = __file__
-
-
-from windmill.authoring import WindmillTestClient
-def test_usecase():
- client = WindmillTestClient(__name__)
- import pdb; pdb.set_trace()
- client.open(url=u'/')
-# ...
-
+# Run all scenarii found in windmill directory
+from cubicweb.devtools.cwwindmill import (CubicWebWindmillUseCase,
+ unittest_main)
if __name__ == '__main__':
- cwwindmill.unittest_main()
+ unittest_main()
--- a/web/views/wdoc.py Fri Oct 08 17:07:46 2010 +0200
+++ b/web/views/wdoc.py Sat Oct 09 00:05:49 2010 +0200
@@ -62,9 +62,11 @@
snode = index[section.attrib['insertbefore']]
node = snode.parent
idx = node.getchildren().index(snode)
- else:
+ elif 'appendto' in section.attrib:
node = index[section.attrib['appendto']]
idx = None
+ else:
+ node, idx = None, None
return node, idx
def build_toc(config):
@@ -79,6 +81,8 @@
toc = parse(fpath).getroot()
for section in toc:
node, idx = get_insertion_point(section, index)
+ if node is None:
+ continue
if idx is None:
node.append(section)
else:
--- a/web/webconfig.py Fri Oct 08 17:07:46 2010 +0200
+++ b/web/webconfig.py Sat Oct 09 00:05:49 2010 +0200
@@ -202,8 +202,6 @@
'help': 'use cubicweb.old.css instead of 3.9 cubicweb.css',
'group': 'web', 'level': 2,
}),
-
-
))
def fckeditor_installed(self):