merge stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 24 Feb 2010 15:08:13 +0100
branchstable
changeset 4696 ce3adab29aef
parent 4695 4aaf87e7f79e (diff)
parent 4684 876a79ece6f7 (current diff)
child 4697 b8263d717e74
child 4700 b981c7d3e3c0
merge
--- a/devtools/testlib.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/devtools/testlib.py	Wed Feb 24 15:08:13 2010 +0100
@@ -66,24 +66,7 @@
     return set(schema.entities()) - protected_entities
 
 
-def get_versions(self, checkversions=False):
-    """return the a dictionary containing cubes used by this instance
-    as key with their version as value, including cubicweb version. This is a
-    public method, not requiring a session id.
-
-    replace Repository.get_versions by this method if you want to get versions
-    from code instead of from the database
-    """
-    vcconf = {'cubicweb': self.config.cubicweb_version()}
-    self.config.bootstrap_cubes()
-    for pk in self.config.cubes():
-        version = self.config.cube_version(pk)
-        vcconf[pk] = version
-    self.config._cubes = None
-    return vcconf
-
-
-def refresh_repo(repo):
+def refresh_repo(repo, resetschema=False, resetvreg=False):
     devtools.reset_test_database(repo.config)
     for pool in repo.pools:
         pool.reconnect()
@@ -92,6 +75,8 @@
     repo.querier._rql_cache = {}
     for source in repo.sources:
         source.reset_caches()
+    if resetschema:
+        repo.set_schema(repo.config.load_schema(), resetvreg=resetvreg)
 
 
 # email handling, to test emails sent by an application ########################
