Code

Added metakit backend to the db tests and fixed the more easily fixable test
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Thu, 11 Jul 2002 01:11:03 +0000 (01:11 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Thu, 11 Jul 2002 01:11:03 +0000 (01:11 +0000)
failures.

git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@866 57a73879-2fb5-44c3-a270-3262357dd7e2

roundup/backends/__init__.py
roundup/backends/back_metakit.py
test/test_db.py

index ee73759d26ceb41a003f90111d56fa3b65c2c4c9..1e0348b819096543a0b553b849948b41fbed00f3 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: __init__.py,v 1.12 2002-05-22 00:32:33 richard Exp $
+# $Id: __init__.py,v 1.13 2002-07-11 01:11:03 richard Exp $
 
 __all__ = []
 
@@ -54,8 +54,26 @@ else:
     bsddb3 = back_bsddb3
     __all__.append('bsddb3')
 
+try:
+    import metakit
+except ImportError, message:
+    if str(message) != 'No module named metakit': raise
+else:
+    import back_metakit
+    metakit = back_metakit
+    __all__.append('metakit')
+
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.12  2002/05/22 00:32:33  richard
+#  . changed the default message list in issues to display the message body
+#  . made backends.__init__ be more specific about which ImportErrors it really
+#    wants to ignore
+#  . fixed the example addresses in the templates to use correct example domains
+#  . cleaned out the template stylesheets, removing a bunch of junk that really
+#    wasn't necessary (font specs, styles never used) and added a style for
+#    message content
+#
 # Revision 1.11  2002/02/16 08:39:42  richard
 #  . #516854 ] "My Issues" and redisplay
 #
index 3c29f4f5d42aa151644d7f0719091b00d4aaf247..49028519361c754d4902b792cb4029c9554b6164 100755 (executable)
@@ -10,8 +10,10 @@ def Database(config, journaltag=None):
         db = _instances[id(config)]
         old = db.journaltag
         db.journaltag = journaltag
-        if hasattr(db, 'curuserid'):
+        try:
             delattr(db, 'curuserid')
+        except AttributeError:
+            pass
         return db
     else:
         db = _Database(config, journaltag)
@@ -260,7 +262,10 @@ class Class:    # no, I'm not going to subclass the existing!
             if ndx < 0:
                 raise IndexError, "%s has no node %s" % (self.classname, nodeid)
             self.idcache[id] = ndx
-        raw = getattr(view[ndx], propname)
+        try:
+            raw = getattr(view[ndx], propname)
+        except AttributeError:
+            raise KeyError, propname
         rutyp = self.ruprops.get(propname, None)
         if rutyp is None:
             rutyp = self.privateprops[propname]
@@ -270,7 +275,6 @@ class Class:    # no, I'm not going to subclass the existing!
         return raw
         
     def set(self, nodeid, **propvalues):
-        
         isnew = 0
         if propvalues.has_key('#ISNEW'):
             isnew = 1
@@ -489,7 +493,7 @@ class Class:    # no, I'm not going to subclass the existing!
         iv = self.db._db.getas('_%s[k:S,i:I]' % self.classname)
         iv = iv.ordered(1)
         #XXX
-        print "setkey building index"
+#        print "setkey building index"
         for row in self.getview():
             iv.append(k=getattr(row, propname), i=row.id)
     def getkey(self):
@@ -512,19 +516,24 @@ class Class:    # no, I'm not going to subclass the existing!
         """Get the ids of nodes in this class which link to the given nodes.
 
         'propspec' consists of keyword args propname={nodeid:1,}   
-          'propname' must be the name of a property in this class, or a
-            KeyError is raised.  That property must be a Link or Multilink
-            property, or a TypeError is raised.
+        'propname' must be the name of a property in this class, or a
+                   KeyError is raised.  That property must be a Link or
+                   Multilink property, or a TypeError is raised.
+
         Any node in this class whose propname property links to any of the
         nodeids will be returned. Used by the full text indexing, which knows
-        that "foo" occurs in msg1, msg3 and file7; so we have hits on these issues:
+        that "foo" occurs in msg1, msg3 and file7; so we have hits on these
+        issues:
+
             db.issue.find(messages={'1':1,'3':1}, files={'7':1})
+
         """
         propspec = propspec.items()
         for propname, nodeid in propspec:
             # check the prop is OK
             prop = self.ruprops[propname]
-            if not isinstance(prop, hyperdb.Link) and not isinstance(prop, hyperdb.Multilink):
+            if (not isinstance(prop, hyperdb.Link) and
+                    not isinstance(prop, hyperdb.Multilink)):
                 raise TypeError, "'%s' not a Link/Multilink property"%propname
 
         vws = []
@@ -542,6 +551,11 @@ class Class:    # no, I'm not going to subclass the existing!
                     return ids.has_key(str(getattr(row, nm)))
             ndxview = view.filter(ff)
             vws.append(ndxview.unique())
+
+        # handle the empty match case
+        if not vws:
+            return []
+
         ndxview = vws[0]
         for v in vws[1:]:
             ndxview = ndxview.union(v)
index e8a51b0b93f2a74050465a5b3bdb3899f49071c1..3b9c9baa68a169fd021fc2cd2382bda1e7932333 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: test_db.py,v 1.25 2002-07-09 04:19:09 richard Exp $ 
+# $Id: test_db.py,v 1.26 2002-07-11 01:11:03 richard Exp $ 
 
 import unittest, os, shutil
 
 from roundup.hyperdb import String, Password, Link, Multilink, Date, \
-    Interval, Class, DatabaseError
+    Interval, DatabaseError, Class
 from roundup.roundupdb import FileClass
 from roundup import date, password
 from roundup.indexer import Indexer
 
-def setupSchema(db, create):
+def setupSchema(db, create, Class, FileClass):
     status = Class(db, "status", name=String())
     status.setkey("name")
     user = Class(db, "user", username=String(), password=Password())
@@ -69,9 +69,9 @@ class anydbmDBTestCase(MyTestCase):
             shutil.rmtree(config.DATABASE)
         os.makedirs(config.DATABASE + '/files')
         self.db = anydbm.Database(config, 'test')
-        setupSchema(self.db, 1)
+        setupSchema(self.db, 1, Class, FileClass)
         self.db2 = anydbm.Database(config, 'test')
-        setupSchema(self.db2, 0)
+        setupSchema(self.db2, 0, Class, FileClass)
 
     def testStringChange(self):
         self.db.issue.create(title="spam", status='1')
@@ -111,6 +111,7 @@ class anydbmDBTestCase(MyTestCase):
         self.assertNotEqual(self.db.issue.get('1', "foo"), a)
 
     def testNewProperty(self):
+        ' make sure a new property is added ok '
         self.db.issue.create(title="spam", status='1')
         self.db.issue.addprop(fixer=Link("user"))
         props = self.db.issue.getprops()
@@ -203,7 +204,7 @@ class anydbmDBTestCase(MyTestCase):
         # class get
         #
         # invalid node id
-        ar(IndexError, self.db.status.get, '10', 'name')
+        ar(IndexError, self.db.issue.get, '1', 'title')
         # invalid property name
         ar(KeyError, self.db.status.get, '2', 'foo')
 
@@ -211,7 +212,7 @@ class anydbmDBTestCase(MyTestCase):
         # class set
         #
         # invalid node id
-        ar(IndexError, self.db.issue.set, '1', name='foo')
+        ar(IndexError, self.db.issue.set, '1', title='foo')
         # invalid property name
         ar(KeyError, self.db.status.set, '1', foo='foo')
         # string property
@@ -346,13 +347,14 @@ class anydbmReadOnlyDBTestCase(MyTestCase):
             shutil.rmtree(config.DATABASE)
         os.makedirs(config.DATABASE + '/files')
         db = anydbm.Database(config, 'test')
-        setupSchema(db, 1)
+        setupSchema(db, 1, Class, FileClass)
         self.db = anydbm.Database(config)
-        setupSchema(self.db, 0)
+        setupSchema(self.db, 0, Class, FileClass)
         self.db2 = anydbm.Database(config, 'test')
-        setupSchema(self.db2, 0)
+        setupSchema(self.db2, 0, Class, FileClass)
 
     def testExceptions(self):
+        ' make sure exceptions are raised on writes to a read-only db '
         # this tests the exceptions that should be raised
         ar = self.assertRaises
 
@@ -370,9 +372,9 @@ class bsddbDBTestCase(anydbmDBTestCase):
             shutil.rmtree(config.DATABASE)
         os.makedirs(config.DATABASE + '/files')
         self.db = bsddb.Database(config, 'test')
-        setupSchema(self.db, 1)
+        setupSchema(self.db, 1, Class, FileClass)
         self.db2 = bsddb.Database(config, 'test')
-        setupSchema(self.db2, 0)
+        setupSchema(self.db2, 0, Class, FileClass)
 
 class bsddbReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
     def setUp(self):
@@ -382,11 +384,11 @@ class bsddbReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
             shutil.rmtree(config.DATABASE)
         os.makedirs(config.DATABASE + '/files')
         db = bsddb.Database(config, 'test')
-        setupSchema(db, 1)
+        setupSchema(db, 1, Class, FileClass)
         self.db = bsddb.Database(config)
-        setupSchema(self.db, 0)
+        setupSchema(self.db, 0, Class, FileClass)
         self.db2 = bsddb.Database(config, 'test')
-        setupSchema(self.db2, 0)
+        setupSchema(self.db2, 0, Class, FileClass)
 
 
 class bsddb3DBTestCase(anydbmDBTestCase):
@@ -397,9 +399,9 @@ class bsddb3DBTestCase(anydbmDBTestCase):
             shutil.rmtree(config.DATABASE)
         os.makedirs(config.DATABASE + '/files')
         self.db = bsddb3.Database(config, 'test')
-        setupSchema(self.db, 1)
+        setupSchema(self.db, 1, Class, FileClass)
         self.db2 = bsddb3.Database(config, 'test')
-        setupSchema(self.db2, 0)
+        setupSchema(self.db2, 0, Class, FileClass)
 
 class bsddb3ReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
     def setUp(self):
@@ -409,13 +411,81 @@ class bsddb3ReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
             shutil.rmtree(config.DATABASE)
         os.makedirs(config.DATABASE + '/files')
         db = bsddb3.Database(config, 'test')
-        setupSchema(db, 1)
+        setupSchema(db, 1, Class, FileClass)
         self.db = bsddb3.Database(config)
-        setupSchema(self.db, 0)
+        setupSchema(self.db, 0, Class, FileClass)
         self.db2 = bsddb3.Database(config, 'test')
-        setupSchema(self.db2, 0)
+        setupSchema(self.db2, 0, Class, FileClass)
 
 
+class metakitDBTestCase(anydbmDBTestCase):
+    def setUp(self):
+        from roundup.backends import metakit
+        import weakref
+        metakit._instances = weakref.WeakValueDictionary()
+        # remove previous test, ignore errors
+        if os.path.exists(config.DATABASE):
+            shutil.rmtree(config.DATABASE)
+        os.makedirs(config.DATABASE + '/files')
+        self.db = metakit.Database(config, 'test')
+        setupSchema(self.db, 1, metakit.Class, metakit.FileClass)
+        self.db2 = metakit.Database(config, 'test')
+        setupSchema(self.db2, 0, metakit.Class, metakit.FileClass)
+
+    def testTransactions(self):
+        # remember the number of items we started
+        num_issues = len(self.db.issue.list())
+        self.db.issue.create(title="don't commit me!", status='1')
+        self.assertNotEqual(num_issues, len(self.db.issue.list()))
+        self.db.rollback()
+        self.assertEqual(num_issues, len(self.db.issue.list()))
+        self.db.issue.create(title="please commit me!", status='1')
+        self.assertNotEqual(num_issues, len(self.db.issue.list()))
+        self.db.commit()
+        self.assertNotEqual(num_issues, len(self.db.issue.list()))
+        self.db.rollback()
+        self.assertNotEqual(num_issues, len(self.db.issue.list()))
+        self.db.file.create(name="test", type="text/plain", content="hi")
+        self.db.rollback()
+        for i in range(10):
+            self.db.file.create(name="test", type="text/plain", 
+                    content="hi %d"%(i))
+            self.db.commit()
+        # TODO: would be good to be able to ensure the file is not on disk after
+        # a rollback...
+        self.assertNotEqual(num_files, num_files2)
+        self.db.file.create(name="test", type="text/plain", content="hi")
+        self.db.rollback()
+
+    def testNewProperty(self):
+        ' make sure a new property is added ok '
+        self.db.issue.create(title="spam", status='1')
+        self.db.issue.addprop(fixer=Link("user"))
+        props = self.db.issue.getprops()
+        keys = props.keys()
+        keys.sort()
+        # metakit _base_ Class has the activity, creation and creator too
+        self.assertEqual(keys, ['activity', 'creation', 'creator',
+            'deadline', 'files', 'fixer', 'foo', 'id', 'nosy', 'status',
+            'title'])
+        self.assertEqual(self.db.issue.get('1', "fixer"), None)
+
+class metakitReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
+    def setUp(self):
+        from roundup.backends import metakit
+        import weakref
+        metakit._instances = weakref.WeakValueDictionary()
+        # remove previous test, ignore errors
+        if os.path.exists(config.DATABASE):
+            shutil.rmtree(config.DATABASE)
+        os.makedirs(config.DATABASE + '/files')
+        db = metakit.Database(config, 'test')
+        setupSchema(db, 1, metakit.Class, metakit.FileClass)
+        self.db = metakit.Database(config)
+        setupSchema(self.db, 0, metakit.Class, metakit.FileClass)
+        self.db2 = metakit.Database(config, 'test')
+        setupSchema(self.db2, 0, metakit.Class, metakit.FileClass)
+
 def suite():
     l = [
          unittest.makeSuite(anydbmDBTestCase, 'test'),
@@ -436,10 +506,22 @@ def suite():
     except:
         print 'bsddb3 module not found, skipping bsddb3 DBTestCase'
 
+    try:
+        import metakit
+        l.append(unittest.makeSuite(metakitDBTestCase, 'test'))
+        l.append(unittest.makeSuite(metakitReadOnlyDBTestCase, 'test'))
+    except:
+        print 'metakit module not found, skipping metakit DBTestCase'
+
     return unittest.TestSuite(l)
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.25  2002/07/09 04:19:09  richard
+# Added reindex command to roundup-admin.
+# Fixed reindex on first access.
+# Also fixed reindexing of entries that change.
+#
 # Revision 1.24  2002/07/09 03:02:53  richard
 # More indexer work:
 # - all String properties may now be indexed too. Currently there's a bit of