[Zodb-checkins] CVS: Zope/lib/python/BTrees/tests - testSetOps.py:1.5

Tim Peters tim.one@comcast.net
Sat, 22 Jun 2002 23:21:00 -0400


Update of /cvs-repository/Zope/lib/python/BTrees/tests
In directory cvs.zope.org:/tmp/cvs-serv25581/tests

Modified Files:
	testSetOps.py 
Log Message:
Added many new tests of set operations (they weren't tested at all before).
Noted that the way difference() treats None doesn't match the docs and is
almost certainly wrong.


=== Zope/lib/python/BTrees/tests/testSetOps.py 1.4 => 1.5 ===
 #
 ##############################################################################
-import sys, os, time, random
+import random
 from unittest import TestCase, TestSuite, TextTestRunner, makeSuite
 
-from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet, \
-    union, intersection, difference, weightedUnion, weightedIntersection, \
-    multiunion
+from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet
+from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet
+from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet
+from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet
+
+from BTrees.IIBTree import multiunion
 
 # XXX TODO Needs more tests.
 # This file was created when multiunion was added.  The other set operations
 # don't appear to be tested anywhere yet.
 
+# Subclasses have to set up:
+#     builders - functions to build inputs, taking an optional keys arg
+#     intersection, union, difference - set to the type-correct versions
+class SetResult(TestCase):
+    def setUp(self):
+        self.Akeys = [1,    3,    5, 6   ]
+        self.Bkeys = [   2, 3, 4,    6, 7]
+        self.As = [makeset(self.Akeys) for makeset in self.builders]
+        self.Bs = [makeset(self.Bkeys) for makeset in self.builders]
+        self.emptys = [makeset() for makeset in self.builders]
+
+    # Slow but obviously correct Python implementations of basic ops.
+    def _union(self, x, y):
+        result = list(x.keys())
+        for e in y.keys():
+            if e not in result:
+                result.append(e)
+        result.sort()
+        return result
+
+    def _intersection(self, x, y):
+        result = []
+        ykeys = y.keys()
+        for e in x.keys():
+            if e in ykeys:
+                result.append(e)
+        return result
+
+    def _difference(self, x, y):
+        result = list(x.keys())
+        for e in y.keys():
+            if e in result:
+                result.remove(e)
+        # Difference preserves LHS values.
+        if hasattr(x, "values"):
+            result = [(k, x[k]) for k in result]
+        return result
+
+    def testNone(self):
+        for op in self.union, self.intersection, self.difference:
+            C = op(None, None)
+            self.assert_(C is None)
+
+        for op in self.union, self.intersection:
+            for A in self.As:
+                C = op(A, None)
+                self.assert_(C is A)
+
+                C = op(None, A)
+                self.assert_(C is A)
+
+        # XXX These difference results contradict the docs.  The implementation
+        # XXX is almost certainly wrong, but can we change it?
+        for A in self.As:
+            C = self.difference(A, None)
+            self.assert_(C is None)
+
+            C = self.difference(None, A)
+            self.assert_(C is None)
+
+    def testEmptyUnion(self):
+        for A in self.As:
+            for E in self.emptys:
+                C = self.union(A, E)
+                self.assert_(not hasattr(C, "values"))
+                self.assertEqual(list(C), self.Akeys)
+
+                C = self.union(E, A)
+                self.assert_(not hasattr(C, "values"))
+                self.assertEqual(list(C), self.Akeys)
+
+    def testEmptyIntersection(self):
+        for A in self.As:
+            for E in self.emptys:
+                C = self.intersection(A, E)
+                self.assert_(not hasattr(C, "values"))
+                self.assertEqual(list(C), [])
+
+                C = self.intersection(E, A)
+                self.assert_(not hasattr(C, "values"))
+                self.assertEqual(list(C), [])
+
+    def testEmptyDifference(self):
+        for A in self.As:
+            for E in self.emptys:
+                C = self.difference(A, E)
+                # Difference preserves LHS values.
+                self.assertEqual(hasattr(C, "values"), hasattr(A, "values"))
+                if hasattr(A, "values"):
+                    self.assertEqual(list(C.items()), list(A.items()))
+                else:
+                    self.assertEqual(list(C), self.Akeys)
+
+                C = self.difference(E, A)
+                self.assertEqual(hasattr(C, "values"), hasattr(E, "values"))
+                self.assertEqual(list(C.keys()), [])
+
+    def testUnion(self):
+        inputs = self.As + self.Bs
+        for A in inputs:
+            for B in inputs:
+                C = self.union(A, B)
+                self.assert_(not hasattr(C, "values"))
+                self.assertEqual(list(C), self._union(A, B))
+
+    def testIntersection(self):
+        inputs = self.As + self.Bs
+        for A in inputs:
+            for B in inputs:
+                C = self.intersection(A, B)
+                self.assert_(not hasattr(C, "values"))
+                self.assertEqual(list(C), self._intersection(A, B))
+
+    def testDifference(self):
+        inputs = self.As + self.Bs
+        for A in inputs:
+            for B in inputs:
+                C = self.difference(A, B)
+                # Difference preserves LHS values.
+                self.assertEqual(hasattr(C, "values"), hasattr(A, "values"))
+                want = self._difference(A, B)
+                if hasattr(A, "values"):
+                    self.assertEqual(list(C.items()), want)
+                else:
+                    self.assertEqual(list(C), want)
+
+# Given a mapping builder (IIBTree, OOBucket, etc), return a function
+# that builds an object of that type given only a list of keys.
+def makeBuilder(mapbuilder):
+    def result(keys=[], mapbuilder=mapbuilder):
+        return mapbuilder(zip(keys, keys))
+    return result
+
+class PureII(SetResult):
+    from BTrees.IIBTree import union, intersection, difference
+    builders = IISet, IITreeSet, makeBuilder(IIBTree), makeBuilder(IIBucket)
+
+class PureIO(SetResult):
+    from BTrees.IOBTree import union, intersection, difference
+    builders = IOSet, IOTreeSet, makeBuilder(IOBTree), makeBuilder(IOBucket)
+
+class PureOO(SetResult):
+    from BTrees.OOBTree import union, intersection, difference
+    builders = OOSet, OOTreeSet, makeBuilder(OOBTree), makeBuilder(OOBucket)
+
+class PureOI(SetResult):
+    from BTrees.OIBTree import union, intersection, difference
+    builders = OISet, OITreeSet, makeBuilder(OIBTree), makeBuilder(OIBucket)
+
 class TestMultiUnion(TestCase):
 
     def testEmpty(self):
@@ -72,6 +224,7 @@
     def testFunkyKeyIteration(self):
         # The internal set iteration protocol allows "iterating over" a
         # a single key as if it were a set.
+        from BTrees.IIBTree import union
         N = 100
         slow = IISet()
         for i in range(N):
@@ -83,10 +236,14 @@
         self.assertEqual(list(fast.keys()), range(N))
 
 def test_suite():
-    return makeSuite(TestMultiUnion, 'test')
+    s = TestSuite()
+    for klass in (TestMultiUnion,
+                  PureII, PureIO, PureOI, PureOO):
+        s.addTest(makeSuite(klass))
+    return s
 
 def main():
-  TextTestRunner().run(test_suite())
+    TextTestRunner().run(test_suite())
 
 if __name__ == '__main__':
     main()