@@ -160,6 +145,7 @@
     """
     appid = 'data'
     configcls = devtools.ApptestConfiguration
+    reset_schema = reset_vreg = False # reset schema / vreg between tests
 
     @classproperty
     def config(cls):
@@ -230,7 +216,7 @@
 
     @classmethod
     def _refresh_repo(cls):
-        refresh_repo(cls.repo)
+        refresh_repo(cls.repo, cls.reset_schema, cls.reset_vreg)
 
     # global resources accessors ###############################################
 
--- a/embedded/README	Wed Feb 24 12:49:55 2010 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-This directory contains extra libraries which are needed
-to make cubicweb work.
-
-The mx.DateTime python implementation is directly taken from
-the mx.DateTime distribution. The only modification is the
-strptime function which has been mocked using the standard
-``datetime`` module. (as provided by the python2.5's stdlib)
--- a/goa/goactl.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/goa/goactl.py	Wed Feb 24 15:08:13 2010 +0100
@@ -31,7 +31,6 @@
     (docutils.__path__[0], 'docutils'),
     (roman.__file__.replace('.pyc', '.py'), 'roman.py'),
 
-    (join(CW_SOFTWARE_ROOT, 'embedded', 'mx'), 'mx'),
     ('/usr/share/fckeditor/', 'fckeditor'),
 
     (join(CW_SOFTWARE_ROOT, 'web', 'data'), join('cubes', 'shared', 'data')),
--- a/goa/test/pytestconf.py	Wed Feb 24 12:49:55 2010 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-"""this pytestconf automatically adds the mx's python version in the PYTHONPATH
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
-"""
-import sys
-import os.path as osp
-
-import cubicweb
-# remove 'mx' modules imported by cubicweb
-for modname in sys.modules.keys():
-    if modname.startswith('mx'):
-        sys.modules.pop(modname)
-
-# this is where mx should get imported from
-mxpath = osp.abspath(osp.join(osp.dirname(cubicweb.__file__), 'embedded'))
-sys.path.insert(1, mxpath)
-
-# make sure the correct mx is imported
-import mx
-assert osp.dirname(mx.__file__) == osp.join(mxpath, 'mx'), '%s != %s' % (osp.dirname(mx.__file__), mxpath)
--- a/hooks/test/unittest_hooks.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/hooks/test/unittest_hooks.py	Wed Feb 24 15:08:13 2010 +0100
@@ -9,22 +9,8 @@
 
 from datetime import datetime
 
-from cubicweb import (ConnectionError, ValidationError, AuthenticationError,
-                      BadConnectionId)
-from cubicweb.devtools.testlib import CubicWebTC, get_versions
-
-from cubicweb.server.sqlutils import SQL_PREFIX
-from cubicweb.server.repository import Repository
-
-orig_get_versions = Repository.get_versions
-
-def setup_module(*args):
-    Repository.get_versions = get_versions
-
-def teardown_module(*args):
-    Repository.get_versions = orig_get_versions
-
-
+from cubicweb import ValidationError, AuthenticationError, BadConnectionId
+from cubicweb.devtools.testlib import CubicWebTC
 
 class CoreHooksTC(CubicWebTC):
 
--- a/hooks/test/unittest_syncschema.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/hooks/test/unittest_syncschema.py	Wed Feb 24 15:08:13 2010 +0100
@@ -1,38 +1,43 @@
 from logilab.common.testlib import TestCase, unittest_main
-from cubicweb.devtools.testlib import CubicWebTC
 
-#################
-# <required  ?> #
-#################
+from cubicweb import ValidationError
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.server.sqlutils import SQL_PREFIX
 
 
-from datetime import datetime
-
-from cubicweb import (ConnectionError, ValidationError, AuthenticationError,
-                      BadConnectionId)
-from cubicweb.devtools.testlib import get_versions
-
-from cubicweb.server.sqlutils import SQL_PREFIX
-from cubicweb.server.repository import Repository
-
-orig_get_versions = Repository.get_versions
-#################
-# </required ?> #
-#################
-
-def setup_module(*args):
-    Repository.get_versions = get_versions
-
-def teardown_module(*args):
-    Repository.get_versions = orig_get_versions
-
+SCHEMA_EIDS = {}
 class SchemaModificationHooksTC(CubicWebTC):
+    reset_schema = True
 
     @classmethod
     def init_config(cls, config):
         super(SchemaModificationHooksTC, cls).init_config(config)
+        # we have to read schema from the database to get eid for schema entities
         config._cubes = None
         cls.repo.fill_schema()
+        # remember them so we can reread it from the fs instead of the db (too
+        # costly) between tests
+        for x in cls.repo.schema.entities():
+            SCHEMA_EIDS[x] = x.eid
+        for x in cls.repo.schema.relations():
+            SCHEMA_EIDS[x] = x.eid
+            for rdef in x.rdefs.itervalues():
+                SCHEMA_EIDS[(rdef.subject, rdef.rtype, rdef.object)] = rdef.eid
+
+    @classmethod
+    def _refresh_repo(cls):
+        super(SchemaModificationHooksTC, cls)._refresh_repo()
+        # rebuild schema eid index
+        schema = cls.repo.schema
+        for x in schema.entities():
+            x.eid = SCHEMA_EIDS[x]
+            schema._eid_index[x.eid] = x
+        for x in cls.repo.schema.relations():
+            x.eid = SCHEMA_EIDS[x]
+            schema._eid_index[x.eid] = x
+            for rdef in x.rdefs.itervalues():
+                rdef.eid = SCHEMA_EIDS[(rdef.subject, rdef.rtype, rdef.object)]
+                schema._eid_index[rdef.eid] = rdef
 
     def index_exists(self, etype, attr, unique=False):
         self.session.set_pool()
--- a/server/checkintegrity.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/server/checkintegrity.py	Wed Feb 24 15:08:13 2010 +0100
@@ -111,6 +111,8 @@
             pb.update()
     # restore Entity.check
     Entity.check = _check
+    repo.config.disabled_hooks_categories.remove('metadata')
+    repo.config.disabled_hooks_categories.remove('integrity')
 
 
 def check_schema(schema, session, eids, fix=1):
--- a/server/migractions.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/server/migractions.py	Wed Feb 24 15:08:13 2010 +0100
@@ -973,7 +973,7 @@
         you usually want to use sync_schema_props_perms instead.
         """
         oldvalue = None
-        for constr in self.repo.schema.eschema(etype).constraints(rtype):
+        for constr in self.repo.schema.eschema(etype).rdef(rtype).constraints:
             if isinstance(constr, SizeConstraint):
                 oldvalue = constr.max
         if oldvalue == size:
