backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 08 Mar 2011 18:32:19 +0100
changeset 7061 bb2080547722
parent 7059 1d65b235549f (current diff)
parent 7060 f26a1cbddc91 (diff)
child 7062 dca2215c5e82
backport stable
dbapi.py
devtools/cwwindmill.py
devtools/testlib.py
server/__init__.py
server/serverctl.py
server/sources/ldapuser.py
--- a/dbapi.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/dbapi.py	Tue Mar 08 18:32:19 2011 +0100
@@ -201,21 +201,30 @@
     cnx.vreg = vreg
     return cnx
 
-def in_memory_cnx(config, login, **kwargs):
-    """usefull method for testing and scripting to get a dbapi.Connection
-    object connected to an in-memory repository instance
-    """
+def in_memory_repo(config):
+    """Return and in_memory Repository object from a config (or vreg)"""
     if isinstance(config, cwvreg.CubicWebVRegistry):
         vreg = config
         config = None
     else:
         vreg = None
     # get local access to the repository
-    repo = get_repository('inmemory', config=config, vreg=vreg)
-    # connection to the CubicWeb repository
+    return get_repository('inmemory', config=config, vreg=vreg)
+
+def in_memory_cnx(repo, login, **kwargs):
+    """Establish a In memory connection to a <repo> for the user with <login>
+
+    additionel credential might be required"""
     cnxprops = ConnectionProperties('inmemory')
-    cnx = repo_connect(repo, login, cnxprops=cnxprops, **kwargs)
-    return repo, cnx
+    return repo_connect(repo, login, cnxprops=cnxprops, **kwargs)
+
+def in_memory_repo_cnx(config, login, **kwargs):
+    """usefull method for testing and scripting to get a dbapi.Connection
+    object connected to an in-memory repository instance
+    """
+    # connection to the CubicWeb repository
+    repo = in_memory_repo(config)
+    return repo, in_memory_cnx(repo, login, **kwargs)
 
 class _NeedAuthAccessMock(object):
     def __getattribute__(self, attr):
--- a/devtools/__init__.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/devtools/__init__.py	Tue Mar 08 18:32:19 2011 +0100
@@ -217,7 +217,7 @@
 
 def init_test_database(config=None, appid='data', apphome=None):
     """init a test database for a specific driver"""
-    from cubicweb.dbapi import in_memory_cnx
+    from cubicweb.dbapi import in_memory_repo_cnx
     config = config or TestServerConfiguration(appid, apphome=apphome)
     sources = config.sources()
     driver = sources['system']['db-driver']
@@ -229,7 +229,7 @@
         else:
             raise ValueError('no initialization function for driver %r' % driver)
     config._cubes = None # avoid assertion error
