SVN: ZODB/branches/adamg-btree-keeps-reference/src/BTrees/tests/testBTrees.py added a test that shows that the Btree keeps a reference to objects

Adam Groszer <[email protected]>
Newsgroups gmane.comp.python.zope.zodb.cvs
Message-ID <20090826165700.501619417B__30829.5981995345$1251306522$gmane$org@cvs.zope.org>
Log message for revision 103237:
  added a test that shows that the Btree keeps a reference to objects

Changed:
  U   ZODB/branches/adamg-btree-keeps-reference/src/BTrees/tests/testBTrees.py

-=-
Modified: ZODB/branches/adamg-btree-keeps-reference/src/BTrees/tests/testBTrees.py
===================================================================
--- ZODB/branches/adamg-btree-keeps-reference/src/BTrees/tests/testBTrees.py	2009-08-26 16:54:53 UTC (rev 103236)
+++ ZODB/branches/adamg-btree-keeps-reference/src/BTrees/tests/testBTrees.py	2009-08-26 16:56:59 UTC (rev 103237)
@@ -11,6 +11,7 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
+import gc
 import pickle
 import random
 import StringIO
@@ -1569,7 +1570,23 @@
             # at some unrelated line.
             del t   # trigger destructor
 
+LP294788_ids = {}
 
+class ToBeDeleted(object):
+    def __init__(self, id):
+        assert type(id) is int #we don't want to store any object ref here
+        self.id = id
+
+        global LP294788_ids
+        LP294788_ids[id] = 1
+
+    def __del__(self):
+        global LP294788_ids
+        #this might raise KeyError if the LP294788_ids dict is cleaned before
+        #the object gets GC'ed, but this should not happen
+        del LP294788_ids[self.id]
+
+
 class BugFixes(TestCase):
 
     # Collector 1843.  Error returns were effectively ignored in
@@ -1582,7 +1599,164 @@
         # This one used to segfault.
         self.assertRaises(TypeError, t.keys, 0, "")
 
+    def test_LP294788(self):
+        # https://bugs.launchpad.net/bugs/294788
+        # BTree keeps some deleted objects referenced
 
+        # The logic here together with the ToBeDeleted class is that
+        # a separate reference dict is populated on object creation
+        # and removed in __del__
+        # That means what's left in the reference dict is never GC'ed
+        # therefore referenced somewhere
+        # To simulate real life, some random data is used to exercise the tree
+
+        t = OOBTree()
+
+        random.seed('OOBTree')
+
+        global LP294788_ids
+
+        # /// BTree keys are integers, value is an object
+        LP294788_ids = {}
+        ids = {}
+        for i in xrange(1024):
+            if random.random() > 0.1:
+                #add
+                id = None
+                while id is None or ids.has_key(id):
+                    id = random.randint(0,1000000)
+
+                ids[id] = 1
+                t[id] = ToBeDeleted(id)
+            else:
+                #del
+                id = random.choice(ids.keys())
+                del t[id]
+                del ids[id]
+
+        ids = ids.keys()
+        random.shuffle(ids)
+        for id in ids:
+            del t[id]
+        ids = None
+
+        #to be on the safe side run a full GC
+        gc.collect()
+
+        #print LP294788_ids
+
+        self.assertEqual(len(t), 0)
+        self.assertEqual(len(LP294788_ids), 0)
+        # \\\
+
+        # /// BTree keys are integers, value is a tuple having an object
+        LP294788_ids = {}
+        ids = {}
+        for i in xrange(1024):
+            if random.random() > 0.1:
+                #add
+                id = None
+                while id is None or ids.has_key(id):
+                    id = random.randint(0,1000000)
+
+                ids[id] = 1
+                t[id] = (id, ToBeDeleted(id), u'somename')
+            else:
+                #del
+                id = random.choice(ids.keys())
+                del t[id]
+                del ids[id]
+
+        ids = ids.keys()
+        random.shuffle(ids)
+        for id in ids:
+            del t[id]
+        ids = None
+
+        #to be on the safe side run a full GC
+        gc.collect()
+
+        #print LP294788_ids
+
+        self.assertEqual(len(t), 0)
+        self.assertEqual(len(LP294788_ids), 0)
+        # \\\
+
+
+        # /// BTree keys are objects, value is an int
+        t = OOBTree()
+        LP294788_ids = {}
+        ids = {}
+        for i in xrange(1024):
+            if random.random() > 0.1:
+                #add
+                id = None
+                while id is None or ids.has_key(id):
+                    id = random.randint(0,1000000)
+
+                obj = ToBeDeleted(id)
+                ids[obj] = 1
+                t[obj] = 1
+            else:
+                #del
+                obj = random.choice(ids.keys())
+                del ids[obj]
+                del t[obj]
+
+        ids = ids.keys()
+        random.shuffle(ids)
+        for id in ids:
+            del t[id]
+        #release all refs
+        ids = None
+
+        #to be on the safe side run a full GC
+        gc.collect()
+
+        #print LP294788_ids
+
+        self.assertEqual(len(t), 0)
+        self.assertEqual(len(LP294788_ids), 0)
+        # \\\
+
+        # /// BTree keys are tuples having objects, value is an int
+        t = OOBTree()
+        LP294788_ids = {}
+        ids = {}
+        for i in xrange(1024):
+            if random.random() > 0.1:
+                #add
+                id = None
+                while id is None or ids.has_key(id):
+                    id = random.randint(0,1000000)
+
+                key = (id, ToBeDeleted(id), u'somename')
+                ids[key] = 1
+                t[key] = 1
+            else:
+                #del
+                key = random.choice(ids.keys())
+                del ids[key]
+                del t[key]
+
+        ids = ids.keys()
+        random.shuffle(ids)
+        for id in ids:
+            del t[id]
+        #release all refs
+        ids = None
+
+        #to be on the safe side run a full GC
+        gc.collect()
+
+        #print LP294788_ids
+
+        self.assertEqual(len(t), 0)
+        self.assertEqual(len(LP294788_ids), 0)
+        # \\\
+
+
+
 class IIBTreeTest(BTreeTests):
     def setUp(self):
         self.t = IIBTree()
@@ -1618,7 +1792,7 @@
             self.t = OIBTree()
         def getTwoKeys(self):
             return object(), object()
-    
+
 class LLBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues):
     def setUp(self):
         self.t = LLBTree()
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.