Skip to content

Commit 596b482

Browse files
author
Richard Jones
committed
Added metakit backend to the db tests...
...and fixed the more easily fixable test failures.
1 parent 181ee96 commit 596b482

File tree

3 files changed

+144
-30
lines changed

3 files changed

+144
-30
lines changed

roundup/backends/__init__.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: __init__.py,v 1.12 2002-05-22 00:32:33 richard Exp $
18+
# $Id: __init__.py,v 1.13 2002-07-11 01:11:03 richard Exp $
1919

2020
__all__ = []
2121

@@ -54,8 +54,26 @@
5454
bsddb3 = back_bsddb3
5555
__all__.append('bsddb3')
5656

57+
try:
58+
import metakit
59+
except ImportError, message:
60+
if str(message) != 'No module named metakit': raise
61+
else:
62+
import back_metakit
63+
metakit = back_metakit
64+
__all__.append('metakit')
65+
5766
#
5867
# $Log: not supported by cvs2svn $
68+
# Revision 1.12 2002/05/22 00:32:33 richard
69+
# . changed the default message list in issues to display the message body
70+
# . made backends.__init__ be more specific about which ImportErrors it really
71+
# wants to ignore
72+
# . fixed the example addresses in the templates to use correct example domains
73+
# . cleaned out the template stylesheets, removing a bunch of junk that really
74+
# wasn't necessary (font specs, styles never used) and added a style for
75+
# message content
76+
#
5977
# Revision 1.11 2002/02/16 08:39:42 richard
6078
# . #516854 ] "My Issues" and redisplay
6179
#

roundup/backends/back_metakit.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ def Database(config, journaltag=None):
1010
db = _instances[id(config)]
1111
old = db.journaltag
1212
db.journaltag = journaltag
13-
if hasattr(db, 'curuserid'):
13+
try:
1414
delattr(db, 'curuserid')
15+
except AttributeError:
16+
pass
1517
return db
1618
else:
1719
db = _Database(config, journaltag)
@@ -260,7 +262,10 @@ def get(self, nodeid, propname, default=_marker, cache=1):
260262
if ndx < 0:
261263
raise IndexError, "%s has no node %s" % (self.classname, nodeid)
262264
self.idcache[id] = ndx
263-
raw = getattr(view[ndx], propname)
265+
try:
266+
raw = getattr(view[ndx], propname)
267+
except AttributeError:
268+
raise KeyError, propname
264269
rutyp = self.ruprops.get(propname, None)
265270
if rutyp is None:
266271
rutyp = self.privateprops[propname]
@@ -270,7 +275,6 @@ def get(self, nodeid, propname, default=_marker, cache=1):
270275
return raw
271276

272277
def set(self, nodeid, **propvalues):
273-
274278
isnew = 0
275279
if propvalues.has_key('#ISNEW'):
276280
isnew = 1
@@ -489,7 +493,7 @@ def setkey(self, propname):
489493
iv = self.db._db.getas('_%s[k:S,i:I]' % self.classname)
490494
iv = iv.ordered(1)
491495
#XXX
492-
print "setkey building index"
496+
# print "setkey building index"
493497
for row in self.getview():
494498
iv.append(k=getattr(row, propname), i=row.id)
495499
def getkey(self):
@@ -512,19 +516,24 @@ def find(self, **propspec):
512516
"""Get the ids of nodes in this class which link to the given nodes.
513517
514518
'propspec' consists of keyword args propname={nodeid:1,}
515-
'propname' must be the name of a property in this class, or a
516-
KeyError is raised. That property must be a Link or Multilink
517-
property, or a TypeError is raised.
519+
'propname' must be the name of a property in this class, or a
520+
KeyError is raised. That property must be a Link or
521+
Multilink property, or a TypeError is raised.
522+
518523
Any node in this class whose propname property links to any of the
519524
nodeids will be returned. Used by the full text indexing, which knows
520-
that "foo" occurs in msg1, msg3 and file7; so we have hits on these issues:
525+
that "foo" occurs in msg1, msg3 and file7; so we have hits on these
526+
issues:
527+
521528
db.issue.find(messages={'1':1,'3':1}, files={'7':1})
529+
522530
"""
523531
propspec = propspec.items()
524532
for propname, nodeid in propspec:
525533
# check the prop is OK
526534
prop = self.ruprops[propname]
527-
if not isinstance(prop, hyperdb.Link) and not isinstance(prop, hyperdb.Multilink):
535+
if (not isinstance(prop, hyperdb.Link) and
536+
not isinstance(prop, hyperdb.Multilink)):
528537
raise TypeError, "'%s' not a Link/Multilink property"%propname
529538

530539
vws = []
@@ -542,6 +551,11 @@ def ff(row, nm=propname, ids=ids):
542551
return ids.has_key(str(getattr(row, nm)))
543552
ndxview = view.filter(ff)
544553
vws.append(ndxview.unique())
554+
555+
# handle the empty match case
556+
if not vws:
557+
return []
558+
545559
ndxview = vws[0]
546560
for v in vws[1:]:
547561
ndxview = ndxview.union(v)

