File: Subject-Fix-Python-test-cases-with-Python-3.14.patch

package info (click to toggle)
subversion 1.14.5-5
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 80,512 kB
  • sloc: ansic: 1,039,361; python: 140,270; cpp: 24,862; java: 24,547; ruby: 12,312; lisp: 7,619; sh: 7,414; perl: 7,010; sql: 1,686; makefile: 1,191; xml: 577
file content (241 lines) | stat: -rw-r--r-- 10,991 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
From: James McCoy <jamessan@debian.org>
Date: Mon, 13 Oct 2025 22:05:45 -0400
Subject: Subject: Fix Python test cases with Python 3.14

Python 3.14 changes the way reference counting works, which causes some
tests to fail. Backport upstream revisions r1926575 and r1927715 to fix
the tests.

Forwarded: not-needed
Signed-off-by: James McCoy <jamessan@debian.org>
---
 subversion/bindings/swig/python/tests/mergeinfo.py | 52 +++++++++++++++++++++-
 .../bindings/swig/python/tests/repository.py       | 49 ++++++++++++++------
 subversion/bindings/swig/python/tests/utils.py     | 10 +++++
 3 files changed, 95 insertions(+), 16 deletions(-)

diff --git a/subversion/bindings/swig/python/tests/mergeinfo.py b/subversion/bindings/swig/python/tests/mergeinfo.py
index 873fc52..739c0e5 100644
--- a/subversion/bindings/swig/python/tests/mergeinfo.py
+++ b/subversion/bindings/swig/python/tests/mergeinfo.py
@@ -18,7 +18,7 @@
 # under the License.
 #
 #
-import unittest, os, sys, gc
+import unittest, os, sys, weakref, gc
 from svn import core, repos, fs
 import utils
 
@@ -125,6 +125,9 @@ class SubversionMergeinfoTestCase(unittest.TestCase):
       }
     self.compare_mergeinfo_catalogs(mergeinfo, expected_mergeinfo)
 
+  @unittest.skipIf(utils.HAS_DEFERRED_REFCOUNT,
+                   "Reference counting tests skipped because of deferred "
+                   "reference counting")
   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."""
@@ -138,7 +141,8 @@ class SubversionMergeinfoTestCase(unittest.TestCase):
         # ....and now 3 (incref during iteration of each range object)
 
         refcount = sys.getrefcount(r)
-        # ....and finally, 4 (getrefcount() also increfs)
+        # ....and finally, 4 (getrefcount() also increfs, unless deferred 
+        #                     reference counting)
         expected = 4
 
         # Note: if path and index are not '/trunk' and 0 respectively, then
@@ -150,8 +154,49 @@ class SubversionMergeinfoTestCase(unittest.TestCase):
           "cause: incorrect Py_INCREF/Py_DECREF usage in libsvn_swig_py/"
           "swigutil_py.c." % (expected, refcount, path, i)))
 
+  def test_mergeinfo_leakage__incorrect_range_t_weakrefs(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)
+    merge_range_refdict = weakref.WeakValueDictionary()
+    merge_range_indexes = []
+    n_merge_range = 0 
+    for (path, rangelist) in core._as_list(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)
+
+        idx = (path, i)
+        merge_range_refdict[idx] = r
+        merge_range_indexes.append(idx) 
+        n_merge_range += 1
+
+        # 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.
+
+    del rangelist, r
+    gc.collect()
+    # Now (strong) reference count of all svn_merge_range_t should be 1
+    # again and those objects should not be removed yet.
+    for idx in merge_range_indexes:
+      self.assertIn(idx, merge_range_refdict, (
+          "Refarence count error on svn_merge_info_t object for "
+          "(path: %s, index: %d). It should still exists because "
+          "mergeinfo holds its reference, but after GC, it already "
+          "removed." % idx))
     del mergeinfo
     gc.collect()
+    if merge_range_refdict:
+      # certainly memory leak, but we want to listing up leaked objects
+      # before raise an assertion error.  
+      self.assertFalse(merge_range_refdict,
+         "Memory leak! All svn_merge_range_t object holded "
+         "by mergeinfo object should be removed, but at least "
+         "one object still alive.")
 
   def test_mergeinfo_leakage__lingering_range_t_objects_after_del(self):
     """Ensure that there are no svn_merge_range_t objects being tracked by
@@ -162,6 +207,9 @@ class SubversionMergeinfoTestCase(unittest.TestCase):
        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)
+    lingering = get_svn_merge_range_t_objects()
+    self.assertNotEqual(lingering, list())
+    del lingering
     del mergeinfo
     gc.collect()
     lingering = get_svn_merge_range_t_objects()
diff --git a/subversion/bindings/swig/python/tests/repository.py b/subversion/bindings/swig/python/tests/repository.py
index e764ec5..d28f1ad 100644
--- a/subversion/bindings/swig/python/tests/repository.py
+++ b/subversion/bindings/swig/python/tests/repository.py
@@ -87,15 +87,32 @@ class DumpStreamParser(repos.ParseFns3):
 
 class BatonCollector(repos.ChangeCollector):
   """A ChangeCollector with collecting batons, too"""
+
   def __init__(self, fs_ptr, root, pool=None, notify_cb=None):
+
+    def get_expected_baton_refcount():
+      """determine expected refcount of batons within a batoun_tuple,
+         by using dumy object"""
+      self.open_root(-1, None)
+      for baton_tuple in self.batons: 
+        rc = sys.getrefcount(baton_tuple[2])
+        break
+      return rc
+    
     repos.ChangeCollector.__init__(self, fs_ptr, root, pool, notify_cb)
