1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
4 from __future__
import absolute_import
12 from gi
.repository
import GObject
13 from gi
.repository
import GIMarshallingTests
16 from gi
.repository
import Regress
21 class StrongRef(object):
22 # A class that behaves like weakref.ref but holds a strong reference.
23 # This allows re-use of the VFuncsBase by swapping out the ObjectRef
24 # class var with either weakref.ref or StrongRef.
26 def __init__(self
, obj
):
33 class VFuncsBase(GIMarshallingTests
.Object
):
34 # Class which generically implements the vfuncs used for reference counting tests
35 # in a way that can be easily sub-classed and modified.
37 #: Object type used by this class for testing
38 #: This can be GObject.Object or GObject.InitiallyUnowned
39 Object
= GObject
.Object
41 #: Reference type used by this class for holding refs to in/out objects.
42 #: This can be set to weakref.ref or StrongRef
43 ObjectRef
= weakref
.ref
46 super(VFuncsBase
, self
).__init
__()
48 #: Hold ref of input or output python wrappers
49 self
.object_ref
= None
51 #: store grefcount of input object
52 self
.in_object_grefcount
= None
53 self
.in_object_is_floating
= None
55 def do_vfunc_return_object_transfer_none(self
):
56 # Return an object but keep a python reference to it.
58 self
.object_ref
= self
.ObjectRef(obj
)
61 def do_vfunc_return_object_transfer_full(self
):
62 # Return an object and hand off the reference to the caller.
64 self
.object_ref
= self
.ObjectRef(obj
)
67 def do_vfunc_out_object_transfer_none(self
):
68 # Same as do_vfunc_return_object_transfer_none but the pygi
69 # internals convert the return here into an out arg.
71 self
.object_ref
= self
.ObjectRef(obj
)
74 def do_vfunc_out_object_transfer_full(self
):
75 # Same as do_vfunc_return_object_transfer_full but the pygi
76 # internals convert the return here into an out arg.
78 self
.object_ref
= self
.ObjectRef(obj
)
81 def do_vfunc_in_object_transfer_none(self
, obj
):
82 # 'obj' will have a python wrapper as well as still held
84 self
.object_ref
= self
.ObjectRef(obj
)
85 self
.in_object_grefcount
= obj
.__grefcount
__
86 self
.in_object_is_floating
= obj
.is_floating()
88 def do_vfunc_in_object_transfer_full(self
, obj
):
89 # 'obj' will now be owned by the Python GObject wrapper.
90 # When obj goes out of scope and is collected, the GObject
91 # should also be fully released.
92 self
.object_ref
= self
.ObjectRef(obj
)
93 self
.in_object_grefcount
= obj
.__grefcount
__
94 self
.in_object_is_floating
= obj
.is_floating()
97 class TestVFuncsWithObjectArg(unittest
.TestCase
):
98 # Basic set of tests which work on non-floating objects which python does
99 # not keep an additional reference of.
101 class VFuncs(VFuncsBase
):
102 # Object for testing non-floating objects without holding any refs.
103 Object
= GObject
.Object
104 ObjectRef
= weakref
.ref
106 def test_vfunc_self_arg_ref_count(self
):
107 # Check to make sure vfunc "self" arguments don't leak.
108 vfuncs
= self
.VFuncs()
109 vfuncs_ref
= weakref
.ref(vfuncs
)
110 vfuncs
.get_ref_info_for_vfunc_return_object_transfer_full() # Use any vfunc to test this.
113 if hasattr(sys
, "getrefcount"):
114 self
.assertEqual(sys
.getrefcount(vfuncs
), 2)
115 self
.assertEqual(vfuncs
.__grefcount
__, 1)
119 self
.assertTrue(vfuncs_ref() is None)
121 def test_vfunc_return_object_transfer_none(self
):
122 # This tests a problem case where the vfunc returns a GObject owned solely by Python
123 # but the argument is marked as transfer-none.
124 # In this case pygobject marshaling adds an additional ref and gives a warning
125 # of a potential leak. If this occures it is really a bug in the underlying library
126 # but pygobject tries to react to this in a reasonable way.
127 vfuncs
= self
.VFuncs()
128 with warnings
.catch_warnings(record
=True) as warn
:
129 warnings
.simplefilter('always')
130 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_none()
131 if hasattr(sys
, "getrefcount"):
132 self
.assertTrue(issubclass(warn
[0].category
, RuntimeWarning))
134 # The ref count of the GObject returned to the caller (get_ref_info_for_vfunc_return_object_transfer_none)
135 # should be a single floating ref
136 if hasattr(sys
, "getrefcount"):
137 self
.assertEqual(ref_count
, 1)
138 self
.assertFalse(is_floating
)
141 self
.assertTrue(vfuncs
.object_ref() is None)
143 def test_vfunc_out_object_transfer_none(self
):
144 # Same as above except uses out arg instead of return
145 vfuncs
= self
.VFuncs()
146 with warnings
.catch_warnings(record
=True) as warn
:
147 warnings
.simplefilter('always')
148 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_none()
149 if hasattr(sys
, "getrefcount"):
150 self
.assertTrue(issubclass(warn
[0].category
, RuntimeWarning))
152 if hasattr(sys
, "getrefcount"):
153 self
.assertEqual(ref_count
, 1)
154 self
.assertFalse(is_floating
)
157 self
.assertTrue(vfuncs
.object_ref() is None)
159 def test_vfunc_return_object_transfer_full(self
):
160 vfuncs
= self
.VFuncs()
161 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_return_object_transfer_full()
163 # The vfunc caller receives full ownership of a single ref which should not
165 if hasattr(sys
, "getrefcount"):
166 self
.assertEqual(ref_count
, 1)
167 self
.assertFalse(is_floating
)
170 self
.assertTrue(vfuncs
.object_ref() is None)
172 def test_vfunc_out_object_transfer_full(self
):
173 # Same as above except uses out arg instead of return
174 vfuncs
= self
.VFuncs()
175 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_full()
177 if hasattr(sys
, "getrefcount"):
178 self
.assertEqual(ref_count
, 1)
179 self
.assertFalse(is_floating
)
182 self
.assertTrue(vfuncs
.object_ref() is None)
184 def test_vfunc_in_object_transfer_none(self
):
185 vfuncs
= self
.VFuncs()
186 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_in_object_transfer_none(self
.VFuncs
.Object
)
189 self
.assertEqual(vfuncs
.in_object_grefcount
, 2) # initial + python wrapper
190 self
.assertFalse(vfuncs
.in_object_is_floating
)
192 if hasattr(sys
, "getrefcount"):
193 self
.assertEqual(ref_count
, 1) # ensure python wrapper released
194 self
.assertFalse(is_floating
)
198 self
.assertTrue(vfuncs
.object_ref() is None)
200 def test_vfunc_in_object_transfer_full(self
):
201 vfuncs
= self
.VFuncs()
202 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_in_object_transfer_full(self
.VFuncs
.Object
)
206 # python wrapper should take sole ownership of the gobject
207 self
.assertEqual(vfuncs
.in_object_grefcount
, 1)
208 self
.assertFalse(vfuncs
.in_object_is_floating
)
210 # ensure python wrapper took ownership and released, after vfunc was complete
211 if hasattr(sys
, "getrefcount"):
212 self
.assertEqual(ref_count
, 0)
213 self
.assertFalse(is_floating
)
217 self
.assertTrue(vfuncs
.object_ref() is None)
220 class TestVFuncsWithFloatingArg(unittest
.TestCase
):
221 # All tests here work with a floating object by using InitiallyUnowned as the argument
223 class VFuncs(VFuncsBase
):
224 # Object for testing non-floating objects without holding any refs.
225 Object
= GObject
.InitiallyUnowned
226 ObjectRef
= weakref
.ref
228 @unittest.skipUnless(hasattr(sys
, "getrefcount"), "refcount specific")
229 def test_vfunc_return_object_transfer_none_with_floating(self
):
230 # Python is expected to return a single floating reference without warning.
231 vfuncs
= self
.VFuncs()
232 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_return_object_transfer_none()
234 # The ref count of the GObject returned to the caller (get_ref_info_for_vfunc_return_object_transfer_none)
235 # should be a single floating ref
236 self
.assertEqual(ref_count
, 1)
237 self
.assertTrue(is_floating
)
240 self
.assertTrue(vfuncs
.object_ref() is None)
242 @unittest.skipUnless(hasattr(sys
, "getrefcount"), "refcount specific")
243 def test_vfunc_out_object_transfer_none_with_floating(self
):
244 # Same as above except uses out arg instead of return
245 vfuncs
= self
.VFuncs()
246 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_none()
248 self
.assertEqual(ref_count
, 1)
249 self
.assertTrue(is_floating
)
252 self
.assertTrue(vfuncs
.object_ref() is None)
254 def test_vfunc_return_object_transfer_full_with_floating(self
):
255 vfuncs
= self
.VFuncs()
256 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_return_object_transfer_full()
258 # The vfunc caller receives full ownership of a single ref.
259 if hasattr(sys
, "getrefcount"):
260 self
.assertEqual(ref_count
, 1)
261 self
.assertFalse(is_floating
)
264 self
.assertTrue(vfuncs
.object_ref() is None)
266 def test_vfunc_out_object_transfer_full_with_floating(self
):
267 # Same as above except uses out arg instead of return
268 vfuncs
= self
.VFuncs()
269 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_full()
271 if hasattr(sys
, "getrefcount"):
272 self
.assertEqual(ref_count
, 1)
273 self
.assertFalse(is_floating
)
276 self
.assertTrue(vfuncs
.object_ref() is None)
278 def test_vfunc_in_object_transfer_none_with_floating(self
):
279 vfuncs
= self
.VFuncs()
280 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_in_object_transfer_none(self
.VFuncs
.Object
)
284 # python wrapper should maintain the object as floating and add an additional ref
285 self
.assertEqual(vfuncs
.in_object_grefcount
, 2)
286 self
.assertTrue(vfuncs
.in_object_is_floating
)
288 # vfunc caller should only have a single floating ref after the vfunc finishes
289 if hasattr(sys
, "getrefcount"):
290 self
.assertEqual(ref_count
, 1)
291 self
.assertTrue(is_floating
)
295 self
.assertTrue(vfuncs
.object_ref() is None)
297 def test_vfunc_in_object_transfer_full_with_floating(self
):
298 vfuncs
= self
.VFuncs()
299 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_in_object_transfer_full(self
.VFuncs
.Object
)
303 # python wrapper sinks and owns the gobject
304 self
.assertEqual(vfuncs
.in_object_grefcount
, 1)
305 self
.assertFalse(vfuncs
.in_object_is_floating
)
307 # ensure python wrapper took ownership and released
308 if hasattr(sys
, "getrefcount"):
309 self
.assertEqual(ref_count
, 0)
310 self
.assertFalse(is_floating
)
314 self
.assertTrue(vfuncs
.object_ref() is None)
317 class TestVFuncsWithHeldObjectArg(unittest
.TestCase
):
318 # Same tests as TestVFuncsWithObjectArg except we hold
319 # onto the python object reference in all cases.
321 class VFuncs(VFuncsBase
):
322 # Object for testing non-floating objects with a held ref.
323 Object
= GObject
.Object
324 ObjectRef
= StrongRef
326 def test_vfunc_return_object_transfer_none_with_held_object(self
):
327 vfuncs
= self
.VFuncs()
328 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_return_object_transfer_none()
330 # Python holds the single gobject ref in 'vfuncs.object_ref'
331 # Because of this, we do not expect a floating ref or a ref increase.
332 self
.assertEqual(ref_count
, 1)
333 self
.assertFalse(is_floating
)
335 # The actual grefcount should stay at 1 even after the vfunc return.
336 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
337 self
.assertFalse(vfuncs
.in_object_is_floating
)
339 held_object_ref
= weakref
.ref(vfuncs
.object_ref
)
340 del vfuncs
.object_ref
342 self
.assertTrue(held_object_ref() is None)
344 def test_vfunc_out_object_transfer_none_with_held_object(self
):
345 vfuncs
= self
.VFuncs()
346 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_none()
348 self
.assertEqual(ref_count
, 1)
349 self
.assertFalse(is_floating
)
351 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
352 self
.assertFalse(vfuncs
.in_object_is_floating
)
354 held_object_ref
= weakref
.ref(vfuncs
.object_ref
)
355 del vfuncs
.object_ref
357 self
.assertTrue(held_object_ref() is None)
359 def test_vfunc_return_object_transfer_full_with_held_object(self
):
360 # The vfunc caller receives full ownership which should not
361 # be floating. However, the held python wrapper also has a ref.
363 vfuncs
= self
.VFuncs()
364 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_return_object_transfer_full()
366 # Ref count from the perspective of C after the vfunc is called
367 # The vfunc caller receives a new reference which should not
368 # be floating. However, the held python wrapper also has a ref.
369 self
.assertEqual(ref_count
, 2)
370 self
.assertFalse(is_floating
)
373 # The vfunc caller should have decremented its reference.
374 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
376 held_object_ref
= weakref
.ref(vfuncs
.object_ref
)
377 del vfuncs
.object_ref
379 self
.assertTrue(held_object_ref() is None)
381 def test_vfunc_out_object_transfer_full_with_held_object(self
):
382 # Same test as above except uses out arg instead of return
383 vfuncs
= self
.VFuncs()
384 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_full()
386 # Ref count from the perspective of C after the vfunc is called
387 # The vfunc caller receives a new reference which should not
388 # be floating. However, the held python wrapper also has a ref.
389 self
.assertEqual(ref_count
, 2)
390 self
.assertFalse(is_floating
)
393 # The vfunc caller should have decremented its reference.
394 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
396 held_object_ref
= weakref
.ref(vfuncs
.object_ref())
397 del vfuncs
.object_ref
399 self
.assertTrue(held_object_ref() is None)
401 def test_vfunc_in_object_transfer_none_with_held_object(self
):
402 vfuncs
= self
.VFuncs()
403 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_in_object_transfer_none(self
.VFuncs
.Object
)
407 # Ref count inside vfunc from the perspective of Python
408 self
.assertEqual(vfuncs
.in_object_grefcount
, 2) # initial + python wrapper
409 self
.assertFalse(vfuncs
.in_object_is_floating
)
411 # Ref count from the perspective of C after the vfunc is called
412 self
.assertEqual(ref_count
, 2) # kept after vfunc + held python wrapper
413 self
.assertFalse(is_floating
)
415 # Current ref count after C cleans up its reference
416 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
418 held_object_ref
= weakref
.ref(vfuncs
.object_ref())
419 del vfuncs
.object_ref
421 self
.assertTrue(held_object_ref() is None)
423 def test_vfunc_in_object_transfer_full_with_held_object(self
):
424 vfuncs
= self
.VFuncs()
425 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_in_object_transfer_full(self
.VFuncs
.Object
)
429 # Ref count inside vfunc from the perspective of Python
430 self
.assertEqual(vfuncs
.in_object_grefcount
, 1) # python wrapper takes ownership of the gobject
431 self
.assertFalse(vfuncs
.in_object_is_floating
)
433 # Ref count from the perspective of C after the vfunc is called
434 self
.assertEqual(ref_count
, 1)
435 self
.assertFalse(is_floating
)
438 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
440 held_object_ref
= weakref
.ref(vfuncs
.object_ref())
441 del vfuncs
.object_ref
443 self
.assertTrue(held_object_ref() is None)
446 class TestVFuncsWithHeldFloatingArg(unittest
.TestCase
):
447 # Tests for a floating object which we hold a reference to the python wrapper
448 # on the VFuncs test class.
450 class VFuncs(VFuncsBase
):
451 # Object for testing floating objects with a held ref.
452 Object
= GObject
.InitiallyUnowned
453 ObjectRef
= StrongRef
455 def test_vfunc_return_object_transfer_none_with_held_floating(self
):
456 # Python holds onto the wrapper which basically means the floating ref
457 # should also be owned by python.
459 vfuncs
= self
.VFuncs()
460 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_return_object_transfer_none()
462 # This is a borrowed ref from what is held in python.
463 self
.assertEqual(ref_count
, 1)
464 self
.assertFalse(is_floating
)
466 # The actual grefcount should stay at 1 even after the vfunc return.
467 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
469 held_object_ref
= weakref
.ref(vfuncs
.object_ref
)
470 del vfuncs
.object_ref
472 self
.assertTrue(held_object_ref() is None)
474 def test_vfunc_out_object_transfer_none_with_held_floating(self
):
477 vfuncs
= self
.VFuncs()
478 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_none()
480 self
.assertEqual(ref_count
, 1)
481 self
.assertFalse(is_floating
)
483 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
485 held_object_ref
= weakref
.ref(vfuncs
.object_ref
)
486 del vfuncs
.object_ref
488 self
.assertTrue(held_object_ref() is None)
490 def test_vfunc_return_object_transfer_full_with_held_floating(self
):
491 vfuncs
= self
.VFuncs()
492 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_return_object_transfer_full()
494 # Ref count from the perspective of C after the vfunc is called
495 self
.assertEqual(ref_count
, 2)
496 self
.assertFalse(is_floating
)
499 # vfunc wrapper destroyes ref it was given
500 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
502 held_object_ref
= weakref
.ref(vfuncs
.object_ref
)
503 del vfuncs
.object_ref
505 self
.assertTrue(held_object_ref() is None)
507 def test_vfunc_out_object_transfer_full_with_held_floating(self
):
508 # Same test as above except uses out arg instead of return
509 vfuncs
= self
.VFuncs()
510 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_out_object_transfer_full()
512 # Ref count from the perspective of C after the vfunc is called
513 self
.assertEqual(ref_count
, 2)
514 self
.assertFalse(is_floating
)
517 # vfunc wrapper destroyes ref it was given
518 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
520 held_object_ref
= weakref
.ref(vfuncs
.object_ref())
521 del vfuncs
.object_ref
523 self
.assertTrue(held_object_ref() is None)
525 def test_vfunc_in_floating_transfer_none_with_held_floating(self
):
526 vfuncs
= self
.VFuncs()
527 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_in_object_transfer_none(self
.VFuncs
.Object
)
530 # Ref count inside vfunc from the perspective of Python
531 self
.assertTrue(vfuncs
.in_object_is_floating
)
532 self
.assertEqual(vfuncs
.in_object_grefcount
, 2) # python wrapper sinks and owns the gobject
534 # Ref count from the perspective of C after the vfunc is called
535 self
.assertTrue(is_floating
)
536 self
.assertEqual(ref_count
, 2) # floating + held by wrapper
538 # Current ref count after C cleans up its reference
539 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
541 held_object_ref
= weakref
.ref(vfuncs
.object_ref())
542 del vfuncs
.object_ref
544 self
.assertTrue(held_object_ref() is None)
546 def test_vfunc_in_floating_transfer_full_with_held_floating(self
):
547 vfuncs
= self
.VFuncs()
548 ref_count
, is_floating
= vfuncs
.get_ref_info_for_vfunc_in_object_transfer_full(self
.VFuncs
.Object
)
551 # Ref count from the perspective of C after the vfunc is called
552 self
.assertEqual(vfuncs
.in_object_grefcount
, 1) # python wrapper sinks and owns the gobject
553 self
.assertFalse(vfuncs
.in_object_is_floating
)
555 # Ref count from the perspective of C after the vfunc is called
556 self
.assertEqual(ref_count
, 1) # held by wrapper
557 self
.assertFalse(is_floating
)
560 self
.assertEqual(vfuncs
.object_ref().__grefcount
__, 1)
562 held_object_ref
= weakref
.ref(vfuncs
.object_ref())
563 del vfuncs
.object_ref
565 self
.assertTrue(held_object_ref() is None)
568 @unittest.skipIf(Regress
is None, 'Regress is required')
569 class TestArgumentTypeErrors(unittest
.TestCase
):
570 def test_object_argument_type_error(self
):
571 # ensure TypeError is raised for things which are not GObjects
572 obj
= Regress
.TestObj()
573 obj
.set_bare(GObject
.Object())
576 self
.assertRaises(TypeError, obj
.set_bare
, object())
577 self
.assertRaises(TypeError, obj
.set_bare
, 42)
578 self
.assertRaises(TypeError, obj
.set_bare
, 'not an object')
580 def test_instance_argument_error(self
):
581 # ensure TypeError is raised for non Regress.TestObj instances.
582 obj
= Regress
.TestObj()
583 self
.assertEqual(Regress
.TestObj
.instance_method(obj
), -1)
584 self
.assertRaises(TypeError, Regress
.TestObj
.instance_method
, object())
585 self
.assertRaises(TypeError, Regress
.TestObj
.instance_method
, GObject
.Object())
586 self
.assertRaises(TypeError, Regress
.TestObj
.instance_method
, 42)
587 self
.assertRaises(TypeError, Regress
.TestObj
.instance_method
, 'not an object')
589 def test_instance_argument_base_type_error(self
):
590 # ensure TypeError is raised when a base type is passed to something
591 # expecting a derived type
592 obj
= Regress
.TestSubObj()
593 self
.assertEqual(Regress
.TestSubObj
.instance_method(obj
), 0)
594 self
.assertRaises(TypeError, Regress
.TestSubObj
.instance_method
, GObject
.Object())
595 self
.assertRaises(TypeError, Regress
.TestSubObj
.instance_method
, Regress
.TestObj())