tests: don't test for specific device labels
[pygobject.git] / tests / test_object_marshaling.py
blob3d8ebb84de04935a1d6bba9e0b38da861bf320e0
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
4 from __future__ import absolute_import
6 import unittest
7 import weakref
8 import gc
9 import sys
10 import warnings
12 from gi.repository import GObject
13 from gi.repository import GIMarshallingTests
15 try:
16 from gi.repository import Regress
17 except ImportError:
18 Regress = None
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):
27 self.obj = obj
29 def __call__(self):
30 return 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
45 def __init__(self):
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.
57 obj = self.Object()
58 self.object_ref = self.ObjectRef(obj)
59 return obj
61 def do_vfunc_return_object_transfer_full(self):
62 # Return an object and hand off the reference to the caller.
63 obj = self.Object()
64 self.object_ref = self.ObjectRef(obj)
65 return 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.
70 obj = self.Object()
71 self.object_ref = self.ObjectRef(obj)
72 return 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.
77 obj = self.Object()
78 self.object_ref = self.ObjectRef(obj)
79 return obj
81 def do_vfunc_in_object_transfer_none(self, obj):
82 # 'obj' will have a python wrapper as well as still held
83 # by the caller.
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.
112 gc.collect()
113 if hasattr(sys, "getrefcount"):
114 self.assertEqual(sys.getrefcount(vfuncs), 2)
115 self.assertEqual(vfuncs.__grefcount__, 1)
117 del vfuncs
118 gc.collect()
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)
140 gc.collect()
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)
156 gc.collect()
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
164 # be floating.
165 if hasattr(sys, "getrefcount"):
166 self.assertEqual(ref_count, 1)
167 self.assertFalse(is_floating)
169 gc.collect()
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)
181 gc.collect()
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)
188 gc.collect()
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)
196 gc.collect()
197 gc.collect()
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)
204 gc.collect()
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)
215 gc.collect()
216 gc.collect()
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)
239 gc.collect()
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)
251 gc.collect()
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)
263 gc.collect()
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)
275 gc.collect()
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)
282 gc.collect()
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)
293 gc.collect()
294 gc.collect()
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)
301 gc.collect()
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)
312 gc.collect()
313 gc.collect()
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
341 gc.collect()
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
356 gc.collect()
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)
372 # Current ref count
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
378 gc.collect()
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)
392 # Current ref count
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
398 gc.collect()
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)
405 gc.collect()
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
420 gc.collect()
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)
427 gc.collect()
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)
437 # Current ref count
438 self.assertEqual(vfuncs.object_ref().__grefcount__, 1)
440 held_object_ref = weakref.ref(vfuncs.object_ref())
441 del vfuncs.object_ref
442 gc.collect()
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
471 gc.collect()
472 self.assertTrue(held_object_ref() is None)
474 def test_vfunc_out_object_transfer_none_with_held_floating(self):
475 # Same as above
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
487 gc.collect()
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)
498 # Current ref count
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
504 gc.collect()
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)
516 # Current ref count
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
522 gc.collect()
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)
528 gc.collect()
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
543 gc.collect()
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)
549 gc.collect()
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)
559 # Current ref count
560 self.assertEqual(vfuncs.object_ref().__grefcount__, 1)
562 held_object_ref = weakref.ref(vfuncs.object_ref())
563 del vfuncs.object_ref
564 gc.collect()
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())
574 obj.set_bare(None)
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())