[Checkins] SVN: Products.ZCatalog/trunk/src/Products/PluginIndexes/ avoid deep tests nesting for single test files
Hano Schlichting
cvs-admin at zope.org
Sun Mar 25 13:40:06 UTC 2012
Log message for revision 124721:
avoid deep tests nesting for single test files
Changed:
D Products.ZCatalog/trunk/src/Products/PluginIndexes/DateIndex/tests/
A Products.ZCatalog/trunk/src/Products/PluginIndexes/DateIndex/tests.py
D Products.ZCatalog/trunk/src/Products/PluginIndexes/DateRangeIndex/tests/
A Products.ZCatalog/trunk/src/Products/PluginIndexes/DateRangeIndex/tests.py
D Products.ZCatalog/trunk/src/Products/PluginIndexes/FieldIndex/tests/
A Products.ZCatalog/trunk/src/Products/PluginIndexes/FieldIndex/tests.py
D Products.ZCatalog/trunk/src/Products/PluginIndexes/KeywordIndex/tests/
A Products.ZCatalog/trunk/src/Products/PluginIndexes/KeywordIndex/tests.py
D Products.ZCatalog/trunk/src/Products/PluginIndexes/PathIndex/tests/
A Products.ZCatalog/trunk/src/Products/PluginIndexes/PathIndex/tests.py
D Products.ZCatalog/trunk/src/Products/PluginIndexes/TopicIndex/tests/
A Products.ZCatalog/trunk/src/Products/PluginIndexes/TopicIndex/tests.py
-=-
Copied: Products.ZCatalog/trunk/src/Products/PluginIndexes/DateIndex/tests.py (from rev 124720, Products.ZCatalog/trunk/src/Products/PluginIndexes/DateIndex/tests/test_DateIndex.py)
===================================================================
--- Products.ZCatalog/trunk/src/Products/PluginIndexes/DateIndex/tests.py (rev 0)
+++ Products.ZCatalog/trunk/src/Products/PluginIndexes/DateIndex/tests.py 2012-03-25 13:40:03 UTC (rev 124721)
@@ -0,0 +1,276 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""DateIndex unit tests.
+"""
+
+import unittest
+
+
+class Dummy:
+
+ def __init__(self, name, date):
+ self._name = name
+ self._date = date
+
+ def name(self):
+ return self._name
+
+ def date(self):
+ return self._date
+
+ def __str__(self):
+ return "<Dummy %s, date %s>" % (self._name, str(self._date))
+
+###############################################################################
+# excerpted from the Python module docs
+###############################################################################
+
+def _getEastern():
+ from datetime import date
+ from datetime import datetime
+ from datetime import timedelta
+ from datetime import tzinfo
+ ZERO = timedelta(0)
+ HOUR = timedelta(hours=1)
+ def first_sunday_on_or_after(dt):
+ days_to_go = 6 - dt.weekday()
+ if days_to_go:
+ dt += timedelta(days_to_go)
+ return dt
+
+ # In the US, DST starts at 2am (standard time) on the first Sunday in
+ # April...
+ DSTSTART = datetime(1, 4, 1, 2)
+ # and ends at 2am (DST time; 1am standard time) on the last Sunday of
+ # October, which is the first Sunday on or after Oct 25.
+ DSTEND = datetime(1, 10, 25, 1)
+
+ class USTimeZone(tzinfo):
+
+ def __init__(self, hours, reprname, stdname, dstname):
+ self.stdoffset = timedelta(hours=hours)
+ self.reprname = reprname
+ self.stdname = stdname
+ self.dstname = dstname
+
+ def __repr__(self):
+ return self.reprname
+
+ def tzname(self, dt):
+ if self.dst(dt):
+ return self.dstname
+ else:
+ return self.stdname
+
+ def utcoffset(self, dt):
+ return self.stdoffset + self.dst(dt)
+
+ def dst(self, dt):
+ if dt is None or dt.tzinfo is None:
+ # An exception may be sensible here, in one or both cases.
+ # It depends on how you want to treat them. The default
+ # fromutc() implementation (called by the default astimezone()
+ # implementation) passes a datetime with dt.tzinfo is self.
+ return ZERO
+ assert dt.tzinfo is self
+
+ # Find first Sunday in April & the last in October.
+ start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
+ end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
+
+ # Can't compare naive to aware objects, so strip the timezone from
+ # dt first.
+ if start <= dt.replace(tzinfo=None) < end:
+ return HOUR
+ else:
+ return ZERO
+
+ return USTimeZone(-5, "Eastern", "EST", "EDT")
+
+###############################################################################
+
+
+class DI_Tests(unittest.TestCase):
+
+ def _getTargetClass(self):
+ from Products.PluginIndexes.DateIndex.DateIndex import DateIndex
+ return DateIndex
+
+ def _makeOne(self, id='date'):
+ return self._getTargetClass()(id)
+
+ def _getValues(self):
+ from DateTime import DateTime
+ from datetime import date
+ from datetime import datetime
+ return [
+ (0, Dummy('a', None)), # None
+ (1, Dummy('b', DateTime(0))), # 1055335680
+ (2, Dummy('c', DateTime('2002-05-08 15:16:17'))), # 1072667236
+ (3, Dummy('d', DateTime('2032-05-08 15:16:17'))), # 1088737636
+ (4, Dummy('e', DateTime('2062-05-08 15:16:17'))), # 1018883325
+ (5, Dummy('e', DateTime('2062-05-08 15:16:17'))), # 1018883325
+ (6, Dummy('f', 1072742620.0)), # 1073545923
+ (7, Dummy('f', 1072742900)), # 1073545928
+ (8, Dummy('g', date(2034,2,5))), # 1073599200
+ (9, Dummy('h', datetime(2034,2,5,15,20,5))), # (varies)
+ (10, Dummy('i', datetime(2034,2,5,10,17,5,
+ tzinfo=_getEastern()))), # 1073600117
+ ]
+
+ def _populateIndex(self, index):
+ for k, v in self._getValues():
+ index.index_object(k, v)
+
+ def _checkApply(self, index, req, expectedValues):
+ result, used = index._apply_index(req)
+ if hasattr(result, 'keys'):
+ result = result.keys()
+ self.assertEqual(used, ('date',))
+ self.assertEqual(len(result), len(expectedValues),
+ '%s | %s' % (result, expectedValues))
+ for k, v in expectedValues:
+ self.assertTrue(k in result)
+
+ def _convert(self, dt):
+ from time import gmtime
+ from datetime import date
+ from datetime import datetime
+ from Products.PluginIndexes.DateIndex.DateIndex import Local
+ if isinstance(dt, (float, int)):
+ yr, mo, dy, hr, mn = gmtime(dt)[:5]
+ elif type(dt) is date:
+ yr, mo, dy, hr, mn = dt.timetuple()[:5]
+ elif type(dt) is datetime:
+ if dt.tzinfo is None: # default behavior of index
+ dt = dt.replace(tzinfo=Local)
+ yr, mo, dy, hr, mn = dt.utctimetuple()[:5]
+ else:
+ yr, mo, dy, hr, mn = dt.toZone('UTC').parts()[:5]
+ return (((yr * 12 + mo) * 31 + dy) * 24 + hr) * 60 + mn
+
+ def test_interfaces(self):
+ from Products.PluginIndexes.interfaces import IDateIndex
+ from Products.PluginIndexes.interfaces import IPluggableIndex
+ from Products.PluginIndexes.interfaces import ISortIndex
+ from Products.PluginIndexes.interfaces import IUniqueValueIndex
+ from zope.interface.verify import verifyClass
+
+ verifyClass(IDateIndex, self._getTargetClass())
+ verifyClass(IPluggableIndex, self._getTargetClass())
+ verifyClass(ISortIndex, self._getTargetClass())
+ verifyClass(IUniqueValueIndex, self._getTargetClass())
+
+ def test_empty(self):
+ from DateTime import DateTime
+ index = self._makeOne()
+
+ self.assertEqual(len(index), 0)
+ self.assertEqual(len(index.referencedObjects()), 0)
+
+ self.assertTrue(index.getEntryForObject(1234) is None)
+ marker = []
+ self.assertTrue(index.getEntryForObject(1234, marker) is marker)
+ index.unindex_object(1234) # shouldn't throw
+
+ self.assertTrue(index.hasUniqueValuesFor('date'))
+ self.assertFalse(index.hasUniqueValuesFor('foo'))
+ self.assertEqual(len(index.uniqueValues('date')), 0)
+
+ self.assertTrue(index._apply_index({'zed': 12345}) is None)
+
+ self._checkApply(index,
+ {'date': DateTime(0)}, [])
+ self._checkApply(index,
+ {'date': {'query': DateTime('2032-05-08 15:16:17'),
+ 'range': 'min'}},
+ [])
+ self._checkApply(index,
+ {'date': {'query': DateTime('2032-05-08 15:16:17'),
+ 'range': 'max'}},
+ [])
+ self._checkApply(index,
+ {'date': {'query':(DateTime('2002-05-08 15:16:17'),
+ DateTime('2062-05-08 15:16:17')),
+ 'range': 'min:max'}},
+ [])
+
+ def test_retrieval( self ):
+ from DateTime import DateTime
+ index = self._makeOne()
+ self._populateIndex(index)
+ values = self._getValues()
+
+ self.assertEqual(len(index), len(values) - 2) # One dupe, one empty
+ self.assertEqual(len(index.referencedObjects()), len(values) - 1)
+ # One empty
+
+ self.assertTrue(index.getEntryForObject(1234) is None)
+ marker = []
+ self.assertTrue(index.getEntryForObject(1234, marker) is marker)
+ index.unindex_object(1234) # shouldn't throw
+
+ for k, v in values:
+ if v.date():
+ self.assertEqual(index.getEntryForObject(k),
+ self._convert(v.date()))
+
+ self.assertEqual(len(index.uniqueValues('date')), len(values) - 2)
+ self.assertTrue(index._apply_index({'bar': 123}) is None)
+
+ self._checkApply(index,
+ {'date': DateTime(0)}, values[1:2])
+ self._checkApply(index,
+ {'date': {'query': DateTime('2032-05-08 15:16:17'),
+ 'range': 'min'}},
+ values[3:6] + values[8:])
+ self._checkApply(index,
+ {'date': {'query': DateTime('2032-05-08 15:16:17'),
+ 'range': 'max'}},
+ values[1:4] + values[6:8])
+ self._checkApply(index,
+ {'date': {'query':(DateTime('2002-05-08 15:16:17'),
+ DateTime('2062-05-08 15:16:17')),
+ 'range': 'min:max'}},
+ values[2:] )
+ self._checkApply(index,
+ {'date': 1072742620.0}, [values[6]])
+ self._checkApply(index,
+ {'date': 1072742900}, [values[7]])
+
+ def test_naive_convert_to_utc(self):
+ index = self._makeOne()
+ values = self._getValues()
+ index.index_naive_time_as_local = False
+ self._populateIndex(index)
+ for k, v in values[9:]:
+ # assert that the timezone is effectively UTC for item 9,
+ # and still correct for item 10
+ yr, mo, dy, hr, mn = v.date().utctimetuple()[:5]
+ val = (((yr * 12 + mo) * 31 + dy) * 24 + hr) * 60 + mn
+ self.assertEqual(index.getEntryForObject(k), val)
+
+ def test_removal(self):
+ """ DateIndex would hand back spurious entries when used as a
+ sort_index, because it previously was not removing entries
+ from the _unindex when indexing an object with a value of
+ None. The catalog consults a sort_index's
+ documentToKeyMap() to build the brains.
+ """
+ values = self._getValues()
+ index = self._makeOne()
+ self._populateIndex(index)
+ self._checkApply(index,
+ {'date': 1072742900}, [values[7]])
+ index.index_object(7, None)
+ self.assertFalse(7 in index.documentToKeyMap().keys())
Copied: Products.ZCatalog/trunk/src/Products/PluginIndexes/DateRangeIndex/tests.py (from rev 124720, Products.ZCatalog/trunk/src/Products/PluginIndexes/DateRangeIndex/tests/test_DateRangeIndex.py)
===================================================================
--- Products.ZCatalog/trunk/src/Products/PluginIndexes/DateRangeIndex/tests.py (rev 0)
+++ Products.ZCatalog/trunk/src/Products/PluginIndexes/DateRangeIndex/tests.py 2012-03-25 13:40:03 UTC (rev 124721)
@@ -0,0 +1,247 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+import unittest
+
+
+class Dummy(object):
+
+ def __init__(self, name, start, stop):
+ self._name = name
+ self._start = start
+ self._stop = stop
+
+ def name(self):
+ return self._name
+
+ def start(self):
+ return self._start
+
+ def stop(self):
+ return self._stop
+
+ def datum(self):
+ return (self._start, self._stop)
+
+
+dummies = [ Dummy( 'a', None, None )
+ , Dummy( 'b', None, None )
+ , Dummy( 'c', 0, None )
+ , Dummy( 'd', 10, None )
+ , Dummy( 'e', None, 4 )
+ , Dummy( 'f', None, 11 )
+ , Dummy( 'g', 0, 11 )
+ , Dummy( 'h', 2, 9 )
+ ]
+
+
+def matchingDummies(value):
+ result = []
+ for dummy in dummies:
+ if ((dummy.start() is None or dummy.start() <= value)
+ and (dummy.stop() is None or dummy.stop() >= value)):
+ result.append(dummy)
+ return result
+
+
+class DRI_Tests(unittest.TestCase):
+
+ def _getTargetClass(self):
+ from Products.PluginIndexes.DateRangeIndex.DateRangeIndex \
+ import DateRangeIndex
+ return DateRangeIndex
+
+ def _makeOne(self, id, since_field=None, until_field=None, caller=None,
+ extra=None):
+ klass = self._getTargetClass()
+ return klass(id, since_field, until_field, caller, extra)
+
+ def test_interfaces(self):
+ from Products.PluginIndexes.interfaces import IDateRangeIndex
+ from Products.PluginIndexes.interfaces import IPluggableIndex
+ from Products.PluginIndexes.interfaces import ISortIndex
+ from Products.PluginIndexes.interfaces import IUniqueValueIndex
+ from zope.interface.verify import verifyClass
+
+ verifyClass(IDateRangeIndex, self._getTargetClass())
+ verifyClass(IPluggableIndex, self._getTargetClass())
+ verifyClass(ISortIndex, self._getTargetClass())
+ verifyClass(IUniqueValueIndex, self._getTargetClass())
+
+ def test_empty(self):
+ empty = self._makeOne('empty')
+
+ self.assertTrue(empty.getEntryForObject(1234) is None)
+ empty.unindex_object(1234) # shouldn't throw
+
+ self.assertFalse(empty.uniqueValues('foo'))
+ self.assertFalse(empty.uniqueValues('foo', 1))
+ self.assertTrue(empty._apply_index({'zed': 12345}) is None)
+
+ result, used = empty._apply_index({'empty': 12345})
+ self.assertFalse(result)
+ self.assertEqual(used, (None, None))
+
+ def test_retrieval(self):
+ index = self._makeOne('work', 'start', 'stop')
+
+ for i in range(len(dummies)):
+ index.index_object(i, dummies[i])
+
+ for i in range(len(dummies)):
+ self.assertEqual(index.getEntryForObject(i), dummies[i].datum())
+
+ for value in range(-1, 15):
+ matches = matchingDummies(value)
+ results, used = index._apply_index({'work': value})
+ self.assertEqual(used, ('start', 'stop'))
+ self.assertEqual(len(matches), len(results))
+
+ matches.sort(lambda x, y: cmp(x.name(), y.name()))
+
+ for result, match in map(None, results, matches):
+ self.assertEqual(index.getEntryForObject(result), match.datum())
+
+ def test_longdates(self):
+ too_large = long(2**31)
+ too_small = - long(2**31)
+ index = self._makeOne('work', 'start', 'stop')
+ bad = Dummy('bad', too_large, too_large)
+ self.assertRaises(OverflowError, index.index_object, 0, bad)
+ bad = Dummy('bad', too_small, too_small)
+ self.assertRaises(OverflowError, index.index_object, 0, bad)
+
+ def test_floor_date(self):
+ index = self._makeOne('work', 'start', 'stop')
+ floor = index.floor_value - 1
+ bad = Dummy('bad', floor, None)
+ index.index_object(0, bad)
+ self.assertTrue(0 in index._always.keys())
+
+ def test_ceiling_date(self):
+ index = self._makeOne('work', 'start', 'stop')
+ ceiling = index.ceiling_value + 1
+ bad = Dummy('bad', None, ceiling)
+ index.index_object(1, bad)
+ self.assertTrue(1 in index._always.keys())
+
+ def test_datetime(self):
+ from datetime import datetime
+ from DateTime.DateTime import DateTime
+ from Products.PluginIndexes.DateIndex.tests import _getEastern
+ before = datetime(2009, 7, 11, 0, 0, tzinfo=_getEastern())
+ start = datetime(2009, 7, 13, 5, 15, tzinfo=_getEastern())
+ between = datetime(2009, 7, 13, 5, 45, tzinfo=_getEastern())
+ stop = datetime(2009, 7, 13, 6, 30, tzinfo=_getEastern())
+ after = datetime(2009, 7, 14, 0, 0, tzinfo=_getEastern())
+
+ dummy = Dummy('test', start, stop)
+ index = self._makeOne('work', 'start', 'stop')
+ index.index_object(0, dummy)
+
+ self.assertEqual(index.getEntryForObject(0),
+ (DateTime(start).millis() / 60000,
+ DateTime(stop).millis() / 60000))
+
+ results, used = index._apply_index({'work': before})
+ self.assertEqual(len(results), 0)
+
+ results, used = index._apply_index({'work': start})
+ self.assertEqual(len(results), 1)
+
+ results, used = index._apply_index({'work': between})
+ self.assertEqual(len(results), 1)
+
+ results, used = index._apply_index({'work': stop})
+ self.assertEqual(len(results), 1)
+
+ results, used = index._apply_index({'work': after})
+ self.assertEqual(len(results), 0)
+
+ def test_datetime_naive_timezone(self):
+ from datetime import datetime
+ from DateTime.DateTime import DateTime
+ from Products.PluginIndexes.DateIndex.DateIndex import Local
+ before = datetime(2009, 7, 11, 0, 0)
+ start = datetime(2009, 7, 13, 5, 15)
+ start_local = datetime(2009, 7, 13, 5, 15, tzinfo=Local)
+ between = datetime(2009, 7, 13, 5, 45)
+ stop = datetime(2009, 7, 13, 6, 30)
+ stop_local = datetime(2009, 7, 13, 6, 30, tzinfo=Local)
+ after = datetime(2009, 7, 14, 0, 0)
+
+ dummy = Dummy('test', start, stop)
+ index = self._makeOne('work', 'start', 'stop')
+ index.index_object(0, dummy)
+
+ self.assertEqual(index.getEntryForObject(0),
+ (DateTime(start_local).millis() / 60000,
+ DateTime(stop_local).millis() / 60000))
+
+ results, used = index._apply_index({'work': before})
+ self.assertEqual(len(results), 0)
+
+ results, used = index._apply_index({'work': start})
+ self.assertEqual(len(results), 1)
+
+ results, used = index._apply_index({'work': between})
+ self.assertEqual(len(results), 1)
+
+ results, used = index._apply_index({'work': stop})
+ self.assertEqual(len(results), 1)
+
+ results, used = index._apply_index({'work': after})
+ self.assertEqual(len(results), 0)
+
+ def test_resultset(self):
+ from BTrees.IIBTree import IISet
+
+ index = self._makeOne('work', 'start', 'stop')
+ for i in range(len(dummies)):
+ index.index_object(i, dummies[i])
+
+ results, used = index._apply_index({'work': 20})
+ self.assertEqual(set(results), set([0, 1, 2, 3]))
+
+ # a resultset with everything doesn't actually limit
+ results, used = index._apply_index({'work': 20},
+ resultset=IISet(range(len(dummies))))
+ self.assertEqual(set(results), set([0, 1, 2, 3]))
+
+ # a small resultset limits
+ results, used = index._apply_index({'work': 20},
+ resultset=IISet([1, 2]))
+ self.assertEqual(set(results), set([1, 2]))
+
+ # the specified value is included
+ results, used = index._apply_index({'work': 11})
+ self.assertEqual(set(results), set([0, 1, 2, 3, 5, 6]))
+
+ # also for _since_only
+ results, used = index._apply_index({'work': 10})
+ self.assertEqual(set(results), set([0, 1, 2, 3, 5, 6]))
+
+ # the specified value is included with a large resultset
+ results, used = index._apply_index({'work': 11},
+ resultset=IISet(range(len(dummies))))
+ self.assertEqual(set(results), set([0, 1, 2, 3, 5, 6]))
+
+ # this also works for _since_only
+ results, used = index._apply_index({'work': 10},
+ resultset=IISet(range(len(dummies))))
+ self.assertEqual(set(results), set([0, 1, 2, 3, 5, 6]))
+
+ # the specified value is included with a small resultset
+ results, used = index._apply_index({'work': 11},
+ resultset=IISet([0, 5, 7]))
+ self.assertEqual(set(results), set([0, 5]))
Copied: Products.ZCatalog/trunk/src/Products/PluginIndexes/FieldIndex/tests.py (from rev 124720, Products.ZCatalog/trunk/src/Products/PluginIndexes/FieldIndex/tests/testFieldIndex.py)
===================================================================
--- Products.ZCatalog/trunk/src/Products/PluginIndexes/FieldIndex/tests.py (rev 0)
+++ Products.ZCatalog/trunk/src/Products/PluginIndexes/FieldIndex/tests.py 2012-03-25 13:40:03 UTC (rev 124721)
@@ -0,0 +1,231 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""FieldIndex unit tests.
+"""
+
+import unittest
+
+from Products.PluginIndexes.FieldIndex.FieldIndex import FieldIndex
+
+
+class Dummy:
+
+ def __init__(self, foo):
+ self._foo = foo
+
+ def foo(self):
+ return self._foo
+
+ def __str__(self):
+ return '<Dummy: %s>' % self._foo
+
+ __repr__ = __str__
+
+
+class FieldIndexTests(unittest.TestCase):
+ """Test FieldIndex objects.
+ """
+
+ def setUp(self):
+ self._index = FieldIndex('foo')
+ self._marker = []
+ self._values = [(0, Dummy('a')),
+ (1, Dummy('ab')),
+ (2, Dummy('abc')),
+ (3, Dummy('abca')),
+ (4, Dummy('abcd')),
+ (5, Dummy('abce')),
+ (6, Dummy('abce')),
+ (7, Dummy(0)), # Collector #1959
+ (8, Dummy(None))]
+ self._forward = {}
+ self._backward = {}
+ for k, v in self._values:
+ self._backward[k] = v
+ keys = self._forward.get(v, [])
+ self._forward[v] = keys
+
+ self._noop_req = {'bar': 123}
+ self._request = {'foo': 'abce'}
+ self._min_req = {'foo':
+ {'query': 'abc', 'range': 'min'}}
+ self._min_req_n = {'foo':
+ {'query': 'abc', 'range': 'min', 'not': 'abca'}}
+ self._max_req = {'foo':
+ {'query': 'abc', 'range': 'max'}}
+ self._max_req_n = {'foo':
+ {'query': 'abc', 'range': 'max', 'not': ['a', 'b', None, 0]}}
+ self._range_req = {'foo':
+ {'query': ('abc', 'abcd'), 'range': 'min:max'}}
+ self._range_ren = {'foo':
+ {'query': ('abc', 'abcd'), 'range': 'min:max', 'not': 'abcd'}}
+ self._range_non = {'foo':
+ {'query': ('a', 'aa'), 'range': 'min:max', 'not': 'a'}}
+ self._zero_req = {'foo': 0 }
+ self._none_req = {'foo': None }
+ self._not_1 = {'foo': {'query': 'a', 'not': 'a'}}
+ self._not_2 = {'foo': {'query': ['a', 'ab'], 'not': 'a'}}
+ self._not_3 = {'foo': {'not': 'a'}}
+ self._not_4 = {'foo': {'not': [0, None]}}
+ self._not_5 = {'foo': {'not': ['a', 'b']}}
+ self._not_6 = {'foo': 'a', 'bar': {'query': 123, 'not': 1}}
+
+ def _populateIndex(self):
+ for k, v in self._values:
+ self._index.index_object(k, v)
+
+ def _checkApply(self, req, expectedValues):
+ result, used = self._index._apply_index(req)
+ if hasattr(result, 'keys'):
+ result = result.keys()
+ assert used == ('foo', )
+ assert len(result) == len(expectedValues), \
+ '%s | %s' % (map(None, result), expectedValues)
+ for k, v in expectedValues:
+ assert k in result
+
+ def test_interfaces(self):
+ from Products.PluginIndexes.interfaces import IPluggableIndex
+ from Products.PluginIndexes.interfaces import ISortIndex
+ from Products.PluginIndexes.interfaces import IUniqueValueIndex
+ from zope.interface.verify import verifyClass
+
+ verifyClass(IPluggableIndex, FieldIndex)
+ verifyClass(ISortIndex, FieldIndex)
+ verifyClass(IUniqueValueIndex, FieldIndex)
+
+ def testEmpty(self):
+ "Test an empty FieldIndex."
+
+ assert len(self._index) == 0
+ assert len(self._index.referencedObjects()) == 0
+ self.assertEqual(self._index.numObjects(), 0)
+
+ assert self._index.getEntryForObject(1234) is None
+ assert (self._index.getEntryForObject(1234, self._marker)
+ is self._marker)
+ self._index.unindex_object(1234) # nothrow
+
+ assert self._index.hasUniqueValuesFor('foo')
+ assert not self._index.hasUniqueValuesFor('bar')
+ assert len(self._index.uniqueValues('foo')) == 0
+
+ assert self._index._apply_index(self._noop_req) is None
+ self._checkApply(self._request, [])
+ self._checkApply(self._min_req, [])
+ self._checkApply(self._min_req_n, [])
+ self._checkApply(self._max_req, [])
+ self._checkApply(self._max_req_n, [])
+ self._checkApply(self._range_req, [])
+ self._checkApply(self._range_ren, [])
+ self._checkApply(self._range_non, [])
+
+ def testPopulated(self):
+ """ Test a populated FieldIndex """
+ self._populateIndex()
+ values = self._values
+
+ assert len(self._index) == len(values) - 1 # 'abce' is duplicate
+ assert len(self._index.referencedObjects() ) == len(values)
+ self.assertEqual(self._index.indexSize(), len(values) - 1)
+
+ assert self._index.getEntryForObject(1234) is None
+ assert (self._index.getEntryForObject(1234, self._marker)
+ is self._marker)
+ self._index.unindex_object(1234) # nothrow
+
+ for k, v in values:
+ assert self._index.getEntryForObject(k) == v.foo()
+
+ assert len(self._index.uniqueValues('foo')) == len(values) - 1
+
+ assert self._index._apply_index(self._noop_req) is None
+
+ self._checkApply(self._request, values[-4:-2])
+ self._checkApply(self._min_req, values[2:-2])
+ self._checkApply(self._min_req_n, values[2:3] + values[4:-2])
+ self._checkApply(self._max_req, values[:3] + values[-2:])
+ self._checkApply(self._max_req_n, values[1:3])
+ self._checkApply(self._range_req, values[2:5])
+ self._checkApply(self._range_ren, values[2:4])
+ self._checkApply(self._range_non, [])
+
+ self._checkApply(self._not_1, [])
+ self._checkApply(self._not_2, values[1:2])
+ self._checkApply(self._not_3, values[1:])
+ self._checkApply(self._not_4, values[:7])
+ self._checkApply(self._not_5, values[1:])
+ self._checkApply(self._not_6, values[0:1])
+
+ def testZero(self):
+ """ Make sure 0 gets indexed """
+ self._populateIndex()
+ values = self._values
+ self._checkApply(self._zero_req, values[-2:-1])
+ assert 0 in self._index.uniqueValues('foo')
+
+ def testNone(self):
+ """ make sure None gets indexed """
+ self._populateIndex()
+ values = self._values
+ self._checkApply(self._none_req, values[-1:])
+ assert None in self._index.uniqueValues('foo')
+
+ def testReindex(self):
+ self._populateIndex()
+ result, used = self._index._apply_index({'foo': 'abc'})
+ assert list(result) == [2]
+ assert self._index.keyForDocument(2) == 'abc'
+ d = Dummy('world')
+ self._index.index_object(2, d)
+ result, used = self._index._apply_index({'foo': 'world'})
+ assert list(result) == [2]
+ assert self._index.keyForDocument(2) == 'world'
+ del d._foo
+ self._index.index_object(2, d)
+ result, used = self._index._apply_index({'foo': 'world'})
+ assert list(result) == []
+ try:
+ should_not_be = self._index.keyForDocument(2)
+ except KeyError:
+ # As expected, we deleted that attribute.
+ pass
+ else:
+ # before Collector #291 this would be 'world'
+ raise ValueError(repr(should_not_be))
+
+ def testRange(self):
+ """Test a range search"""
+ index = FieldIndex('foo')
+ for i in range(100):
+ index.index_object(i, Dummy(i % 10))
+
+ record = {'foo': {'query': [-99, 3], 'range': 'min:max'}}
+ r = index._apply_index(record)
+
+ assert tuple(r[1]) == ('foo', ), r[1]
+ r = list(r[0].keys())
+
+ expect = [
+ 0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33,
+ 40, 41, 42, 43, 50, 51, 52, 53, 60, 61, 62, 63, 70, 71, 72, 73,
+ 80, 81, 82, 83, 90, 91, 92, 93
+ ]
+ assert r == expect, r
+
+ # Make sure that range tests with incompatible paramters
+ # don't return empty sets.
+ record['foo']['operator'] = 'and'
+ r2, ignore = index._apply_index(record)
+ r2 = list(r2.keys())
+ assert r2 == r
Copied: Products.ZCatalog/trunk/src/Products/PluginIndexes/KeywordIndex/tests.py (from rev 124720, Products.ZCatalog/trunk/src/Products/PluginIndexes/KeywordIndex/tests/testKeywordIndex.py)
===================================================================
--- Products.ZCatalog/trunk/src/Products/PluginIndexes/KeywordIndex/tests.py (rev 0)
+++ Products.ZCatalog/trunk/src/Products/PluginIndexes/KeywordIndex/tests.py 2012-03-25 13:40:03 UTC (rev 124721)
@@ -0,0 +1,241 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+import unittest
+
+from Products.PluginIndexes.KeywordIndex.KeywordIndex import KeywordIndex
+
+
+class Dummy:
+
+ def __init__(self, foo):
+ self._foo = foo
+
+ def foo(self):
+ return self._foo
+
+ def __str__(self):
+ return '<Dummy: %s>' % self._foo
+
+ __repr__ = __str__
+
+
+def sortedUnique(seq):
+ unique = {}
+ for i in seq:
+ unique[i] = None
+ unique = unique.keys()
+ unique.sort()
+ return unique
+
+
+class TestKeywordIndex(unittest.TestCase):
+
+ _old_log_write = None
+
+ def setUp(self):
+ self._index = KeywordIndex('foo')
+ self._marker = []
+ self._values = [(0, Dummy(['a'])),
+ (1, Dummy(['a', 'b'])),
+ (2, Dummy(['a', 'b', 'c'])),
+ (3, Dummy(['a', 'b', 'c', 'a'])),
+ (4, Dummy(['a', 'b', 'c', 'd'])),
+ (5, Dummy(['a', 'b', 'c', 'e'])),
+ (6, Dummy(['a', 'b', 'c', 'e', 'f'])),
+ (7, Dummy([0])),
+ ]
+ self._noop_req = {'bar': 123}
+ self._all_req = {'foo': ['a']}
+ self._some_req = {'foo': ['e']}
+ self._overlap_req = {'foo': ['c', 'e']}
+ self._string_req = {'foo': 'a'}
+ self._zero_req = {'foo': [0]}
+
+ def _populateIndex(self):
+ for k, v in self._values:
+ self._index.index_object(k, v)
+
+ def _checkApply(self, req, expectedValues):
+ result, used = self._index._apply_index(req)
+ assert used == ('foo', )
+ assert len(result) == len(expectedValues), \
+ '%s | %s' % (map(None, result),
+ map(lambda x: x[0], expectedValues))
+
+ if hasattr(result, 'keys'):
+ result = result.keys()
+ for k, v in expectedValues:
+ assert k in result
+
+ def test_interfaces(self):
+ from Products.PluginIndexes.interfaces import IPluggableIndex
+ from Products.PluginIndexes.interfaces import ISortIndex
+ from Products.PluginIndexes.interfaces import IUniqueValueIndex
+ from zope.interface.verify import verifyClass
+
+ verifyClass(IPluggableIndex, KeywordIndex)
+ verifyClass(ISortIndex, KeywordIndex)
+ verifyClass(IUniqueValueIndex, KeywordIndex)
+
+ def testAddObjectWOKeywords(self):
+ self._populateIndex()
+ self._index.index_object(999, None)
+
+ def testEmpty(self):
+ assert len(self._index) == 0
+ assert len(self._index.referencedObjects()) == 0
+ self.assertEqual(self._index.numObjects(), 0)
+
+ assert self._index.getEntryForObject(1234) is None
+ assert (self._index.getEntryForObject(1234, self._marker)
+ is self._marker), self._index.getEntryForObject(1234)
+ self._index.unindex_object(1234) # nothrow
+
+ assert self._index.hasUniqueValuesFor('foo')
+ assert not self._index.hasUniqueValuesFor('bar')
+ assert len(self._index.uniqueValues('foo')) == 0
+
+ assert self._index._apply_index(self._noop_req) is None
+ self._checkApply(self._all_req, [])
+ self._checkApply(self._some_req, [])
+ self._checkApply(self._overlap_req, [])
+ self._checkApply(self._string_req, [])
+
+ def testPopulated(self):
+ self._populateIndex()
+ values = self._values
+
+ assert len(self._index.referencedObjects()) == len(values)
+ assert self._index.getEntryForObject(1234) is None
+ assert (self._index.getEntryForObject(1234, self._marker)
+ is self._marker)
+ self._index.unindex_object(1234) # nothrow
+ self.assertEqual(self._index.indexSize(), len(values) - 1)
+
+ for k, v in values:
+ entry = self._index.getEntryForObject(k)
+ entry.sort()
+ kw = sortedUnique(v.foo())
+ self.assertEqual(entry, kw)
+
+ assert len(self._index.uniqueValues('foo')) == len(values) - 1
+ assert self._index._apply_index(self._noop_req) is None
+
+ self._checkApply(self._all_req, values[:-1])
+ self._checkApply(self._some_req, values[5:7])
+ self._checkApply(self._overlap_req, values[2:7])
+ self._checkApply(self._string_req, values[:-1])
+
+ def testZero(self):
+ self._populateIndex()
+ values = self._values
+ self._checkApply(self._zero_req, values[-1:])
+ assert 0 in self._index.uniqueValues('foo')
+
+ def testReindexChange(self):
+ self._populateIndex()
+ expected = Dummy(['x', 'y'])
+ self._index.index_object(6, expected)
+ result, used = self._index._apply_index({'foo': ['x', 'y']})
+ result = result.keys()
+ assert len(result) == 1
+ assert result[0] == 6
+ result, used = self._index._apply_index(
+ {'foo': ['a', 'b', 'c', 'e', 'f']})
+ result = result.keys()
+ assert 6 not in result
+
+ def testReindexNoChange(self):
+ self._populateIndex()
+ expected = Dummy(['foo', 'bar'])
+ self._index.index_object(8, expected)
+ result, used = self._index._apply_index(
+ {'foo': ['foo', 'bar']})
+ result = result.keys()
+ assert len(result) == 1
+ assert result[0] == 8
+ self._index.index_object(8, expected)
+ result, used = self._index._apply_index(
+ {'foo': ['foo', 'bar']})
+ result = result.keys()
+ assert len(result) == 1
+ assert result[0] == 8
+
+ def testIntersectionWithRange(self):
+ # Test an 'and' search, ensuring that 'range' doesn't mess it up.
+ self._populateIndex()
+
+ record = {'foo': {'query': ['e', 'f'], 'operator': 'and'}}
+ self._checkApply(record, self._values[6:7])
+
+ # Make sure that 'and' tests with incompatible parameters
+ # don't return empty sets.
+ record['foo']['range'] = 'min:max'
+ self._checkApply(record, self._values[6:7])
+
+ def testDuplicateKeywords(self):
+ self._index.index_object(0, Dummy(['a', 'a', 'b', 'b']))
+ self._index.unindex_object(0)
+
+ def testCollectorIssue889(self):
+ # Test that collector issue 889 is solved
+ values = self._values
+ nonexistent = 'foo-bar-baz'
+ self._populateIndex()
+ # make sure key is not indexed
+ result = self._index._index.get(nonexistent, self._marker)
+ assert result is self._marker
+ # patched _apply_index now works as expected
+ record = {'foo': {'query': [nonexistent], 'operator': 'and'}}
+ self._checkApply(record, [])
+ record = {'foo': {'query': [nonexistent, 'a'], 'operator': 'and'}}
+ # and does not break anything
+ self._checkApply(record, [])
+ record = {'foo': {'query': ['d'], 'operator': 'and'}}
+ self._checkApply(record, values[4:5])
+ record = {'foo': {'query': ['a', 'e'], 'operator': 'and'}}
+ self._checkApply(record, values[5:7])
+
+ def test_noindexing_when_noattribute(self):
+ to_index = Dummy(['hello'])
+ self._index._index_object(10, to_index, attr='UNKNOWN')
+ self.assertFalse(self._index._unindex.get(10))
+ self.assertFalse(self._index.getEntryForObject(10))
+
+ def test_noindexing_when_raising_attribute(self):
+ class FauxObject:
+ def foo(self):
+ raise AttributeError
+ to_index = FauxObject()
+ self._index._index_object(10, to_index, attr='foo')
+ self.assertFalse(self._index._unindex.get(10))
+ self.assertFalse(self._index.getEntryForObject(10))
+
+ def test_noindexing_when_raising_typeeror(self):
+ class FauxObject:
+ def foo(self, name):
+ return 'foo'
+ to_index = FauxObject()
+ self._index._index_object(10, to_index, attr='foo')
+ self.assertFalse(self._index._unindex.get(10))
+ self.assertFalse(self._index.getEntryForObject(10))
+
+ def test_value_removes(self):
+ to_index = Dummy(['hello'])
+ self._index._index_object(10, to_index, attr='foo')
+ self.assertTrue(self._index._unindex.get(10))
+
+ to_index = Dummy('')
+ self._index._index_object(10, to_index, attr='foo')
+ self.assertFalse(self._index._unindex.get(10))
Copied: Products.ZCatalog/trunk/src/Products/PluginIndexes/PathIndex/tests.py (from rev 124720, Products.ZCatalog/trunk/src/Products/PluginIndexes/PathIndex/tests/testPathIndex.py)
===================================================================
--- Products.ZCatalog/trunk/src/Products/PluginIndexes/PathIndex/tests.py (rev 0)
+++ Products.ZCatalog/trunk/src/Products/PluginIndexes/PathIndex/tests.py 2012-03-25 13:40:03 UTC (rev 124721)
@@ -0,0 +1,529 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""PathIndex unit tests.
+"""
+
+import unittest
+
+
+class Dummy:
+
+ def __init__(self, path):
+ self.path = path
+
+ def getPhysicalPath(self):
+ return self.path.split('/')
+
+DUMMIES = {
+ 1 : Dummy("/aa/aa/aa/1.html"),
+ 2 : Dummy("/aa/aa/bb/2.html"),
+ 3 : Dummy("/aa/aa/cc/3.html"),
+ 4 : Dummy("/aa/bb/aa/4.html"),
+ 5 : Dummy("/aa/bb/bb/5.html"),
+ 6 : Dummy("/aa/bb/cc/6.html"),
+ 7 : Dummy("/aa/cc/aa/7.html"),
+ 8 : Dummy("/aa/cc/bb/8.html"),
+ 9 : Dummy("/aa/cc/cc/9.html"),
+ 10 : Dummy("/bb/aa/aa/10.html"),
+ 11 : Dummy("/bb/aa/bb/11.html"),
+ 12 : Dummy("/bb/aa/cc/12.html"),
+ 13 : Dummy("/bb/bb/aa/13.html"),
+ 14 : Dummy("/bb/bb/bb/14.html"),
+ 15 : Dummy("/bb/bb/cc/15.html"),
+ 16 : Dummy("/bb/cc/aa/16.html"),
+ 17 : Dummy("/bb/cc/bb/17.html"),
+ 18 : Dummy("/bb/cc/cc/18.html")
+}
+
+def _populateIndex(index):
+ for k, v in DUMMIES.items():
+ index.index_object(k, v)
+
+_marker = object()
+
+class PathIndexTests(unittest.TestCase):
+ """ Test PathIndex objects """
+
+ def _getTargetClass(self):
+ from Products.PluginIndexes.PathIndex.PathIndex import PathIndex
+ return PathIndex
+
+ def _makeOne(self, id='path', caller=_marker):
+ if caller is not _marker:
+ return self._getTargetClass()(id, caller)
+ return self._getTargetClass()(id)
+
+ def test_class_conforms_to_IPluggableIndex(self):
+ from Products.PluginIndexes.interfaces import IPluggableIndex
+ from zope.interface.verify import verifyClass
+ verifyClass(IPluggableIndex, self._getTargetClass())
+
+ def test_instance_conforms_to_IPluggableIndex(self):
+ from Products.PluginIndexes.interfaces import IPluggableIndex
+ from zope.interface.verify import verifyObject
+ verifyObject(IPluggableIndex, self._makeOne())
+
+ def test_class_conforms_to_IUniqueValueIndex(self):
+ from Products.PluginIndexes.interfaces import IUniqueValueIndex
+ from zope.interface.verify import verifyClass
+ verifyClass(IUniqueValueIndex, self._getTargetClass())
+
+ def test_instance_conforms_to_IUniqueValueIndex(self):
+ from Products.PluginIndexes.interfaces import IUniqueValueIndex
+ from zope.interface.verify import verifyObject
+ verifyObject(IUniqueValueIndex, self._makeOne())
+
+ def test_class_conforms_to_ISortIndex(self):
+ from Products.PluginIndexes.interfaces import ISortIndex
+ from zope.interface.verify import verifyClass
+ verifyClass(ISortIndex, self._getTargetClass())
+
+ def test_instance_conforms_to_ISortIndex(self):
+ from Products.PluginIndexes.interfaces import ISortIndex
+ from zope.interface.verify import verifyObject
+ verifyObject(ISortIndex, self._makeOne())
+
+ def test_class_conforms_to_IPathIndex(self):
+ from Products.PluginIndexes.interfaces import IPathIndex
+ from zope.interface.verify import verifyClass
+ verifyClass(IPathIndex, self._getTargetClass())
+
+ def test_instance_conforms_to_IPathIndex(self):
+ from Products.PluginIndexes.interfaces import IPathIndex
+ from zope.interface.verify import verifyObject
+ verifyObject(IPathIndex, self._makeOne())
+
+ def test_ctor(self):
+ index = self._makeOne()
+ self.assertEqual(index.id, 'path')
+ self.assertEqual(index.operators, ('or', 'and'))
+ self.assertEqual(index.useOperator, 'or')
+ self.assertEqual(len(index), 0)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 0)
+ self.assertEqual(len(index._unindex), 0)
+ self.assertEqual(index._length(), 0)
+
+ def test_getEntryForObject_miss_no_default(self):
+ index = self._makeOne()
+ self.assertEqual(index.getEntryForObject(1234), None)
+
+ def test_getEntryForObject_miss_w_default(self):
+ index = self._makeOne()
+ default = object()
+ self.assertTrue(index.getEntryForObject(1234, default) is default)
+
+ def test_getEntryForObject_hit(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ self.assertEqual(index.getEntryForObject(1), DUMMIES[1].path)
+
+ def test_getIndexSourceNames(self):
+ index = self._makeOne('foo')
+ self.assertEqual(list(index.getIndexSourceNames()),
+ ['foo', 'getPhysicalPath'])
+
+ def test_index_object_broken_path_raises_TypeError(self):
+ index = self._makeOne()
+ doc = Dummy({})
+ self.assertRaises(TypeError, index.index_object, 1, doc)
+
+ def test_index_object_broken_callable(self):
+ index = self._makeOne()
+ doc = Dummy(lambda: self.nonesuch)
+ rc = index.index_object(1, doc)
+ self.assertEqual(rc, 0)
+ self.assertEqual(len(index), 0)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 0)
+ self.assertEqual(len(index._unindex), 0)
+ self.assertEqual(index._length(), 0)
+
+ def test_index_object_at_root(self):
+ index = self._makeOne()
+ doc = Dummy('/xx')
+ rc = index.index_object(1, doc)
+ self.assertEqual(len(index), 1)
+ self.assertEqual(rc, 1)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 1)
+ self.assertEqual(list(index._index['xx'][0]), [1])
+ self.assertEqual(len(index._unindex), 1)
+ self.assertEqual(index._unindex[1], '/xx')
+ self.assertEqual(index._length(), 1)
+
+ def test_index_object_at_root_callable_attr(self):
+ index = self._makeOne()
+ doc = Dummy(lambda: '/xx')
+ rc = index.index_object(1, doc)
+ self.assertEqual(len(index), 1)
+ self.assertEqual(rc, 1)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 1)
+ self.assertEqual(list(index._index['xx'][0]), [1])
+ self.assertEqual(len(index._unindex), 1)
+ self.assertEqual(index._unindex[1], '/xx')
+ self.assertEqual(index._length(), 1)
+
+ def test_index_object_at_root_no_attr_but_getPhysicalPath(self):
+ class Other:
+ def getPhysicalPath(self):
+ return '/xx'
+ index = self._makeOne()
+ doc = Other()
+ rc = index.index_object(1, doc)
+ self.assertEqual(rc, 1)
+ self.assertEqual(len(index), 1)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 1)
+ self.assertEqual(list(index._index['xx'][0]), [1])
+ self.assertEqual(len(index._unindex), 1)
+ self.assertEqual(index._unindex[1], '/xx')
+ self.assertEqual(index._length(), 1)
+
+ def test_index_object_at_root_attr_as_tuple(self):
+ index = self._makeOne()
+ doc = Dummy(('', 'xx'))
+ rc = index.index_object(1, doc)
+ self.assertEqual(rc, 1)
+ self.assertEqual(len(index), 1)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 1)
+ self.assertEqual(list(index._index['xx'][0]), [1])
+ self.assertEqual(len(index._unindex), 1)
+ self.assertEqual(index._unindex[1], '/xx')
+ self.assertEqual(index._length(), 1)
+
+ def test_index_object_strips_empty_path_elements(self):
+ index = self._makeOne()
+ doc = Dummy('////xx//')
+ rc = index.index_object(1, doc)
+ self.assertEqual(rc, 1)
+ self.assertEqual(len(index), 1)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 1)
+ self.assertEqual(list(index._index['xx'][0]), [1])
+ self.assertEqual(len(index._unindex), 1)
+ self.assertEqual(index._unindex[1], '////xx//')
+ self.assertEqual(index._length(), 1)
+
+ def test_index_object_below_root(self):
+ index = self._makeOne()
+ doc = Dummy('/xx/yy/zz')
+ rc = index.index_object(1, doc)
+ self.assertEqual(rc, 1)
+ self.assertEqual(len(index), 1)
+ self.assertEqual(index._depth, 2)
+ self.assertEqual(len(index._index), 3)
+ self.assertEqual(list(index._index['xx'][0]), [1])
+ self.assertEqual(list(index._index['yy'][1]), [1])
+ self.assertEqual(list(index._index['zz'][2]), [1])
+ self.assertEqual(len(index._unindex), 1)
+ self.assertEqual(index._unindex[1], '/xx/yy/zz')
+ self.assertEqual(index._length(), 1)
+
+ def test_index_object_again(self):
+ index = self._makeOne()
+ o = Dummy('/foo/bar')
+ index.index_object(1234, o)
+ self.assertEqual(len(index), 1)
+ self.assertEqual(index.numObjects(), 1)
+ index.index_object(1234, o)
+ self.assertEqual(len(index), 1)
+ self.assertEqual(index.numObjects(), 1)
+
+ def test_unindex_object_nonesuch(self):
+ index = self._makeOne()
+ index.unindex_object( 1234 ) # nothrow
+
+ def test_unindex_object_broken_path(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ index._unindex[1] = "/broken/thing"
+ index.unindex_object(1) # nothrow
+
+ def test_unindex_object_found(self):
+ index = self._makeOne()
+ _populateIndex(index)
+
+ for k in DUMMIES.keys():
+ index.unindex_object(k)
+
+ self.assertEqual(index.numObjects(), 0)
+ self.assertEqual(len(index._index), 0)
+ self.assertEqual(len(index._unindex), 0)
+
+ def test__apply_index_no_match_in_query(self):
+ index = self._makeOne()
+ self.assertEqual(index._apply_index({'foo': 'xxx'}), None)
+
+ def test__apply_index_nonesuch(self):
+ index = self._makeOne()
+ res = index._apply_index({'path': 'xxx'})
+ self.assertEqual(len(res[0]), 0)
+ self.assertEqual(res[1], ('path',))
+
+ def test___apply_index_root_levelO_dict(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ query = {'path': {'query': '/', 'level': 0}}
+ res = index._apply_index(query)
+ self.assertEqual(list(res[0].keys()), range(1,19))
+
+ def test___apply_index_root_levelO_tuple(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ query = {'path': (('/', 0),)}
+ res = index._apply_index(query)
+ self.assertEqual(list(res[0].keys()), range(1,19))
+
+ def test__apply_index_simple(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ tests = [
+ # component, level, expected results
+ ("aa", 0, [1,2,3,4,5,6,7,8,9]),
+ ("aa", 1, [1,2,3,10,11,12] ),
+ ("bb", 0, [10,11,12,13,14,15,16,17,18]),
+ ("bb", 1, [4,5,6,13,14,15]),
+ ("bb/cc", 0, [16,17,18]),
+ ("bb/cc", 1, [6,15]),
+ ("bb/aa", 0, [10,11,12]),
+ ("bb/aa", 1, [4,13]),
+ ("aa/cc", -1, [3,7,8,9,12]),
+ ("bb/bb", -1, [5,13,14,15]),
+ ("18.html", 3, [18]),
+ ("18.html", -1, [18]),
+ ("cc/18.html", -1, [18]),
+ ("cc/18.html", 2, [18]),
+ ]
+
+ for comp, level, expected in tests:
+ for path in [comp, "/"+comp, "/"+comp+"/"]:
+ # Test with the level passed in as separate parameter
+ query = {'path': {'query':path, 'level': level}}
+ res = index._apply_index(query)
+ self.assertEqual(list(res[0].keys()), expected)
+
+ # Test with the level passed in as part of the path parameter
+ query = {'path': ((path, level),)}
+ res = index._apply_index(query)
+ self.assertEqual(list(res[0].keys()), expected)
+
+ def test__apply_index_ComplexOrTests(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ tests = [
+ (['aa','bb'],1,[1,2,3,4,5,6,10,11,12,13,14,15]),
+ (['aa','bb','xx'],1,[1,2,3,4,5,6,10,11,12,13,14,15]),
+ ([('cc',1),('cc',2)],0,[3,6,7,8,9,12,15,16,17,18]),
+ ]
+
+ for lst, level, expected in tests:
+ query = {'path': {'query': lst, 'level': level, 'operator': 'or'}}
+ res = index._apply_index(query)
+ lst = list(res[0].keys())
+ self.assertEqual(lst, expected)
+
+ def test__apply_index_ComplexANDTests(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ tests = [
+ # Path query (as list or (path, level) tuple), level, expected
+ (['aa','bb'], 1, []),
+ ([('aa',0), ('bb',1)], 0, [4,5,6]),
+ ([('aa',0), ('cc',2)], 0, [3,6,9]),
+ ]
+
+ for lst, level, expected in tests:
+ query = {'path': {'query': lst, 'level': level, 'operator': 'and'}}
+ res = index._apply_index(query)
+ lst = list(res[0].keys())
+ self.assertEqual(lst, expected)
+
+ def test__apply_index_QueryPathReturnedInResult(self):
+ index = self._makeOne()
+ index.index_object(1, Dummy("/ff"))
+ index.index_object(2, Dummy("/ff/gg"))
+ index.index_object(3, Dummy("/ff/gg/3.html"))
+ index.index_object(4, Dummy("/ff/gg/4.html"))
+ res = index._apply_index({'path': {'query': '/ff/gg'}})
+ lst = list(res[0].keys())
+ self.assertEqual(lst, [2, 3, 4])
+
+ def test_numObjects_empty(self):
+ index = self._makeOne()
+ self.assertEqual(index.numObjects(), 0)
+
+ def test_numObjects_filled(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ self.assertEqual(index.numObjects(), len(DUMMIES))
+
+ def test_indexSize_empty(self):
+ index = self._makeOne()
+ self.assertEqual(index.indexSize(), 0)
+
+ def test_indexSize_filled(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ self.assertEqual(index.indexSize(), len(DUMMIES))
+
+ def test_indexSize_multiple_items_same_path(self):
+ index = self._makeOne()
+ doc1 = Dummy('/shared')
+ doc2 = Dummy('/shared')
+ index.index_object(1, doc1)
+ index.index_object(2, doc2)
+ self.assertEqual(len(index._index), 1)
+ self.assertEqual(len(index), 2)
+ self.assertEqual(index.numObjects(), 2)
+ self.assertEqual(index.indexSize(), 2)
+
+ def test_clear(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ index.clear()
+ self.assertEqual(len(index), 0)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 0)
+ self.assertEqual(len(index._unindex), 0)
+ self.assertEqual(index._length(), 0)
+
+ def test_hasUniqueValuesFor_miss(self):
+ index = self._makeOne()
+ self.assertFalse(index.hasUniqueValuesFor('miss'))
+
+ def test_hasUniqueValuesFor_hit(self):
+ index = self._makeOne()
+ self.assertTrue(index.hasUniqueValuesFor('path'))
+
+ def test_uniqueValues_empty(self):
+ index = self._makeOne()
+ self.assertEqual(len(list(index.uniqueValues())), 0)
+
+ def test_uniqueValues_miss(self):
+ index = self._makeOne('foo')
+ _populateIndex(index)
+ self.assertEqual(len(list(index.uniqueValues('bar'))), 0)
+
+ def test_uniqueValues_hit(self):
+ index = self._makeOne('foo')
+ _populateIndex(index)
+ self.assertEqual(len(list(index.uniqueValues('foo'))),
+ len(DUMMIES) + 3)
+
+ def test_uniqueValues_hit_w_withLength(self):
+ index = self._makeOne('foo')
+ _populateIndex(index)
+ results = dict(index.uniqueValues('foo', True))
+ self.assertEqual(len(results), len(DUMMIES) + 3)
+ for i in range(1, 19):
+ self.assertEqual(results['%s.html' % i], 1)
+ self.assertEqual(results['aa'],
+ len([x for x in DUMMIES.values() if 'aa' in x.path]))
+ self.assertEqual(results['bb'],
+ len([x for x in DUMMIES.values() if 'bb' in x.path]))
+ self.assertEqual(results['cc'],
+ len([x for x in DUMMIES.values() if 'cc' in x.path]))
+
+ def test_keyForDocument_miss(self):
+ index = self._makeOne()
+ self.assertEqual(index.keyForDocument(1), None)
+
+ def test_keyForDocument_hit(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ self.assertEqual(index.keyForDocument(1), DUMMIES[1].path)
+
+ def test_documentToKeyMap_empty(self):
+ index = self._makeOne()
+ self.assertEqual(dict(index.documentToKeyMap()), {})
+
+ def test_documentToKeyMap_filled(self):
+ index = self._makeOne()
+ _populateIndex(index)
+ self.assertEqual(dict(index.documentToKeyMap()),
+ dict([(k, v.path) for k, v in DUMMIES.items()]))
+
+ def test_insertEntry_empty_depth_0(self):
+ index = self._makeOne()
+ index.insertEntry('xx', 123, level=0)
+ self.assertEqual(index._depth, 0)
+ self.assertEqual(len(index._index), 1)
+ self.assertEqual(list(index._index['xx'][0]), [123])
+
+ # insertEntry oesn't update the length or the reverse index.
+ self.assertEqual(len(index), 0)
+ self.assertEqual(len(index._unindex), 0)
+ self.assertEqual(index._length(), 0)
+
+ def test_insertEntry_empty_depth_1(self):
+ index = self._makeOne()
+ index.insertEntry('xx', 123, level=0)
+ index.insertEntry('yy', 123, level=1)
+ self.assertEqual(index._depth, 1)
+ self.assertEqual(len(index._index), 2)
+ self.assertEqual(list(index._index['xx'][0]), [123])
+ self.assertEqual(list(index._index['yy'][1]), [123])
+
+ def test_insertEntry_multiple(self):
+ index = self._makeOne()
+ index.insertEntry('xx', 123, level=0)
+ index.insertEntry('yy', 123, level=1)
+ index.insertEntry('aa', 456, level=0)
+ index.insertEntry('bb', 456, level=1)
+ index.insertEntry('cc', 456, level=2)
+ self.assertEqual(index._depth, 2)
+ self.assertEqual(len(index._index), 5)
+ self.assertEqual(list(index._index['xx'][0]), [123])
+ self.assertEqual(list(index._index['yy'][1]), [123])
+ self.assertEqual(list(index._index['aa'][0]), [456])
+ self.assertEqual(list(index._index['bb'][1]), [456])
+ self.assertEqual(list(index._index['cc'][2]), [456])
+
+ def test__search_empty_index_string_query(self):
+ index = self._makeOne()
+ self.assertEqual(list(index._search('/xxx')), [])
+
+ def test__search_empty_index_tuple_query(self):
+ index = self._makeOne()
+ self.assertEqual(list(index._search(('/xxx', 0))), [])
+
+ def test__search_empty_path(self):
+ index = self._makeOne()
+ doc = Dummy('/aa')
+ index.index_object(1, doc)
+ self.assertEqual(list(index._search('/')), [1])
+
+ def test__search_matching_path(self):
+ index = self._makeOne()
+ doc = Dummy('/aa')
+ index.index_object(1, doc)
+ self.assertEqual(list(index._search('/aa')), [1])
+
+ def test__search_mismatched_path(self):
+ index = self._makeOne()
+ doc = Dummy('/aa')
+ index.index_object(1, doc)
+ self.assertEqual(list(index._search('/bb')), [])
+
+ def test__search_w_level_0(self):
+ index = self._makeOne()
+ doc = Dummy('/aa/bb')
+ index.index_object(1, doc)
+ self.assertEqual(list(index._search('aa', 0)), [1])
+ self.assertEqual(list(index._search('aa', 1)), [])
+ self.assertEqual(list(index._search('bb', 1)), [1])
+ self.assertEqual(list(index._search('aa/bb', 0)), [1])
+ self.assertEqual(list(index._search('aa/bb', 1)), [])
Copied: Products.ZCatalog/trunk/src/Products/PluginIndexes/TopicIndex/tests.py (from rev 124720, Products.ZCatalog/trunk/src/Products/PluginIndexes/TopicIndex/tests/testTopicIndex.py)
===================================================================
--- Products.ZCatalog/trunk/src/Products/PluginIndexes/TopicIndex/tests.py (rev 0)
+++ Products.ZCatalog/trunk/src/Products/PluginIndexes/TopicIndex/tests.py 2012-03-25 13:40:03 UTC (rev 124721)
@@ -0,0 +1,88 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""TopicIndex unit tests.
+"""
+
+import unittest
+
+from Products.PluginIndexes.TopicIndex.TopicIndex import TopicIndex
+
+
+class Obj:
+
+ def __init__(self,id,meta_type=''):
+ self.id = id
+ self.meta_type = meta_type
+
+ def getId(self): return self.id
+ def getPhysicalPath(self): return self.id
+
+
+class TestBase(unittest.TestCase):
+
+ def _searchAnd(self,query,expected):
+ return self._search(query,'and',expected)
+
+ def _searchOr(self,query,expected):
+ return self._search(query,'or',expected)
+
+ def _search(self,query,operator,expected):
+ res = self.TI._apply_index({'topic':{'query':query,'operator':operator}})
+ rows = list(res[0].keys())
+ rows.sort()
+ expected.sort()
+ self.assertEqual(rows,expected,query)
+ return rows
+
+
+class TestTopicIndex(TestBase):
+
+ def setUp(self):
+ self.TI = TopicIndex("topic")
+ self.TI.addFilteredSet("doc1","PythonFilteredSet","o.meta_type=='doc1'")
+ self.TI.addFilteredSet("doc2","PythonFilteredSet","o.meta_type=='doc2'")
+
+ self.TI.index_object(0 , Obj('0',))
+ self.TI.index_object(1 , Obj('1','doc1'))
+ self.TI.index_object(2 , Obj('2','doc1'))
+ self.TI.index_object(3 , Obj('3','doc2'))
+ self.TI.index_object(4 , Obj('4','doc2'))
+ self.TI.index_object(5 , Obj('5','doc3'))
+ self.TI.index_object(6 , Obj('6','doc3'))
+
+ def test_interfaces(self):
+ from Products.PluginIndexes.interfaces import ITopicIndex
+ from Products.PluginIndexes.interfaces import IPluggableIndex
+ from zope.interface.verify import verifyClass
+
+ verifyClass(ITopicIndex, TopicIndex)
+ verifyClass(IPluggableIndex, TopicIndex)
+
+ def testOr(self):
+ self._searchOr('doc1',[1,2])
+ self._searchOr(['doc1'],[1,2])
+ self._searchOr('doc2',[3,4]),
+ self._searchOr(['doc2'],[3,4])
+ self._searchOr(['doc1','doc2'], [1,2,3,4])
+
+ def testAnd(self):
+ self._searchAnd('doc1',[1,2])
+ self._searchAnd(['doc1'],[1,2])
+ self._searchAnd('doc2',[3,4])
+ self._searchAnd(['doc2'],[3,4])
+ self._searchAnd(['doc1','doc2'],[])
+
+ def testRemoval(self):
+ self.TI.index_object(1, Obj('1','doc2'))
+ self._searchOr('doc1',[2])
+ self._searchOr('doc2', [1,3,4])
More information about the checkins
mailing list