Author: danielsh Date: Tue Jan 24 15:30:06 2012 New Revision: 1235296 URL: http://svn.apache.org/viewvc?rev=1235296&view=rev Log: Follow-up to r1235264: add two new tests to aid in detecting memory leaks. They essentially test the same thing in two different ways; if one fails, the other *should* fail. (Of course, if one fails and the other does not -- that's just as valuable, diagnostically, and points to an issue with the 'automatic pool memory management' logic.)
Patch by: Trent Nelson <[email protected]> Tested on: FreeBSD, OS X, Windows. (split from r1235264 by me) * subversion/bindings/swig/python/tests/mergeinfo.py: (get_svn_merge_range_t_objects): New helper method that returns a list of any 'svn_merge_range_t' objects being tracked by the garbage collector. (SubversionMergeinfoTestCase): (test_mergeinfo_leakage__incorrect_range_t_refcounts): New test. Verify svn_merge_range_t objects returned from svn_mergeinfo_parse() have the correct refcounts set. (test_mergeinfo_leakage__lingering_range_t_objects_after_del): New test. Verify that there are no lingering svn_merge_range_t objects after we explicitly delete the results returned from svn_mergeinfo_parse(). Modified: subversion/trunk/subversion/bindings/swig/python/tests/mergeinfo.py Modified: subversion/trunk/subversion/bindings/swig/python/tests/mergeinfo.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/swig/python/tests/mergeinfo.py?rev=1235296&r1=1235295&r2=1235296&view=diff ============================================================================== --- subversion/trunk/subversion/bindings/swig/python/tests/mergeinfo.py (original) +++ subversion/trunk/subversion/bindings/swig/python/tests/mergeinfo.py Tue Jan 24 15:30:06 2012 @@ -18,7 +18,7 @@ # under the License. # # -import unittest, os +import unittest, os, sys, gc, itertools from svn import core, repos, fs import utils @@ -29,6 +29,15 @@ class RevRange: self.start = start self.end = end +def get_svn_merge_range_t_objects(): + """Returns a list 'svn_merge_range_t' objects being tracked by the + garbage collector, used for detecting memory leaks.""" + return [ + o for o in gc.get_objects() + if hasattr(o, '__class__') and + o.__class__.__name__ == 'svn_merge_range_t' + ] + class SubversionMergeinfoTestCase(unittest.TestCase): """Test cases for mergeinfo""" @@ -116,6 +125,53 @@ class SubversionMergeinfoTestCase(unitte } self.compare_mergeinfo_catalogs(mergeinfo, expected_mergeinfo) + def test_mergeinfo_leakage__incorrect_range_t_refcounts(self): + """Ensure that the ref counts on svn_merge_range_t objects returned by + svn_mergeinfo_parse() are correct.""" + # When reference counting is working properly, each svn_merge_range_t in + # the returned mergeinfo will have a ref count of 1... + mergeinfo = core.svn_mergeinfo_parse(self.TEXT_MERGEINFO1) + for (path, rangelist) in mergeinfo.items(): + # ....and now 2 (incref during iteration of rangelist) + + for (i, r) in enumerate(rangelist): + # ....and now 3 (incref during iteration of each range object) + + refcount = sys.getrefcount(r) + # ....and finally, 4 (getrefcount() also increfs) + expected = 4 + + # Note: if path and index are not '/trunk' and 0 respectively, then + # only some of the range objects are leaking, which is, as far as + # leaks go, even more impressive. + self.assertEquals(refcount, expected, ( + "Memory leak! Expected a ref count of %d for svn_merge_range_t " + "object, but got %d instead (path: %s, index: %d). Probable " + "cause: incorrect Py_INCREF/Py_DECREF usage in libsvn_swig_py/" + "swigutil_py.c." % (expected, refcount, path, i))) + + del mergeinfo + gc.collect() + + def test_mergeinfo_leakage__lingering_range_t_objects_after_del(self): + """Ensure that there are no svn_merge_range_t objects being tracked by + the garbage collector after we explicitly `del` the results returned + by svn_mergeinfo_parse(). We disable gc before the svn_mergeinfo_parse + call and force an explicit collection cycle straight after the `del`; + if our reference counts are correct, the allocated svn_merge_range_t + objects will be garbage collected and thus, not appear in the list of + objects returned by gc.get_objects().""" + mergeinfo = core.svn_mergeinfo_parse(self.TEXT_MERGEINFO1) + del mergeinfo + gc.collect() + lingering = get_svn_merge_range_t_objects() + self.assertEquals(lingering, list(), ( + "Memory leak! Found lingering svn_merge_range_t objects left over from " + "our call to svn_mergeinfo_parse(), even though we explicitly deleted " + "the returned mergeinfo object. Probable cause: incorrect Py_INCREF/" + "Py_DECREF usage in libsvn_swig_py/swigutil_py.c. Lingering objects:\n" + "%s" % lingering)) + def inspect_mergeinfo_dict(self, mergeinfo, merge_source, nbr_rev_ranges): rangelist = mergeinfo.get(merge_source) self.inspect_rangelist_tuple(rangelist, nbr_rev_ranges)
