[Checkins] SVN: Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/ Implement ISortIndex; test coverage.
Tres Seaver
tseaver at palladion.com
Wed Mar 17 07:01:56 EDT 2010
Log message for revision 110014:
Implement ISortIndex; test coverage.
Changed:
U Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/index.py
U Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/tests/test_index.py
-=-
Modified: Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/index.py
===================================================================
--- Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/index.py 2010-03-17 08:45:23 UTC (rev 110013)
+++ Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/index.py 2010-03-17 11:01:56 UTC (rev 110014)
@@ -29,6 +29,7 @@
from App.class_init import InitializeClass
from OFS.SimpleItem import SimpleItem
from Products.PluginIndexes.interfaces import IUniqueValueIndex
+from Products.PluginIndexes.interfaces import ISortIndex
from Products.ZCatalog.Lazy import LazyMap
from zLOG import LOG
from zLOG import WARNING
@@ -49,7 +50,7 @@
class RecentItemsIndex(SimpleItem):
""" Recent items index.
"""
- implements(IUniqueValueIndex)
+ implements(IUniqueValueIndex, ISortIndex)
meta_type = 'Recent Items Index'
@@ -103,8 +104,11 @@
self.id = id
self.field_name = field_name or getattr(extra, 'field_name', None)
self.date_name = date_name or extra.date_name
- self.max_length = max_length or extra.max_length
- assert self.max_length > 0, 'Max item length value must be 1 or greater'
+ if max_length is None:
+ max_length = extra.max_length
+ if max_length < 1:
+ raise ValueError('max_length value must be 1 or greater')
+ self.max_length = max_length
if guard_roles is None:
guard_roles = getattr(extra, 'guard_roles', None)
if guard_permission is None:
@@ -256,6 +260,17 @@
else:
return []
+ # ISortIndex implementation
+ def keyForDocument(self, documentId):
+ """ See ISortIndex.
+ """
+ return self._rid2value[documentId]
+
+ def documentToKeyMap(self):
+ """ See ISortIndex.
+ """
+ return self._rid2value
+
## Index specific methods ##
def getItemCounts(self):
""" Return a mapping of field values => item counts.
Modified: Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/tests/test_index.py
===================================================================
--- Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/tests/test_index.py 2010-03-17 08:45:23 UTC (rev 110013)
+++ Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/tests/test_index.py 2010-03-17 11:01:56 UTC (rev 110014)
@@ -89,6 +89,43 @@
aq_parent(index).docs = docs
return docs
+ def _get_top_docs(self, docs):
+ top = {'huey':[], 'dooey':[], 'looey':[]}
+ for doc in docs.values():
+ top[doc.type].append((doc.date.timeTime(), doc.docid))
+ for typ, docs in top.items():
+ docs.sort()
+ top[typ] = docs[-10:]
+ return top
+
+ def _getExpectedTopDocs(self, index, docs, limit=None):
+ top = self._get_top_docs(docs)
+ query = ['huey', 'dooey']
+ if limit is None:
+ result = index.query(query)
+ else:
+ result = index.query(query, limit=limit)
+ expected = top['huey'] + top['dooey']
+ expected.sort()
+ expected = [docid for nil, docid in expected]
+ expected.reverse()
+ return result, expected
+
+ def _get_indexed_doc(self, index, fromtop=0):
+ docs = self._makeAndIndexDocs(index)
+ top = self._get_top_docs(docs)
+ items = docs.items()
+ if fromtop:
+ items.reverse()
+ for docid, doc in items:
+ entry = index.getEntryForObject(docid)
+ if entry is not None:
+ break
+ else:
+ self.fail('No objects in index')
+ self.assertEqual(entry, {'value':doc.type, 'date':doc.date.timeTime()})
+ return doc
+
def test_class_conforms_to_IPluggableIndex(self):
from zope.interface.verify import verifyClass
from Products.PluginIndexes.interfaces import IPluggableIndex
@@ -111,15 +148,56 @@
index = self._makeOne()
verifyObject(IUniqueValueIndex, index)
- def test_construct_with_extra(self):
+ def test_class_conforms_to_ISortIndex(self):
+ from zope.interface.verify import verifyClass
+ from Products.PluginIndexes.interfaces import ISortIndex
+ verifyClass(ISortIndex, self._getTargetClass())
+
+ def test_instance_conforms_to_ISortIndex(self):
+ from zope.interface.verify import verifyObject
+ from Products.PluginIndexes.interfaces import ISortIndex
+ index = self._makeOne()
+ verifyObject(ISortIndex, index)
+
+ def test_ctor_defaults(self):
+ from BTrees.Length import Length
+ klass = self._getTargetClass()
+ index = klass('ID', date_name='date_name', max_length=15)
+ self.assertEqual(index.getId(), 'ID')
+ self.assertEqual(index.field_name, None)
+ self.assertEqual(index.date_name, 'date_name')
+ self.assertEqual(index.max_length, 15)
+ self.assertEqual(index.guard_roles, None)
+ self.assertEqual(index.guard_permission, None)
+ self.failUnless(isinstance(index.numObjects, Length))
+ self.assertEqual(index.numObjects(), 0)
+
+ def test_ctor_normal(self):
+ klass = self._getTargetClass()
+ index = klass(id='ID',
+ field_name='field_name',
+ date_name='date_name',
+ max_length=15,
+ guard_roles=['guard_role1', 'guard_role2'],
+ guard_permission='guard_permission',
+ )
+ self.assertEqual(index.getId(), 'ID')
+ self.assertEqual(index.field_name, 'field_name')
+ self.assertEqual(index.date_name, 'date_name')
+ self.assertEqual(index.max_length, 15)
+ self.assertEqual(index.guard_roles, ('guard_role1', 'guard_role2'))
+ self.assertEqual(index.guard_permission, 'guard_permission')
+
+ def test_ctor_with_extra(self):
# Simulate instantiating from ZCatalog
+ klass = self._getTargetClass()
class extra:
field_name = 'bruford'
date_name = 'wakeman'
max_length = 25
guard_roles = ['Anonymous']
guard_permission = 'View'
- index = self._getTargetClass()('extra', extra=extra)
+ index = klass('extra', extra=extra)
self.assertEqual(index.getId(), 'extra')
self.assertEqual(index.field_name, 'bruford')
self.assertEqual(index.date_name, 'wakeman')
@@ -127,22 +205,44 @@
self.assertEqual(tuple(index.guard_roles), ('Anonymous',))
self.assertEqual(index.guard_permission, 'View')
- def test_construct_with_no_classifier_or_guard(self):
+ def test_ctor_with_no_classifier_or_guard(self):
# Simulate instantiating from ZCatalog
+ klass = self._getTargetClass()
class extra:
date_name = 'modified'
max_length = 30
- index = self._getTargetClass()('nuttin', extra=extra)
+ index = klass('nuttin', extra=extra)
self.assertEqual(index.getId(), 'nuttin')
+ self.assertEqual(index.field_name, None)
self.assertEqual(index.date_name, 'modified')
self.assertEqual(index.max_length, 30)
+ self.assertEqual(index.guard_roles, None)
+ self.assertEqual(index.guard_permission, None)
- def test_construct_with_bogus_max_length(self):
- self.assertRaises(
- Exception, self._getTargetClass(), 'test', 'type', 'date', 0)
- self.assertRaises(
- Exception, self._getTargetClass(), 'test', 'type', 'date', -20)
+ def test_ctor_with_zero_max_length(self):
+ klass = self._getTargetClass()
+ self.assertRaises(ValueError, klass, 'test', 'type', 'date', 0)
+ def test_ctor_with_negative_max_length(self):
+ klass = self._getTargetClass()
+ self.assertRaises(ValueError, klass, 'test', 'type', 'date', -20)
+
+ def test_getEntryForObject(self):
+ index = self._makeOne()
+ docs = self._makeAndIndexDocs(index)
+ top = self._get_top_docs(docs)
+ for docid, doc in docs.items():
+ entry = index.getEntryForObject(docid)
+ if entry is not None:
+ self.assertEqual(entry,
+ {'value': doc.type, 'date': doc.date.timeTime()})
+ else:
+ self.failIf((doc.date.timeTime(), doc.docid) in top[doc.type])
+
+ def test_getIndexSourceNames(self):
+ index = self._makeOne(field_name='foo')
+ self.assertEqual(index.getIndexSourceNames(), ('foo',))
+
def test_index_object_skips_obj_without_field_or_date(self):
index = self._makeOne()
doc = self._makeDoc()
@@ -165,37 +265,100 @@
self.assertEqual(index.numObjects(), 0)
self.assertEqual(index.getItemCounts(), {})
- def test_index_single(self):
+ def test_index_object_single(self):
index = self._makeOne()
result = self._makeAndIndexOneDoc(index)
self.failUnless(result)
self.assertEqual(index.numObjects(), 1)
self.assertEqual(index.getItemCounts(), {'fluke': 1})
- def test_unindex_single(self):
+ def test_index_object_many(self):
index = self._makeOne()
- result = self._makeAndIndexOneDoc(index)
- self.failUnless(index.unindex_object(1))
- self.assertEqual(index.numObjects(), 0)
- self.assertEqual(index.getItemCounts(), {})
-
- def test_index_many(self):
- index = self._makeOne()
docs = self._makeAndIndexDocs(index)
maxlen = index.max_length
self.assertEqual(index.getItemCounts(),
{'huey': maxlen, 'dooey':maxlen, 'looey':maxlen})
self.assertEqual(index.numObjects(), maxlen*3)
- def test_index_many_no_classifier(self):
+ def test_index_object_many_no_classifier(self):
index = self._makeOne('test', None, 'date', 10)
docs = self._makeAndIndexDocs(index)
maxlen = index.max_length
self.assertEqual(index.getItemCounts(), {None: maxlen,})
self.assertEqual(index.numObjects(), maxlen)
- def test_unindex_one_type(self):
+ def test_index_object_again_no_change(self):
+ # reindex with no change should be a no-op
index = self._makeOne()
+ doc = self._get_indexed_doc(index)
+ self.failIf(index.index_object(doc.docid, doc))
+ self.assertEqual(index.getEntryForObject(doc.docid),
+ {'value':doc.type, 'date':doc.date.timeTime()})
+
+ def test_index_object_change_date(self):
+ index = self._makeOne()
+ doc = self._get_indexed_doc(index)
+ doc.date = doc.date + 10
+ self.failUnless(index.index_object(doc.docid, doc))
+ self.assertEqual(index.getEntryForObject(doc.docid),
+ {'value':doc.type, 'date':doc.date.timeTime()})
+
+ def test_index_object_change_value(self):
+ index = self._makeOne()
+ doc = self._get_indexed_doc(index, fromtop=1)
+ oldtype = doc.type
+ for typ in index.uniqueValues():
+ if typ != oldtype:
+ doc.type = typ
+ break
+ self.failUnless(index.index_object(doc.docid, doc))
+ self.assertEqual(index.getEntryForObject(doc.docid),
+ {'value':doc.type, 'date':doc.date.timeTime()})
+
+ def test_index_object_change_date_and_value(self):
+ index = self._makeOne()
+ doc = self._get_indexed_doc(index, fromtop=1)
+ doc.date = doc.date + 4
+ oldtype = doc.type
+ for typ in index.uniqueValues():
+ if typ != oldtype:
+ doc.type = typ
+ break
+ self.failUnless(index.index_object(doc.docid, doc))
+ self.assertEqual(index.getEntryForObject(doc.docid),
+ {'value':doc.type, 'date':doc.date.timeTime()})
+
+ def test_index_object_w_role_permission_guard(self):
+ index = self._makeOne(
+ 'test', 'type', 'date', 5, ['NerfHerder', 'Bloke'], 'View')
+ viewable = self._makeViewable('NerfHerder')
+ index.index_object(0, viewable)
+ self.assertEqual(index.numObjects(), 1)
+ notviewable = self._makeViewable()
+ index.index_object(1, notviewable)
+ self.assertEqual(index.numObjects(), 1)
+ bloke = self._makeViewable('Bloke')
+ index.index_object(2, bloke)
+ self.assertEqual(index.numObjects(), 2)
+ bloke.manage_permission('View', [])
+ index.index_object(2, bloke)
+ self.assertEqual(index.numObjects(), 1)
+ dummy = self._makeViewable('Dummy')
+ index.index_object(3, dummy)
+ self.assertEqual(index.numObjects(), 1)
+ viewable.manage_permission('View', [])
+ index.index_object(0, viewable)
+ self.assertEqual(index.numObjects(), 0)
+
+ def test_unindex_object_single(self):
+ index = self._makeOne()
+ result = self._makeAndIndexOneDoc(index)
+ self.failUnless(index.unindex_object(1))
+ self.assertEqual(index.numObjects(), 0)
+ self.assertEqual(index.getItemCounts(), {})
+
+ def test_unindex_object_one_type(self):
+ index = self._makeOne()
docs = self._makeAndIndexDocs(index)
for docid, doc in docs.items():
if doc.type == 'looey':
@@ -203,7 +366,7 @@
self.assertEqual(index.numObjects(), 20)
self.assertEqual(index.getItemCounts(), {'huey': 10, 'dooey':10})
- def test_unindex_all(self):
+ def test_unindex_object_all(self):
index = self._makeOne()
docs = self._makeAndIndexDocs(index)
for docid in docs.keys():
@@ -212,31 +375,10 @@
self.assertEqual(index.getItemCounts(), {})
self.assertEqual(list(index.uniqueValues()), [])
- def _get_top_docs(self, docs):
- top = {'huey':[], 'dooey':[], 'looey':[]}
- for doc in docs.values():
- top[doc.type].append((doc.date.timeTime(), doc.docid))
- for typ, docs in top.items():
- docs.sort()
- top[typ] = docs[-10:]
- return top
-
- def test_getEntryForObject(self):
+ def test_unindex_object_most_recent(self):
index = self._makeOne()
docs = self._makeAndIndexDocs(index)
top = self._get_top_docs(docs)
- for docid, doc in docs.items():
- entry = index.getEntryForObject(docid)
- if entry is not None:
- self.assertEqual(entry,
- {'value': doc.type, 'date': doc.date.timeTime()})
- else:
- self.failIf((doc.date.timeTime(), doc.docid) in top[doc.type])
-
- def test_unindex_most_recent(self):
- index = self._makeOne()
- docs = self._makeAndIndexDocs(index)
- top = self._get_top_docs(docs)
item_counts = index.getItemCounts()
total_count = 30
for i in range(10):
@@ -252,67 +394,122 @@
self.assertEqual(index.numObjects(), 0)
self.assertEqual(index.getItemCounts(), {})
- def test_unindex_bogus_rid(self):
+ def test_unindex_object_bogus_rid(self):
index = self._makeOne()
docs = self._makeAndIndexDocs(index)
self.failIf(index.unindex_object(-2000))
- def _get_indexed_doc(self, index, fromtop=0):
+ def test_apply_index(self):
+ # _apply_index always returns none since recent items index
+ # do not participate in the normal ZCatalog query as they
+ # handle both intersection and sorting
+ index = self._makeOne()
+ self.failUnless(index._apply_index({}) is None)
+ self.failUnless(index._apply_index({'query':'looey'}) is None)
+
+ def test_numObjects(self):
+ index = self._makeOne()
docs = self._makeAndIndexDocs(index)
- top = self._get_top_docs(docs)
- items = docs.items()
- if fromtop:
- items.reverse()
- for docid, doc in items:
- entry = index.getEntryForObject(docid)
- if entry is not None:
- break
- else:
- self.fail('No objects in index')
- self.assertEqual(entry, {'value':doc.type, 'date':doc.date.timeTime()})
- return doc
+ self.assertEqual(index.numObjects(), 30)
- def test_reindex_no_change(self):
- # reindex with no change should be a no-op
+ def test_numObjects_small_maxlen(self):
index = self._makeOne()
- doc = self._get_indexed_doc(index)
- self.failIf(index.index_object(doc.docid, doc))
- self.assertEqual(index.getEntryForObject(doc.docid),
- {'value':doc.type, 'date':doc.date.timeTime()})
+ index.max_length = 1
+ docs = self._makeAndIndexDocs(index)
+ self.assertEqual(index.numObjects(), 3)
- def test_reindex_change_date(self):
+ def test_numObjects_empty_index(self):
index = self._makeOne()
- doc = self._get_indexed_doc(index)
- doc.date = doc.date + 10
- self.failUnless(index.index_object(doc.docid, doc))
- self.assertEqual(index.getEntryForObject(doc.docid),
- {'value':doc.type, 'date':doc.date.timeTime()})
+ self.assertEqual(index.numObjects(), 0)
- def test_reindex_change_value(self):
+ def test_indexSize_empty(self):
index = self._makeOne()
- doc = self._get_indexed_doc(index, fromtop=1)
- oldtype = doc.type
- for typ in index.uniqueValues():
- if typ != oldtype:
- doc.type = typ
- break
- self.failUnless(index.index_object(doc.docid, doc))
- self.assertEqual(index.getEntryForObject(doc.docid),
- {'value':doc.type, 'date':doc.date.timeTime()})
+ self.assertEqual(index.indexSize(), 0)
- def test_reindex_change_date_and_value(self):
+ def test_indexSize_one_value(self):
index = self._makeOne()
- doc = self._get_indexed_doc(index, fromtop=1)
- doc.date = doc.date + 4
- oldtype = doc.type
- for typ in index.uniqueValues():
- if typ != oldtype:
- doc.type = typ
- break
- self.failUnless(index.index_object(doc.docid, doc))
- self.assertEqual(index.getEntryForObject(doc.docid),
- {'value':doc.type, 'date':doc.date.timeTime()})
+ self._makeAndIndexOneDoc(index)
+ self.assertEqual(index.indexSize(), 1)
+ def test_indexSize_many_values(self):
+ index = self._makeOne()
+ self._makeAndIndexDocs(index)
+ self.assertEqual(index.indexSize(), 3)
+
+ def test_clear(self):
+ index = self._makeOne()
+ docs = self._makeAndIndexDocs(index)
+ self.failUnless(index.numObjects())
+ index.clear()
+ self.assertEqual(index.numObjects(), 0)
+
+ def test_hasUniqueValuesFor_hit(self):
+ index = self._makeOne()
+ self.failUnless(index.hasUniqueValuesFor('type'))
+
+ def test_hasUniqueValuesFor_miss(self):
+ index = self._makeOne()
+ self.failIf(index.hasUniqueValuesFor('spork'))
+
+ def test_uniqueValues_empty(self):
+ index = self._makeOne()
+ self.failIf(index.uniqueValues('type'))
+
+ def test_uniqueValues_non_empty_hit(self):
+ index = self._makeOne()
+ docs = self._makeAndIndexDocs(index)
+ values = list(index.uniqueValues('type'))
+ self.assertEqual(sorted(values), ['dooey', 'huey', 'looey'])
+
+ def test_uniqueValues_non_empty_miss(self):
+ index = self._makeOne()
+ docs = self._makeAndIndexDocs(index)
+ self.failIf(index.uniqueValues('carbtastic'))
+
+ def test_keyForDocument_empty(self):
+ index = self._makeOne()
+ self.assertRaises(KeyError, index.keyForDocument, 123)
+
+ def test_keyForDocument_non_empty_hit(self):
+ index = self._makeOne()
+ docs = self._makeAndIndexDocs(index)
+ for value in index.uniqueValues():
+ for date, rid in index._value2items[value]:
+ doc = docs[rid]
+ self.assertEqual(index.keyForDocument(rid), doc.type)
+ self.assertEqual(doc.date, date)
+
+ def test_documentToKeyMap_empty(self):
+ index = self._makeOne()
+ self.assertEqual(dict(index.documentToKeyMap()), {})
+
+ def test_documentToKeyMap_nonempty(self):
+ from DateTime.DateTime import DateTime
+ index = self._makeOne()
+ doc1 = self._makeDoc(type='doc', date=DateTime('1/1/2004'))
+ index.index_object(1, doc1)
+ doc2 = self._makeDoc(type='doc', date=DateTime('2/1/2004'))
+ index.index_object(2, doc2)
+ doc3 = self._makeDoc(type='doc', date=DateTime('3/1/2004'))
+ index.index_object(3, doc3)
+ self.assertEqual(dict(index.documentToKeyMap()),
+ {1: 'doc', 2: 'doc', 3: 'doc'})
+
+ def test_getItemCounts_empty(self):
+ index = self._makeOne()
+ self.assertEqual(dict(index.getItemCounts()), {})
+
+ def test_getItemCounts_nonempty(self):
+ from DateTime.DateTime import DateTime
+ index = self._makeOne()
+ doc1 = self._makeDoc(type='doc', date=DateTime('1/1/2004'))
+ index.index_object(1, doc1)
+ doc2 = self._makeDoc(type='doc', date=DateTime('2/1/2004'))
+ index.index_object(2, doc2)
+ doc3 = self._makeDoc(type='doc2', date=DateTime('3/1/2004'))
+ index.index_object(3, doc3)
+ self.assertEqual(dict(index.getItemCounts()), {'doc': 2, 'doc2': 1})
+
def test_query_empty_index(self):
index = self._makeOne()
result = index.query('foobar')
@@ -355,19 +552,6 @@
for rrow, erow in zip(result, expected):
self.assertEqual(rrow[:2], erow[:2])
- def _getExpectedTopDocs(self, index, docs, limit=None):
- top = self._get_top_docs(docs)
- query = ['huey', 'dooey']
- if limit is None:
- result = index.query(query)
- else:
- result = index.query(query, limit=limit)
- expected = top['huey'] + top['dooey']
- expected.sort()
- expected = [docid for nil, docid in expected]
- expected.reverse()
- return result, expected
-
def test_query_multiple_values(self):
index = self._makeOne()
docs = self._makeAndIndexDocs(index)
@@ -442,73 +626,6 @@
for rrow, erow in zip(result, expected):
self.assertEqual(rrow[:2], erow[:2])
- def test_apply_index(self):
- # _apply_index always returns none since recent items index
- # do not participate in the normal ZCatalog query as they
- # handle both intersection and sorting
- index = self._makeOne()
- self.failUnless(index._apply_index({}) is None)
- self.failUnless(index._apply_index({'query':'looey'}) is None)
-
- def test_uniqueValues(self):
- index = self._makeOne()
- self.failIf(index.uniqueValues('type'))
- index = self._makeOne()
- docs = self._makeAndIndexDocs(index)
- values = list(index.uniqueValues('type'))
- values.sort()
- self.assertEqual(values, ['dooey', 'huey', 'looey'])
- self.failIf(index.uniqueValues('carbtastic'))
-
- def test_hasUniqueValuesFor(self):
- index = self._makeOne()
- self.failUnless(index.hasUniqueValuesFor('type'))
- self.failIf(index.hasUniqueValuesFor('spork'))
-
- def test_numObjects(self):
- index = self._makeOne()
- docs = self._makeAndIndexDocs(index)
- self.assertEqual(index.numObjects(), 30)
-
- def test_numObjects_small_maxlen(self):
- index = self._makeOne()
- index.max_length = 1
- docs = self._makeAndIndexDocs(index)
- self.assertEqual(index.numObjects(), 3)
-
- def test_numObjects_empty_index(self):
- index = self._makeOne()
- self.assertEqual(index.numObjects(), 0)
-
- def test_clear(self):
- index = self._makeOne()
- docs = self._makeAndIndexDocs(index)
- self.failUnless(index.numObjects())
- index.clear()
- self.assertEqual(index.numObjects(), 0)
-
- def test_role_permission_guard(self):
- index = self._makeOne(
- 'test', 'type', 'date', 5, ['NerfHerder', 'Bloke'], 'View')
- viewable = self._makeViewable('NerfHerder')
- index.index_object(0, viewable)
- self.assertEqual(index.numObjects(), 1)
- notviewable = self._makeViewable()
- index.index_object(1, notviewable)
- self.assertEqual(index.numObjects(), 1)
- bloke = self._makeViewable('Bloke')
- index.index_object(2, bloke)
- self.assertEqual(index.numObjects(), 2)
- bloke.manage_permission('View', [])
- index.index_object(2, bloke)
- self.assertEqual(index.numObjects(), 1)
- dummy = self._makeViewable('Dummy')
- index.index_object(3, dummy)
- self.assertEqual(index.numObjects(), 1)
- viewable.manage_permission('View', [])
- index.index_object(0, viewable)
- self.assertEqual(index.numObjects(), 0)
-
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(RecentItemsIndexTest),
More information about the checkins
mailing list