From 54c2e57dc78736b2d60cd7c66abc7ba2efc0e0a2 Mon Sep 17 00:00:00 2001 From: richard Date: Thu, 11 Jul 2002 01:11:03 +0000 Subject: [PATCH] Added metakit backend to the db tests and fixed the more easily fixable test failures. git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@866 57a73879-2fb5-44c3-a270-3262357dd7e2 --- roundup/backends/__init__.py | 20 ++++- roundup/backends/back_metakit.py | 32 +++++--- test/test_db.py | 122 ++++++++++++++++++++++++++----- 3 files changed, 144 insertions(+), 30 deletions(-) diff --git a/roundup/backends/__init__.py b/roundup/backends/__init__.py index ee73759..1e0348b 100644 --- a/roundup/backends/__init__.py +++ b/roundup/backends/__init__.py @@ -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 # diff --git a/roundup/backends/back_metakit.py b/roundup/backends/back_metakit.py index 3c29f4f..4902851 100755 --- a/roundup/backends/back_metakit.py +++ b/roundup/backends/back_metakit.py @@ -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) diff --git a/test/test_db.py b/test/test_db.py index e8a51b0..3b9c9ba 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -15,17 +15,17 @@ # 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 -- 2.30.2