--- a/server/repository.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/server/repository.py	Wed Feb 24 15:08:13 2010 +0100
@@ -102,10 +102,11 @@
     hooks order hazardness
     """
     # XXX now that rql in migraction default to unsafe_execute we don't want to
-    #     skip that anymore. Also we should imo rely on the orm to first fetch
-    #     existing entity if any then delete it
-    #if session.is_super_session:
-    #    return
+    #     skip that for super session (though we can still skip it for internal
+    #     sessions). Also we should imo rely on the orm to first fetch existing
+    #     entity if any then delete it.
+    if session.is_internal_session:
+        return
     card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
     # one may be tented to check for neweids but this may cause more than one
     # relation even with '1?'  cardinality if thoses relations are added in the
@@ -114,16 +115,27 @@
     # not expected for this).  So: don't do it, we pretend to ensure repository
     # consistency.
     #
-    # also, we must not use unsafe_execute since we want the delete permission
-    # to be checked when some existing relation is deleted
+    # XXX we don't want read permissions to be applied but we want delete
+    # permission to be checked
+    rschema = session.repo.schema.rschema(rtype)
     if card[0] in '1?':
-        rschema = session.repo.schema.rschema(rtype)
         if not rschema.inlined: # inlined relations will be implicitly deleted
-            session.execute('DELETE X %s Y WHERE X eid %%(x)s, NOT Y eid %%(y)s' % rtype,
-                            {'x': eidfrom, 'y': eidto}, 'x')
+            rset = session.unsafe_execute('Any X,Y WHERE X %s Y, X eid %%(x)s, '
+                                          'NOT Y eid %%(y)s' % rtype,
+                                          {'x': eidfrom, 'y': eidto}, 'x')
+            if rset:
+                safe_delete_relation(session, rschema, *rset[0])
     if card[1] in '1?':
-        session.execute('DELETE X %s Y WHERE NOT X eid %%(x)s, Y eid %%(y)s' % rtype,
-                        {'x': eidfrom, 'y': eidto}, 'y')
+        rset = session.unsafe_execute('Any X,Y WHERE X %s Y, Y eid %%(y)s, '
+                                      'NOT X eid %%(x)s' % rtype,
+                                      {'x': eidfrom, 'y': eidto}, 'y')
+        if rset:
+            safe_delete_relation(session, rschema, *rset[0])
+
+def safe_delete_relation(session, rschema, subject, object):
+    if not rschema.has_perm(session, 'delete', fromeid=subject, toeid=object):
+        raise Unauthorized()
+    session.repo.glob_delete_relation(session, subject, rschema.type, object)
 
 
 class Repository(object):
@@ -435,7 +447,8 @@
         public method, not requiring a session id.
         """
         versions = self.get_versions(not (self.config.creating
-                                          or self.config.repairing))
+                                          or self.config.repairing
+                                          or self.config.mode == 'test'))
         cubes = list(versions)
         cubes.remove('cubicweb')
         return cubes
--- a/server/sources/native.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/server/sources/native.py	Wed Feb 24 15:08:13 2010 +0100
@@ -429,7 +429,8 @@
             if rollback:
                 try:
                     session.pool.connection(self.uri).rollback()
-                    self.critical('transaction has been rollbacked')
+                    if self.repo.config.mode != 'test':
+                        self.critical('transaction has been rollbacked')
                 except:
                     pass
             raise
@@ -453,7 +454,8 @@
                               query, args, ex.args[0])
             try:
                 session.pool.connection(self.uri).rollback()
-                self.critical('transaction has been rollbacked')
+                if self.repo.config.mode != 'test':
+                    self.critical('transaction has been rollbacked')
             except:
                 pass
             raise
--- a/server/test/unittest_migractions.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/server/test/unittest_migractions.py	Wed Feb 24 15:08:13 2010 +0100
@@ -9,20 +9,11 @@
 from logilab.common.testlib import TestCase, unittest_main
 
 from cubicweb import ConfigurationError
-from cubicweb.devtools.testlib import CubicWebTC, get_versions
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.schema import CubicWebSchemaLoader
 from cubicweb.server.sqlutils import SQL_PREFIX
-from cubicweb.server.repository import Repository
 from cubicweb.server.migractions import *
 
-orig_get_versions = Repository.get_versions
-
-def setup_module(*args):
-    Repository.get_versions = get_versions
-
-def teardown_module(*args):
-    Repository.get_versions = orig_get_versions
-
 
 class MigrationCommandsTC(CubicWebTC):
 
@@ -43,7 +34,7 @@
     @classmethod
     def _refresh_repo(cls):
         super(MigrationCommandsTC, cls)._refresh_repo()
-        cls.repo.schema = cls.vreg.schema = deepcopy(cls.origschema)
+        cls.repo.set_schema(deepcopy(cls.origschema), resetvreg=False)
 
     def setUp(self):
         CubicWebTC.setUp(self)
--- a/server/test/unittest_msplanner.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/server/test/unittest_msplanner.py	Wed Feb 24 15:08:13 2010 +0100
@@ -5,7 +5,6 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-from logilab.common.decorators import clear_cache
 from cubicweb.devtools import init_test_database
 from cubicweb.devtools.repotest import BasePlannerTC, test_plan
 