-    self.batons = []
     self.close_called = False
     self.abort_called = False
+    # temporary values for get_expected_baton_refcount
+    self.batons = []
+    self.expected_baton_refcount = 0
+    # determin expected_baton_refcount
+    self.expected_baton_refcount = get_expected_baton_refcount()
+    # re-initialize the values after calling get_expected_baton_refcount()
+    self.batons = []
 
   def open_root(self, base_revision, dir_pool=None):
     bt = repos.ChangeCollector.open_root(self, base_revision, dir_pool)
-    self.batons.append((b'dir baton', b'', bt, sys.getrefcount(bt)))
+    self.batons.append((b'dir baton', b'', bt, self.expected_baton_refcount))
     return bt
 
   def add_directory(self, path, parent_baton,
@@ -104,14 +121,14 @@ class BatonCollector(repos.ChangeCollector):
                                              copyfrom_path,
                                              copyfrom_revision,
                                              dir_pool)
-    self.batons.append((b'dir baton', path, bt, sys.getrefcount(bt)))
+    self.batons.append((b'dir baton', path, bt, self.expected_baton_refcount))
     return bt
 
   def open_directory(self, path, parent_baton, base_revision,
                      dir_pool=None):
     bt = repos.ChangeCollector.open_directory(self, path, parent_baton,
                                               base_revision, dir_pool)
-    self.batons.append((b'dir baton', path, bt, sys.getrefcount(bt)))
+    self.batons.append((b'dir baton', path, bt, self.expected_baton_refcount))
     return bt
 
   def add_file(self, path, parent_baton,
@@ -119,13 +136,13 @@ class BatonCollector(repos.ChangeCollector):
     bt = repos.ChangeCollector.add_file(self, path, parent_baton,
                                         copyfrom_path, copyfrom_revision,
                                         file_pool)
-    self.batons.append((b'file baton', path, bt, sys.getrefcount(bt)))
+    self.batons.append((b'file baton', path, bt, self.expected_baton_refcount))
     return bt
 
   def open_file(self, path, parent_baton, base_revision, file_pool=None):
     bt = repos.ChangeCollector.open_file(self, path, parent_baton,
                                          base_revision, file_pool)
-    self.batons.append((b'file baton', path, bt, sys.getrefcount(bt)))
+    self.batons.append((b'file baton', path, bt, self.expected_baton_refcount))
     return bt
 
   def close_edit(self, pool=None):
@@ -429,29 +446,33 @@ class SubversionRepositoryTestCase(unittest.TestCase):
     root = fs.revision_root(self.fs, self.rev)
     editor = BatonCollector(self.fs, root)
     e_ptr, e_baton = delta.make_editor(editor)
+    refcount_at_first = sys.getrefcount(e_ptr)
     repos.replay(root, e_ptr, e_baton)
-    for baton in editor.batons:
-      self.assertEqual(sys.getrefcount(baton[2]), 2,
+    for baton_tuple in editor.batons:
+      # baton_tuple: 4-tuple(baton_type: bytes, node: bytes, bt: baton,
+      #                      expected_refcount_of_bt: int)
+      self.assertEqual(sys.getrefcount(baton_tuple[2]), baton_tuple[3],
                        "leak on baton %s after replay without errors"
-                       % repr(baton))
+                       % repr(baton_tuple))
     del e_baton
-    self.assertEqual(sys.getrefcount(e_ptr), 2,
+    self.assertEqual(sys.getrefcount(e_ptr), refcount_at_first,
                      "leak on editor baton after replay without errors")
 
     editor = BatonCollectorErrorOnClose(self.fs, root,
                                         error_path=b'branches/v1x')
     e_ptr, e_baton = delta.make_editor(editor)
+    refcount_at_first = sys.getrefcount(e_ptr)
     self.assertRaises(SubversionException, repos.replay, root, e_ptr, e_baton)
     batons = editor.batons
     # As svn_repos_replay calls neither close_edit callback nor abort_edit
     # if an error has occured during processing, references of Python objects
     # in decendant batons may live until e_baton is deleted.
     del e_baton
-    for baton in batons:
-      self.assertEqual(sys.getrefcount(baton[2]), 2,
+    for baton_tuple in batons:
+      self.assertEqual(sys.getrefcount(baton_tuple[2]), baton_tuple[3],
                        "leak on baton %s after replay with an error"
-                       % repr(baton))
-    self.assertEqual(sys.getrefcount(e_ptr), 2,
+                       % repr(baton_tuple))
+    self.assertEqual(sys.getrefcount(e_ptr), refcount_at_first,
                      "leak on editor baton after replay with an error")
 
   def test_delta_editor_apply_textdelta_handler_refcount(self):
diff --git a/subversion/bindings/swig/python/tests/utils.py b/subversion/bindings/swig/python/tests/utils.py
index 09061a6..3e29992 100644
--- a/subversion/bindings/swig/python/tests/utils.py
+++ b/subversion/bindings/swig/python/tests/utils.py
@@ -95,3 +95,13 @@ def codecs_eq(a, b):
 
 def is_defaultencoding_utf8():
   return codecs_eq(sys.getdefaultencoding(), 'utf-8')
+
+def get_holded_refcount_by_getrefcount():
+  "get refcount holded by sys.getrefcount() if its arg is a local variable"
+  a = []
+  rv = sys.getrefcount(a) - 1
+  return rv
+
+HAS_DEFERRED_REFCOUNT = not get_holded_refcount_by_getrefcount()
+
+del get_holded_refcount_by_getrefcount