-    repo, cnx = in_memory_cnx(config, unicode(sources['admin']['login']),
+    repo, cnx = in_memory_repo_cnx(config, unicode(sources['admin']['login']),
                               password=sources['admin']['password'] or 'xxx')
     if driver == 'sqlite':
         install_sqlite_patch(repo.querier)
--- a/devtools/cwwindmill.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/devtools/cwwindmill.py	Tue Mar 08 18:32:19 2011 +0100
@@ -29,120 +29,133 @@
 import sys
 
 # imported by default to simplify further import statements
-from logilab.common.testlib import TestCase, unittest_main
+from logilab.common.testlib import TestCase, unittest_main, Tags
 
-import windmill
-from windmill.dep import functest
-from windmill.bin.admin_lib import configure_global_settings, setup, teardown
+try:
+    import windmill
+    from windmill.dep import functest
+    from windmill.bin.admin_lib import configure_global_settings, setup, teardown
+except ImportError, ex:
+    windmill = None
 
 from cubicweb.devtools.httptest import CubicWebServerTC, CubicWebServerConfig
 
+if windmill is None:
+    class CubicWebWindmillUseCase(CubicWebServerTC):
+        tags = CubicWebServerTC.tags & Tags(('windmill',))
 
-# Excerpt from :ref:`windmill.authoring.unit`
-class UnitTestReporter(functest.reports.FunctestReportInterface):
-    def summary(self, test_list, totals_dict, stdout_capture):
-        self.test_list = test_list
-
-unittestreporter = UnitTestReporter()
-functest.reports.register_reporter(unittestreporter)
+        def testWindmill(self):
+            self.skipTest("can't import windmill %s" % ex)
+else:
+    # Excerpt from :ref:`windmill.authoring.unit`
+    class UnitTestReporter(functest.reports.FunctestReportInterface):
+        def summary(self, test_list, totals_dict, stdout_capture):
+            self.test_list = test_list
 
-class CubicWebWindmillUseCase(CubicWebServerTC):
-    """basic class for Windmill use case tests
+    unittestreporter = UnitTestReporter()
+    functest.reports.register_reporter(unittestreporter)
+
+    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:
+        If you want to change cubicweb test server parameters, define a new
+        :class:`CubicWebServerConfig` and override the :var:`configcls`
+        attribute:
 
-        configcls = CubicWebServerConfig
+            configcls = CubicWebServerConfig
 
-    From Windmill configuration:
+        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)
+        .. 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:
 
-    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 `python <test script> -f`
+        """
         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:
+<<<<<<< /home/syt/src/fcubicweb/cubicweb/devtools/cwwindmill.py
 
-        test_dir = __file__
+        edit_test = "-i" in sys.argv # detection for pytest invocation
+        # Windmill use case are written with no anonymous user
+        anonymous_logged = False
 
-    Instead of toggle `edit_test` value, try `python <test script> -f`
-    """
-    browser = 'firefox'
-    edit_test = "-f" in sys.argv or "-i" in sys.argv # XXX pytest
-    # Windmill use case are written with no anonymous user
-    anonymous_logged = False
+        tags = CubicWebServerTC.tags & Tags(('windmill',))
 
-    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 _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
-        super(CubicWebWindmillUseCase, self).setUp()
-        # 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
-        configure_global_settings()
-        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 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
+            configure_global_settings()
+            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):
-        teardown(self.windmill_shell_objects)
-        super(CubicWebWindmillUseCase, self).tearDown()
+        def tearDown(self):
+            teardown(self.windmill_shell_objects)
+            CubicWebServerTC.tearDown(self)
 
-    def testWindmill(self):
-        if self.edit_test:
-            # 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/'])
-        controller = self.windmill_shell_objects['start_' + self.browser]()
-        self.windmill_shell_objects['do_test'](self.test_dir,
-                                               load=self.edit_test,
-                                               threaded=False)
-        # set a breakpoint to be able to debug windmill test
-        if self.edit_test:
-            import pdb; pdb.set_trace()
-            return
+        def testWindmill(self):
+            if self.edit_test:
+                # 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/'])
+            controller = self.windmill_shell_objects['start_' + self.browser]()
+            self.windmill_shell_objects['do_test'](self.test_dir,
+                                                   load=self.edit_test,
+                                                   threaded=False)
+            # set a breakpoint to be able to debug windmill test
+            if self.edit_test:
+                import pdb; pdb.set_trace()
+                return
 
-        # reporter
-        for test in unittestreporter.test_list:
-            msg = ""
-            self._testMethodDoc = getattr(test, "__doc__", None)
-            self._testMethodName = test.__name__
-            # try to display a better message in case of failure
-            if hasattr(test, "tb"):
-                msg = '\n'.join(test.tb)
-            self.assertEqual(test.result, True, msg=msg)
+            # reporter
+            for test in unittestreporter.test_list:
+                msg = ""
+                self._testMethodDoc = getattr(test, "__doc__", None)
+                self._testMethodName = test.__name__
+                # try to display a better message in case of failure
+                if hasattr(test, "tb"):
+                    msg = '\n'.join(test.tb)
+                self.assertEqual(test.result, True, msg=msg)
 
--- a/devtools/livetest.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/devtools/livetest.py	Tue Mar 08 18:32:19 2011 +0100
@@ -35,7 +35,7 @@
 
 from logilab.common.testlib import TestCase
 
-from cubicweb.dbapi import in_memory_cnx
+from cubicweb.dbapi import in_memory_repo_cnx
 from cubicweb.etwist.server import CubicWebRootResource
 from cubicweb.devtools import BaseApptestConfiguration, init_test_database
 
@@ -164,7 +164,7 @@
         # build a config, and get a connection
         self.config = LivetestConfiguration(self.cube, self.sourcefile)
         _, user, passwd, _ = loadconf()
-        self.repo, self.cnx = in_memory_cnx(self.config, user, password=passwd)
+        self.repo, self.cnx = in_memory_repo_cnx(self.config, user, password=passwd)
         self.setup_db(self.cnx)
 
     def tearDown(self):
--- a/devtools/qunit.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/devtools/qunit.py	Tue Mar 08 18:32:19 2011 +0100
@@ -8,7 +8,7 @@
 from uuid import uuid4 
 
 # imported by default to simplify further import statements
-from logilab.common.testlib import unittest_main, with_tempdir, InnerTest
+from logilab.common.testlib import unittest_main, with_tempdir, InnerTest, Tags
 from logilab.common.shellutils import getlogin
 
 import cubicweb
@@ -86,6 +86,8 @@
 
 class QUnitTestCase(CubicWebServerTC):
 
+    tags = CubicWebServerTC.tags | Tags(('qunit',))
+
     # testfile, (dep_a, dep_b)
     all_js_tests = ()
 
--- a/devtools/test/unittest_httptest.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/devtools/test/unittest_httptest.py	Tue Mar 08 18:32:19 2011 +0100
@@ -19,6 +19,7 @@
 
 import httplib
 
+from logilab.common.testlib import Tags
 from cubicweb.devtools.httptest import CubicWebServerTC, CubicWebServerConfig
 
 
@@ -40,7 +41,9 @@
 
 
 class TwistedCWIdentTC(CubicWebServerTC):
+
     anonymous_logged = False
+    tags = CubicWebServerTC.tags | Tags(('auth',))
 
     def test_response_denied(self):
         response = self.web_get()
@@ -49,7 +52,7 @@
     def test_login(self):
         response = self.web_get()
         if response.status != httplib.FORBIDDEN:
-             self.skipTest('Already authenticated')
+            self.skipTest('Already authenticated, "test_response_denied" must have failed')
         # login
         self.web_login(self.admlogin, self.admpassword)
         response = self.web_get()
--- a/devtools/testlib.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/devtools/testlib.py	Tue Mar 08 18:32:19 2011 +0100
@@ -854,6 +854,27 @@
             raise self.failureException("doctest file '%s' failed"
                                         % testfile)
 
+    # notifications ############################################################
+
+    def assertSentEmail(self, subject, recipients=None, nb_msgs=None):
+        """test recipients in system mailbox for given email subject
+
+        :param subject: email subject to find in mailbox
+        :param recipients: list of email recipients
+        :param nb_msgs: expected number of entries
+        :returns: list of matched emails
+        """
+        messages = [email for email in MAILBOX
+                    if email.message.get('Subject') == subject]
+        if recipients is not None:
+            sent_to = set()
+            for msg in messages:
+                sent_to.update(msg.recipients)
+            self.assertSetEqual(set(recipients), sent_to)
+        if nb_msgs is not None:
+            self.assertEqual(len(MAILBOX), nb_msgs)
+        return messages
+
     # deprecated ###############################################################
 
     @deprecated('[3.8] use self.execute(...).get_entity(0, 0)')
--- a/misc/migration/3.10.9_Any.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/misc/migration/3.10.9_Any.py	Tue Mar 08 18:32:19 2011 +0100
@@ -1,4 +1,5 @@
 from __future__ import with_statement
+import sys
 
 # fix some corrupted entities noticed on several instances
 rql('DELETE CWConstraint X WHERE NOT E constrained_by X')
@@ -9,13 +10,17 @@
     from logilab.common.shellutils import ProgressBar
     from cubicweb.server.session import hooks_control
     rset = rql('Any X, XC WHERE X cwuri XC, X cwuri ~= "%/eid/%"')
-    pb = ProgressBar(nbops=rset.rowcount, size=70)
+    if sys.stdout.isatty():
+        pb = ProgressBar(nbops=rset.rowcount, size=70)
+    else:
+        pb = None
     with hooks_control(session, session.HOOKS_DENY_ALL, 'integrity'):
         for i,  e in enumerate(rset.entities()):
             e.set_attributes(cwuri=e.cwuri.replace('/eid', ''))
             if i % 100: # commit every 100 entities to limit memory consumption
                 commit(ask_confirm=False)
-            pb.update()
+            if pb is not None:
+                pb.update()
     commit(ask_confirm=False)
 
 try:
--- a/misc/scripts/repair_file_1-9_migration.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/misc/scripts/repair_file_1-9_migration.py	Tue Mar 08 18:32:19 2011 +0100
@@ -19,9 +19,9 @@
 sourcescfg = repo.config.sources()
 backupcfg = cwconfig.instance_configuration(backupinstance)
 backupcfg.repairing = True
-backuprepo, backupcnx = dbapi.in_memory_cnx(backupcfg, sourcescfg['admin']['login'],
-                                            password=sourcescfg['admin']['password'],
-                                            host='localhost')
+backuprepo, backupcnx = dbapi.in_memory_repo_cnx(backupcfg, sourcescfg['admin']['login'],
+                                                 password=sourcescfg['admin']['password'],
+                                                 host='localhost')
 backupcu = backupcnx.cursor()
 
 with hooks_control(session, session.HOOKS_DENY_ALL):
--- a/server/__init__.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/server/__init__.py	Tue Mar 08 18:32:19 2011 +0100
@@ -121,7 +121,7 @@
     with the minimal set of entities (ie at least the schema, base groups and
     a initial user)
     """
-    from cubicweb.dbapi import in_memory_cnx
+    from cubicweb.dbapi import in_memory_repo_cnx
     from cubicweb.server.repository import Repository
     from cubicweb.server.utils import manager_userpasswd
     from cubicweb.server.sqlutils import sqlexec, sqlschema, sqldropschema
@@ -185,7 +185,7 @@
     repo.shutdown()
     # reloging using the admin user
     config._cubes = None # avoid assertion error
-    repo, cnx = in_memory_cnx(config, login, password=pwd)
+    repo, cnx = in_memory_repo_cnx(config, login, password=pwd)
     repo.system_source.eid = ssource.eid # redo this manually
     # trigger vreg initialisation of entity classes
     config.cubicweb_appobject_path = set(('entities',))
--- a/server/serverctl.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/server/serverctl.py	Tue Mar 08 18:32:19 2011 +0100
@@ -130,7 +130,7 @@
 
 def repo_cnx(config):
     """return a in-memory repository and a db api connection it"""
-    from cubicweb.dbapi import in_memory_cnx
+    from cubicweb.dbapi import in_memory_repo_cnx
     from cubicweb.server.utils import manager_userpasswd
     try:
         login = config.sources()['admin']['login']
@@ -139,7 +139,7 @@
         login, pwd = manager_userpasswd()
     while True:
         try:
-            return in_memory_cnx(config, login, password=pwd)
+            return in_memory_repo_cnx(config, login, password=pwd)
         except AuthenticationError:
             print '-> Error: wrong user/password.'
             # reset cubes else we'll have an assertion error on next retry
--- a/server/session.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/server/session.py	Tue Mar 08 18:32:19 2011 +0100
@@ -854,6 +854,10 @@
         del self.__threaddata
         del self._tx_data
 
+    @property
+    def closed(self):
+        return not hasattr(self, '_tx_data')
+
     # transaction data/operations management ##################################
 
     @property
--- a/server/sources/ldapuser.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/server/sources/ldapuser.py	Tue Mar 08 18:32:19 2011 +0100
@@ -196,6 +196,7 @@
         self._interval = typedconfig['synchronization-interval']
         self._cache_ttl = max(71, typedconfig['cache-life-time'])
         self.reset_caches()
+        self._conn = None
 
     def reset_caches(self):
         """method called during test to reset potential source caches"""
--- a/server/test/unittest_storage.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/server/test/unittest_storage.py	Tue Mar 08 18:32:19 2011 +0100
@@ -60,7 +60,7 @@
         storages.set_attribute_storage(self.repo, 'File', 'data', bfs_storage)
 
     def tearDown(self):
-        super(CubicWebTC, self).tearDown()
+        super(StorageTC, self).tearDown()
         storages.unset_attribute_storage(self.repo, 'File', 'data')
         shutil.rmtree(self.tempdir)
 
--- a/uilib.py	Fri Feb 18 15:26:00 2011 +0100
+++ b/uilib.py	Tue Mar 08 18:32:19 2011 +0100
@@ -47,9 +47,8 @@
     return 'Any X WHERE X eid %s' % eid
 
 def eid_param(name, eid):
+    assert name is not None
     assert eid is not None
-    if eid is None:
-        eid = ''
     return '%s:%s' % (name, eid)
 
 def printable_value(req, attrtype, value, props=None, displaytime=True):