test/test_db.py

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: test_db.py,v 1.25 2002-07-09 04:19:09 richard Exp $
18+
# $Id: test_db.py,v 1.26 2002-07-11 01:11:03 richard Exp $
1919

2020
import unittest, os, shutil
2121

2222
from roundup.hyperdb import String, Password, Link, Multilink, Date, \
23-
Interval, Class, DatabaseError
23+
Interval, DatabaseError, Class
2424
from roundup.roundupdb import FileClass
2525
from roundup import date, password
2626
from roundup.indexer import Indexer
2727

28-
def setupSchema(db, create):
28+
def setupSchema(db, create, Class, FileClass):
2929
status = Class(db, "status", name=String())
3030
status.setkey("name")
3131
user = Class(db, "user", username=String(), password=Password())
@@ -69,9 +69,9 @@ def setUp(self):
6969
shutil.rmtree(config.DATABASE)
7070
os.makedirs(config.DATABASE + '/files')
7171
self.db = anydbm.Database(config, 'test')
72-
setupSchema(self.db, 1)
72+
setupSchema(self.db, 1, Class, FileClass)
7373
self.db2 = anydbm.Database(config, 'test')
74-
setupSchema(self.db2, 0)
74+
setupSchema(self.db2, 0, Class, FileClass)
7575

7676
def testStringChange(self):
7777
self.db.issue.create(title="spam", status='1')
@@ -111,6 +111,7 @@ def testIntervalChange(self):
111111
self.assertNotEqual(self.db.issue.get('1', "foo"), a)
112112

113113
def testNewProperty(self):
114+
' make sure a new property is added ok '
114115
self.db.issue.create(title="spam", status='1')
115116
self.db.issue.addprop(fixer=Link("user"))
116117
props = self.db.issue.getprops()
@@ -203,15 +204,15 @@ def testExceptions(self):
203204
# class get
204205
#
205206
# invalid node id
206-
ar(IndexError, self.db.status.get, '10', 'name')
207+
ar(IndexError, self.db.issue.get, '1', 'title')
207208
# invalid property name
208209
ar(KeyError, self.db.status.get, '2', 'foo')
209210

210211
#
211212
# class set
212213
#
213214
# invalid node id
214-
ar(IndexError, self.db.issue.set, '1', name='foo')
215+
ar(IndexError, self.db.issue.set, '1', title='foo')
215216
# invalid property name
216217
ar(KeyError, self.db.status.set, '1', foo='foo')
217218
# string property
@@ -346,13 +347,14 @@ def setUp(self):
346347
shutil.rmtree(config.DATABASE)
347348
os.makedirs(config.DATABASE + '/files')
348349
db = anydbm.Database(config, 'test')
349-
setupSchema(db, 1)
350+
setupSchema(db, 1, Class, FileClass)
350351
self.db = anydbm.Database(config)
351-
setupSchema(self.db, 0)
352+
setupSchema(self.db, 0, Class, FileClass)
352353
self.db2 = anydbm.Database(config, 'test')
353-
setupSchema(self.db2, 0)
354+
setupSchema(self.db2, 0, Class, FileClass)
354355

355356
def testExceptions(self):
357+
' make sure exceptions are raised on writes to a read-only db '
356358
# this tests the exceptions that should be raised
357359
ar = self.assertRaises
358360

@@ -370,9 +372,9 @@ def setUp(self):
370372
shutil.rmtree(config.DATABASE)
371373
os.makedirs(config.DATABASE + '/files')
372374
self.db = bsddb.Database(config, 'test')
373-
setupSchema(self.db, 1)
375+
setupSchema(self.db, 1, Class, FileClass)
374376
self.db2 = bsddb.Database(config, 'test')
375-
setupSchema(self.db2, 0)
377+
setupSchema(self.db2, 0, Class, FileClass)
376378

377379
class bsddbReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
378380
def setUp(self):
@@ -382,11 +384,11 @@ def setUp(self):
382384
shutil.rmtree(config.DATABASE)
383385
os.makedirs(config.DATABASE + '/files')
384386
db = bsddb.Database(config, 'test')
385-
setupSchema(db, 1)
387+
setupSchema(db, 1, Class, FileClass)
386388
self.db = bsddb.Database(config)
387-
setupSchema(self.db, 0)
389+
setupSchema(self.db, 0, Class, FileClass)
388390
self.db2 = bsddb.Database(config, 'test')
389-
setupSchema(self.db2, 0)
391+
setupSchema(self.db2, 0, Class, FileClass)
390392

391393

392394
class bsddb3DBTestCase(anydbmDBTestCase):
@@ -397,9 +399,9 @@ def setUp(self):
397399
shutil.rmtree(config.DATABASE)
398400
os.makedirs(config.DATABASE + '/files')
399401
self.db = bsddb3.Database(config, 'test')
400-
setupSchema(self.db, 1)
402+
setupSchema(self.db, 1, Class, FileClass)
401403
self.db2 = bsddb3.Database(config, 'test')
402-
setupSchema(self.db2, 0)
404+
setupSchema(self.db2, 0, Class, FileClass)
403405

404406
class bsddb3ReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
405407
def setUp(self):
@@ -409,13 +411,81 @@ def setUp(self):
409411
shutil.rmtree(config.DATABASE)
410412
os.makedirs(config.DATABASE + '/files')
411413
db = bsddb3.Database(config, 'test')
412-
setupSchema(db, 1)
414+
setupSchema(db, 1, Class, FileClass)
413415
self.db = bsddb3.Database(config)
414-
setupSchema(self.db, 0)
416+
setupSchema(self.db, 0, Class, FileClass)
415417
self.db2 = bsddb3.Database(config, 'test')
416-
setupSchema(self.db2, 0)
418+
setupSchema(self.db2, 0, Class, FileClass)
417419

418420

421+
class metakitDBTestCase(anydbmDBTestCase):
422+
def setUp(self):
423+
from roundup.backends import metakit
424+
import weakref
425+
metakit._instances = weakref.WeakValueDictionary()
426+
# remove previous test, ignore errors
427+
if os.path.exists(config.DATABASE):
428+
shutil.rmtree(config.DATABASE)
429+
os.makedirs(config.DATABASE + '/files')
430+
self.db = metakit.Database(config, 'test')
431+
setupSchema(self.db, 1, metakit.Class, metakit.FileClass)
432+
self.db2 = metakit.Database(config, 'test')
433+
setupSchema(self.db2, 0, metakit.Class, metakit.FileClass)
434+
435+
def testTransactions(self):
436+
# remember the number of items we started
437+
num_issues = len(self.db.issue.list())
438+
self.db.issue.create(title="don't commit me!", status='1')
439+
self.assertNotEqual(num_issues, len(self.db.issue.list()))
440+
self.db.rollback()
441+
self.assertEqual(num_issues, len(self.db.issue.list()))
442+
self.db.issue.create(title="please commit me!", status='1')
443+
self.assertNotEqual(num_issues, len(self.db.issue.list()))
444+
self.db.commit()
445+
self.assertNotEqual(num_issues, len(self.db.issue.list()))
446+
self.db.rollback()
447+
self.assertNotEqual(num_issues, len(self.db.issue.list()))
448+
self.db.file.create(name="test", type="text/plain", content="hi")
449+
self.db.rollback()
450+
for i in range(10):
451+
self.db.file.create(name="test", type="text/plain",
452+
content="hi %d"%(i))
453+
self.db.commit()
454+
# TODO: would be good to be able to ensure the file is not on disk after
455+
# a rollback...
456+
self.assertNotEqual(num_files, num_files2)
457+
self.db.file.create(name="test", type="text/plain", content="hi")
458+
self.db.rollback()
459+
460+
def testNewProperty(self):
461+
' make sure a new property is added ok '
462+
self.db.issue.create(title="spam", status='1')
463+
self.db.issue.addprop(fixer=Link("user"))
464+
props = self.db.issue.getprops()
465+
keys = props.keys()
466+
keys.sort()
467+
# metakit _base_ Class has the activity, creation and creator too
468+
self.assertEqual(keys, ['activity', 'creation', 'creator',
469+
'deadline', 'files', 'fixer', 'foo', 'id', 'nosy', 'status',
470+
'title'])
471+
self.assertEqual(self.db.issue.get('1', "fixer"), None)
472+
473+
class metakitReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
474+
def setUp(self):
475+
from roundup.backends import metakit
476+
import weakref
477+
metakit._instances = weakref.WeakValueDictionary()
478+
# remove previous test, ignore errors
479+
if os.path.exists(config.DATABASE):
480+
shutil.rmtree(config.DATABASE)
481+
os.makedirs(config.DATABASE + '/files')
482+
db = metakit.Database(config, 'test')
483+
setupSchema(db, 1, metakit.Class, metakit.FileClass)
484+
self.db = metakit.Database(config)
485+
setupSchema(self.db, 0, metakit.Class, metakit.FileClass)
486+
self.db2 = metakit.Database(config, 'test')
487+
setupSchema(self.db2, 0, metakit.Class, metakit.FileClass)
488+
419489
def suite():
420490
l = [
421491
unittest.makeSuite(anydbmDBTestCase, 'test'),
@@ -436,10 +506,22 @@ def suite():
436506
except:
437507
print 'bsddb3 module not found, skipping bsddb3 DBTestCase'
438508

509+
try:
510+
import metakit
511+
l.append(unittest.makeSuite(metakitDBTestCase, 'test'))
512+
l.append(unittest.makeSuite(metakitReadOnlyDBTestCase, 'test'))
513+
except:
514+
print 'metakit module not found, skipping metakit DBTestCase'
515+
439516
return unittest.TestSuite(l)
440517

441518
#
442519
# $Log: not supported by cvs2svn $
520+
# Revision 1.25 2002/07/09 04:19:09 richard
521+
# Added reindex command to roundup-admin.
522+
# Fixed reindex on first access.
523+
# Also fixed reindexing of entries that change.
524+
#
443525
# Revision 1.24 2002/07/09 03:02:53 richard
444526
# More indexer work:
445527
# - all String properties may now be indexed too. Currently there's a bit of

0 commit comments

Comments
 (0)