@@ -78,12 +77,12 @@
         self.prevrqlexpr_affaire = affreadperms[-1]
         # add access to type attribute so S can't be invariant
         affreadperms[-1] = ERQLExpression('X concerne S?, S owned_by U, S type "X"')
-        self.schema['Affaire'].permissions['read'] = tuple(affreadperms)
+        self.schema['Affaire'].set_action_permissions('read', affreadperms)
         # hijack CWUser security
         userreadperms = list(self.schema['CWUser'].permissions['read'])
         self.prevrqlexpr_user = userreadperms[-1]
         userreadperms[-1] = ERQLExpression('X owned_by U')
-        self.schema['CWUser'].permissions['read'] = tuple(userreadperms)
+        self.schema['CWUser'].set_action_permissions('read', userreadperms)
         self.add_source(FakeUserROSource, 'ldap')
         self.add_source(FakeCardSource, 'cards')
 
@@ -96,9 +95,7 @@
     def restore_orig_affaire_security(self):
         affreadperms = list(self.schema['Affaire'].permissions['read'])
         affreadperms[-1] = self.prevrqlexpr_affaire
-        self.schema['Affaire'].permissions['read'] = tuple(affreadperms)
-        clear_cache(self.schema['Affaire'], 'get_rqlexprs')
-        #clear_cache(self.schema['Affaire'], 'get_groups')
+        self.schema['Affaire'].set_action_permissions('read', affreadperms)
 
     def restore_orig_cwuser_security(self):
         if hasattr(self, '_orig_cwuser_security_restored'):
@@ -106,9 +103,7 @@
         self._orig_cwuser_security_restored = True
         userreadperms = list(self.schema['CWUser'].permissions['read'])
         userreadperms[-1] = self.prevrqlexpr_user
-        self.schema['CWUser'].permissions['read'] = tuple(userreadperms)
-        clear_cache(self.schema['CWUser'], 'get_rqlexprs')
-        #clear_cache(self.schema['CWUser'], 'get_groups')
+        self.schema['CWUser'].set_action_permissions('read', userreadperms)
 
 
 class PartPlanInformationTC(BaseMSPlannerTC):
--- a/server/test/unittest_multisources.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/server/test/unittest_multisources.py	Wed Feb 24 15:08:13 2010 +0100
@@ -30,13 +30,25 @@
 repo2, cnx2 = init_test_database(config=ExternalSource1Configuration('data'))
 repo3, cnx3 = init_test_database(config=ExternalSource2Configuration('data'))
 
-# XXX, access existing connection, no pyro connection
+# hi-jacking
 from cubicweb.server.sources.pyrorql import PyroRQLSource
-PyroRQLSource.get_connection = lambda x: x.uri == 'extern-multi' and cnx3 or cnx2
-# necessary since the repository is closing its initial connections pool though
-# we want to keep cnx2 valid
 from cubicweb.dbapi import Connection
-Connection.close = lambda x: None
+
+PyroRQLSource_get_connection = PyroRQLSource.get_connection
+Connection_close = Connection.close
+
+def setup_module(*args):
+    # hi-jack PyroRQLSource.get_connection to access existing connection (no
+    # pyro connection)
+    PyroRQLSource.get_connection = lambda x: x.uri == 'extern-multi' and cnx3 or cnx2
+    # also necessary since the repository is closing its initial connections
+    # pool though we want to keep cnx2 valid
+    Connection.close = lambda x: None
+
+def teardown_module(*args):
+    PyroRQLSource.get_connection = PyroRQLSource_get_connection
+    Connection.close = Connection_close
+
 
 class TwoSourcesTC(CubicWebTC):
     config = TwoSourcesConfiguration('data')
--- a/server/test/unittest_security.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/server/test/unittest_security.py	Wed Feb 24 15:08:13 2010 +0100
@@ -494,12 +494,13 @@
         # needed to avoid check_perm error
         session.set_pool()
         # needed to remove rql expr granting update perm to the user
+        affaire_perms = self.schema['Affaire'].permissions.copy()
         self.schema['Affaire'].set_action_permissions('update', self.schema['Affaire'].get_groups('update'))
-        self.assertRaises(Unauthorized,
-                          self.schema['Affaire'].check_perm, session, 'update', eid=eid)
-        cu = cnx.cursor()
-        self.schema['Affaire'].set_action_permissions('read', ('users',))
         try:
+            self.assertRaises(Unauthorized,
+                              self.schema['Affaire'].check_perm, session, 'update', eid=eid)
+            cu = cnx.cursor()
+            self.schema['Affaire'].set_action_permissions('read', ('users',))
             aff = cu.execute('Any X WHERE X ref "ARCT01"').get_entity(0, 0)
             aff.fire_transition('abort')
             cnx.commit()
@@ -510,7 +511,9 @@
             # from the current state but Unauthorized if it exists but user can't pass it
             self.assertRaises(ValidationError, user.fire_transition, 'deactivate')
         finally:
-            self.schema['Affaire'].set_action_permissions('read', ('managers',))
+            # restore orig perms
+            for action, perms in affaire_perms.iteritems():
+                self.schema['Affaire'].set_action_permissions(action, perms)
 
     def test_trinfo_security(self):
         aff = self.execute('INSERT Affaire X: X ref "ARCT01"').get_entity(0, 0)
--- a/test/unittest_cwconfig.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/test/unittest_cwconfig.py	Wed Feb 24 15:08:13 2010 +0100
@@ -9,6 +9,7 @@
 import os
 from os.path import dirname, join, abspath
 
+from logilab.common.modutils import cleanup_sys_modules
 from logilab.common.testlib import TestCase, unittest_main
 from logilab.common.changelog import Version
 
@@ -26,6 +27,7 @@
 
 class CubicWebConfigurationTC(TestCase):
     def setUp(self):
+        cleanup_sys_modules([CUSTOM_CUBES_DIR, ApptestConfiguration.CUBES_DIR])
         self.config = ApptestConfiguration('data')
         self.config._cubes = ('email', 'file')
 
--- a/test/unittest_entity.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/test/unittest_entity.py	Wed Feb 24 15:08:13 2010 +0100
@@ -178,9 +178,11 @@
     def test_related_rql_base(self):
         Personne = self.vreg['etypes'].etype_class('Personne')
         Note = self.vreg['etypes'].etype_class('Note')
+        SubNote = self.vreg['etypes'].etype_class('SubNote')
         self.failUnless(issubclass(self.vreg['etypes'].etype_class('SubNote'), Note))
         Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', 'type'))
         Note.fetch_attrs, Note.fetch_order = fetch_config(('type',))
+        SubNote.fetch_attrs, SubNote.fetch_order = fetch_config(('type',))
         p = self.request().create_entity('Personne', nom=u'pouet')
         self.assertEquals(p.related_rql('evaluee'),
                           'Any X,AA,AB ORDERBY AA ASC WHERE E eid %(x)s, E evaluee X, '
--- a/test/unittest_utils.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/test/unittest_utils.py	Wed Feb 24 15:08:13 2010 +0100
@@ -6,14 +6,18 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
-from logilab.common.testlib import TestCase, unittest_main
-
-import simplejson
+import re
 import decimal
 import datetime
 
-from cubicweb.utils import make_uid, UStringIO, SizeConstrainedList, CubicWebJsonEncoder
+from logilab.common.testlib import TestCase, unittest_main
+from cubicweb.utils import make_uid, UStringIO, SizeConstrainedList
 
+try:
+    import simplejson
+    from cubicweb.utils import CubicWebJsonEncoder
+except ImportError:
+    simplejson = None
 
 class MakeUidTC(TestCase):
     def test_1(self):
@@ -26,6 +30,9 @@
             uid = make_uid('xyz')
             if uid in d:
                 self.fail(len(d))
+            if re.match('\d', uid):
+                self.fail('make_uid must not return something begining with '
+                          'some numeric character, got %s' % uid)
             d.add(uid)
 
 
@@ -53,6 +60,9 @@
             yield self.assertEquals, l, expected
 
 class JSONEncoerTests(TestCase):
+    def setUp(self):
+        if simplejson is None:
+            self.skip('simplejson not available')
 
     def encode(self, value):
         return simplejson.dumps(value, cls=CubicWebJsonEncoder)
--- a/utils.py	Wed Feb 24 12:49:55 2010 +0100
+++ b/utils.py	Wed Feb 24 15:08:13 2010 +0100
@@ -26,9 +26,11 @@
 
     def make_uid(key):
         """forge a unique identifier
-        not that unique on win32"""
-        msg = str(key) + "%.10f" % time() + str(randint(0, 1000000))
-        return md5(msg).hexdigest()
+        XXX not that unique on win32
+        """
+        key = str(key)
+        msg = key + "%.10f" % time() + str(randint(0, 1000000))
+        return key + md5(msg).hexdigest()
 
 else: