source: main/waeup.sirp/branches/unique-index/tests.py @ 10872

Last change on this file since 10872 was 6211, checked in by uli, 14 years ago

Provide a unique field index for catalogs.

File size: 13.3 KB
Line 
1"""Test unique field index
2"""
3import unittest
4from zope.catalog.catalog import Catalog
5from waeup.sirp.index.uniquefield import UniqueFieldIndex as FieldIndex
6from zope.catalog.tests import (
7    PlacelessSetup, IntIdsStub, stoopid, stoopidCallable,)
8from zope.component import provideUtility
9from zope.intid.interfaces import IIntIds
10
11_marker = object()
12
13class RawUniqueFieldIndexTests(unittest.TestCase):
14
15    def _getTargetClass(self):
16        from waeup.sirp.index.uniquefield import RawUniqueFieldIndex
17        return RawUniqueFieldIndex
18
19    def _makeOne(self, family=_marker):
20        if family is _marker:
21            return self._getTargetClass()()
22        return self._getTargetClass()(family)
23
24    def _populateIndex(self, index):
25        index.index_doc(5, 1) # docid, obj
26        index.index_doc(2, 2)
27        index.index_doc(1, 3)
28        index.index_doc(3, 4)
29        index.index_doc(4, 5)
30        index.index_doc(8, 6)
31        index.index_doc(9, 7)
32        index.index_doc(7, 8)
33        index.index_doc(6, 9)
34        index.index_doc(11, 10)
35        index.index_doc(10, 11)
36
37    def test_class_conforms_to_IInjection(self):
38        from zope.interface.verify import verifyClass
39        from zope.index.interfaces import IInjection
40        verifyClass(IInjection, self._getTargetClass())
41
42    def test_instance_conforms_to_IInjection(self):
43        from zope.interface.verify import verifyObject
44        from zope.index.interfaces import IInjection
45        verifyObject(IInjection, self._makeOne())
46
47    def test_class_conforms_to_IUniqueInjection(self):
48        from zope.interface.verify import verifyClass
49        from waeup.sirp.index.interfaces import IUnique
50        verifyClass(IUnique, self._getTargetClass())
51
52    def test_instance_conforms_to_IUniqueInjection(self):
53        from zope.interface.verify import verifyObject
54        from waeup.sirp.index.interfaces import IUnique
55        verifyObject(IUnique, self._makeOne())
56
57    def test_class_conforms_to_IIndexSearch(self):
58        from zope.interface.verify import verifyClass
59        from zope.index.interfaces import IIndexSearch
60        verifyClass(IIndexSearch, self._getTargetClass())
61
62    def test_instance_conforms_to_IIndexSearch(self):
63        from zope.interface.verify import verifyObject
64        from zope.index.interfaces import IIndexSearch
65        verifyObject(IIndexSearch, self._makeOne())
66
67    def test_class_conforms_to_IStatistics(self):
68        from zope.interface.verify import verifyClass
69        from zope.index.interfaces import IStatistics
70        verifyClass(IStatistics, self._getTargetClass())
71
72    def test_instance_conforms_to_IStatistics(self):
73        from zope.interface.verify import verifyObject
74        from zope.index.interfaces import IStatistics
75        verifyObject(IStatistics, self._makeOne())
76
77    def test_ctor_defaults(self):
78        import BTrees
79        index = self._makeOne()
80        self.failUnless(index.family is BTrees.family32)
81        self.assertEqual(index.documentCount(), 0)
82        self.assertEqual(index.wordCount(), 0)
83
84    def test_ctor_explicit_family(self):
85        import BTrees
86        index = self._makeOne(BTrees.family64)
87        self.failUnless(index.family is BTrees.family64)
88
89    def test_index_doc_new(self):
90        index = self._makeOne()
91        index.index_doc(1, 'value')
92        self.assertEqual(index.documentCount(), 1)
93        self.assertEqual(index.wordCount(), 1)
94        self.failUnless(1 in index._rev_index)
95        self.failUnless('value' in index._fwd_index)
96
97    def test_index_doc_existing_same_value(self):
98        index = self._makeOne()
99        index.index_doc(1, 'value')
100        index.index_doc(1, 'value')
101        self.assertEqual(index.documentCount(), 1)
102        self.assertEqual(index.wordCount(), 1)
103        self.failUnless(1 in index._rev_index)
104        self.failUnless('value' in index._fwd_index)
105        self.assertEqual(list(index._fwd_index['value']), [1])
106
107    def test_index_doc_existing_new_value(self):
108        index = self._makeOne()
109        index.index_doc(1, 'value')
110        index.index_doc(1, 'new_value')
111        self.assertEqual(index.documentCount(), 1)
112        self.assertEqual(index.wordCount(), 1)
113        self.failUnless(1 in index._rev_index)
114        self.failIf('value' in index._fwd_index)
115        self.failUnless('new_value' in index._fwd_index)
116        self.assertEqual(list(index._fwd_index['new_value']), [1])
117
118    def test_unindex_doc_nonesuch(self):
119        index = self._makeOne()
120        index.unindex_doc(1) # doesn't raise
121
122    def test_unindex_doc_no_residual_fwd_values(self):
123        index = self._makeOne()
124        index.index_doc(1, 'value')
125        index.unindex_doc(1) # doesn't raise
126        self.assertEqual(index.documentCount(), 0)
127        self.assertEqual(index.wordCount(), 0)
128        self.failIf(1 in index._rev_index)
129        self.failIf('value' in index._fwd_index)
130
131    def test_unindex_doc_w_residual_fwd_values(self):
132        index = self._makeOne()
133        index.index_doc(1, 'value')
134        self.assertRaises(
135            KeyError,
136            index.index_doc, 2, 'value')
137        index.unindex_doc(1) # doesn't raise
138        self.assertEqual(index.documentCount(), 0)
139        self.assertEqual(index.wordCount(), 0)
140        self.failIf(1 in index._rev_index)
141        self.failIf(2 in index._rev_index)
142        self.failIf('value' in index._fwd_index)
143        self.assertRaises(
144            KeyError,
145            index._fwd_index.__getitem__, 'value')
146        self.assertEqual(index._fwd_index.get('value'), None)
147
148    def test_apply_non_tuple_raises(self):
149        index = self._makeOne()
150        self.assertRaises(TypeError, index.apply, ['a', 'b'])
151
152    def test_apply_empty_tuple_raises(self):
153        index = self._makeOne()
154        self.assertRaises(TypeError, index.apply, ('a',))
155
156    def test_apply_one_tuple_raises(self):
157        index = self._makeOne()
158        self.assertRaises(TypeError, index.apply, ('a',))
159
160    def test_apply_three_tuple_raises(self):
161        index = self._makeOne()
162        self.assertRaises(TypeError, index.apply, ('a', 'b', 'c'))
163
164    def test_apply_two_tuple_miss(self):
165        index = self._makeOne()
166        self.assertEqual(list(index.apply(('a', 'b'))), [])
167
168    def test_apply_two_tuple_hit(self):
169        index = self._makeOne()
170        index.index_doc(1, 'albatross')
171        self.assertEqual(list(index.apply(('a', 'b'))), [1])
172
173    def test_sort_w_limit_lt_1(self):
174        index = self._makeOne()
175        self.assertRaises(ValueError,
176                          lambda: list(index.sort([1, 2, 3], limit=0)))
177
178    def test_sort_w_empty_index(self):
179        index = self._makeOne()
180        self.assertEqual(list(index.sort([1, 2, 3])), [])
181
182    def test_sort_w_empty_docids(self):
183        index = self._makeOne()
184        index.index_doc(1, 'albatross')
185        self.assertEqual(list(index.sort([])), [])
186
187    def test_sort_w_missing_docids(self):
188        index = self._makeOne()
189        index.index_doc(1, 'albatross')
190        self.assertEqual(list(index.sort([2, 3])), [])
191
192    def test_sort_force_nbest_w_missing_docids(self):
193        index = self._makeOne()
194        index._use_nbest = True
195        index.index_doc(1, 'albatross')
196        self.assertEqual(list(index.sort([2, 3])), [])
197
198    def test_sort_force_lazy_w_missing_docids(self):
199        index = self._makeOne()
200        index._use_lazy = True
201        index.index_doc(1, 'albatross')
202        self.assertEqual(list(index.sort([2, 3])), [])
203
204    def test_sort_lazy_nolimit(self):
205        from BTrees.IFBTree import IFSet
206        index = self._makeOne()
207        index._use_lazy = True
208        self._populateIndex(index)
209        c1 = IFSet([1, 2, 3, 4, 5])
210        result = index.sort(c1)
211        self.assertEqual(list(result), [5, 2, 1, 3, 4])
212
213    def test_sort_lazy_withlimit(self):
214        from BTrees.IFBTree import IFSet
215        index = self._makeOne()
216        index._use_lazy = True
217        self._populateIndex(index)
218        c1 = IFSet([1, 2, 3, 4, 5])
219        result = index.sort(c1, limit=3)
220        self.assertEqual(list(result), [5, 2, 1])
221
222    def test_sort_nonlazy_nolimit(self):
223        from BTrees.IFBTree import IFSet
224        index = self._makeOne()
225        self._populateIndex(index)
226        c1 = IFSet([1, 2, 3, 4, 5])
227        result = index.sort(c1)
228        self.assertEqual(list(result), [5, 2, 1, 3, 4])
229
230    def test_sort_nonlazy_missingdocid(self):
231        from BTrees.IFBTree import IFSet
232        index = self._makeOne()
233        self._populateIndex(index)
234        c1 = IFSet([1, 2, 3, 4, 5, 99])
235        result = index.sort(c1)
236        self.assertEqual(list(result), [5, 2, 1, 3, 4]) # 99 not present
237
238    def test_sort_nonlazy_withlimit(self):
239        from BTrees.IFBTree import IFSet
240        index = self._makeOne()
241        self._populateIndex(index)
242        c1 = IFSet([1, 2, 3, 4, 5])
243        result = index.sort(c1, limit=3)
244        self.assertEqual(list(result), [5, 2, 1])
245
246    def test_sort_nonlazy_reverse_nolimit(self):
247        from BTrees.IFBTree import IFSet
248        index = self._makeOne()
249        self._populateIndex(index)
250        c1 = IFSet([1, 2, 3, 4, 5])
251        result = index.sort(c1, reverse=True)
252        self.assertEqual(list(result), [4, 3, 1, 2, 5])
253
254    def test_sort_nonlazy_reverse_withlimit(self):
255        from BTrees.IFBTree import IFSet
256        index = self._makeOne()
257        self._populateIndex(index)
258        c1 = IFSet([1, 2, 3, 4, 5])
259        result = index.sort(c1, reverse=True, limit=3)
260        self.assertEqual(list(result), [4, 3, 1])
261
262    def test_sort_nbest(self):
263        from BTrees.IFBTree import IFSet
264        index = self._makeOne()
265        index._use_nbest = True
266        self._populateIndex(index)
267        c1 = IFSet([1, 2, 3, 4, 5])
268        result = index.sort(c1, limit=3)
269        self.assertEqual(list(result), [5, 2, 1])
270
271    def test_sort_nbest_reverse(self):
272        from BTrees.IFBTree import IFSet
273        index = self._makeOne()
274        index._use_nbest = True
275        self._populateIndex(index)
276        c1 = IFSet([1, 2, 3, 4, 5])
277        result = index.sort(c1, reverse=True, limit=3)
278        self.assertEqual(list(result), [4, 3, 1])
279
280    def test_sort_nbest_missing(self):
281        from BTrees.IFBTree import IFSet
282        index = self._makeOne()
283        index._use_nbest = True
284        self._populateIndex(index)
285        c1 = IFSet([1, 2, 3, 4, 5, 99])
286        result = index.sort(c1, limit=3)
287        self.assertEqual(list(result), [5, 2, 1])
288
289    def test_sort_nbest_missing_reverse(self):
290        from BTrees.IFBTree import IFSet
291        index = self._makeOne()
292        index._use_nbest = True
293        self._populateIndex(index)
294        c1 = IFSet([1, 2, 3, 4, 5, 99])
295        result = index.sort(c1, reverse=True, limit=3)
296        self.assertEqual(list(result), [4, 3, 1])
297
298    def test_sort_nodocs(self):
299        from BTrees.IFBTree import IFSet
300        index = self._makeOne()
301        c1 = IFSet([1, 2, 3, 4, 5])
302        result = index.sort(c1)
303        self.assertEqual(list(result), [])
304
305    def test_sort_nodocids(self):
306        from BTrees.IFBTree import IFSet
307        index = self._makeOne()
308        self._populateIndex(index)
309        c1 = IFSet()
310        result = index.sort(c1)
311        self.assertEqual(list(result), [])
312
313    def test_sort_badlimit(self):
314        from BTrees.IFBTree import IFSet
315        index = self._makeOne()
316        self._populateIndex(index)
317        c1 = IFSet([1, 2, 3, 4, 5])
318        result = index.sort(c1, limit=0)
319        self.assertRaises(ValueError, list, result)
320
321
322class TestCatalogBugs(PlacelessSetup, unittest.TestCase):
323    """I found that z.a.catalog, AttributeIndex failed to remove the previous
324    value/object from the index IF the NEW value is None.
325    """
326
327    def test_updateIndexWithNone(self):
328        uidutil = IntIdsStub()
329        provideUtility(uidutil, IIntIds)
330
331        catalog = Catalog()
332        index = FieldIndex('author', None)
333        catalog['author'] = index
334
335        ob1 = stoopid(author = "joe")
336        ob1id = uidutil.register(ob1)
337        catalog.index_doc(ob1id, ob1)
338
339        res = catalog.searchResults(author=('joe','joe'))
340        names = [x.author for x in res]
341        names.sort()
342        self.assertEqual(len(names), 1)
343        self.assertEqual(names, ['joe'])
344
345        ob1.author = None
346        catalog.index_doc(ob1id, ob1)
347
348        #the index must be empty now because None values are never indexed
349        res = catalog.searchResults(author=(None, None))
350        self.assertEqual(len(res), 0)
351
352    def test_updateIndexFromCallableWithNone(self):
353        uidutil = IntIdsStub()
354        provideUtility(uidutil, IIntIds)
355
356        catalog = Catalog()
357        index = FieldIndex('getAuthor', None, field_callable=True)
358        catalog['author'] = index
359
360        ob1 = stoopidCallable(author = "joe")
361
362        ob1id = uidutil.register(ob1)
363        catalog.index_doc(ob1id, ob1)
364
365        res = catalog.searchResults(author=('joe','joe'))
366        names = [x.author for x in res]
367        names.sort()
368        self.assertEqual(len(names), 1)
369        self.assertEqual(names, ['joe'])
370
371        ob1.author = None
372        catalog.index_doc(ob1id, ob1)
373
374        #the index must be empty now because None values are never indexed
375        res = catalog.searchResults(author=(None, None))
376        self.assertEqual(len(res), 0)
377
378def test_suite():
379    from zope.testing import doctest
380    return unittest.TestSuite((
381        doctest.DocFileSuite('README.txt', optionflags=doctest.ELLIPSIS),
382        unittest.makeSuite(RawUniqueFieldIndexTests),
383        unittest.makeSuite(TestCatalogBugs),
384        ))
385
386if __name__=='__main__':
387    unittest.main(defaultTest='test_suite')
Note: See TracBrowser for help on using the repository browser.