8 from gi.repository import GObject, GLib
9 from gi import PyGIDeprecationWarning
10 from gi.module import get_introspection_module
11 from gi._gobject import _gobject
16 class TestGObjectAPI(unittest.TestCase):
17 def test_gobject_inheritance(self):
18 # GObject.Object is a class hierarchy as follows:
19 # overrides.Object -> introspection.Object -> static.GObject
20 GIObjectModule = get_introspection_module('GObject')
21 self.assertTrue(issubclass(GObject.Object, GIObjectModule.Object))
22 self.assertTrue(issubclass(GIObjectModule.Object, _gobject.GObject))
24 self.assertEqual(_gobject.GObject.__gtype__, GObject.TYPE_OBJECT)
25 self.assertEqual(GIObjectModule.Object.__gtype__, GObject.TYPE_OBJECT)
26 self.assertEqual(GObject.Object.__gtype__, GObject.TYPE_OBJECT)
28 # The pytype wrapper should hold the outer most Object class from overrides.
29 self.assertEqual(GObject.TYPE_OBJECT.pytype, GObject.Object)
31 @unittest.skipIf(sys.version_info[:2] < (2, 7), 'Python 2.7 is required')
32 def test_gobject_unsupported_overrides(self):
33 obj = GObject.Object()
35 with self.assertRaisesRegex(RuntimeError, 'Data access methods are unsupported.*'):
38 with self.assertRaisesRegex(RuntimeError, 'This method is currently unsupported.*'):
41 def test_compat_api(self):
42 with warnings.catch_warnings(record=True) as w:
43 warnings.simplefilter('always')
44 # GObject formerly exposed a lot of GLib's functions
45 self.assertEqual(GObject.markup_escape_text('foo'), 'foo')
47 ml = GObject.MainLoop()
48 self.assertFalse(ml.is_running())
50 context = GObject.main_context_default()
51 self.assertTrue(context.pending() in [False, True])
53 context = GObject.MainContext()
54 self.assertFalse(context.pending())
56 self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning))
57 self.assertTrue('GLib.markup_escape_text' in str(w[0]), str(w[0]))
59 self.assertLess(GObject.PRIORITY_HIGH, GObject.PRIORITY_DEFAULT)
61 def test_min_max_int(self):
62 self.assertEqual(GObject.G_MAXINT16, 2 ** 15 - 1)
63 self.assertEqual(GObject.G_MININT16, -2 ** 15)
64 self.assertEqual(GObject.G_MAXUINT16, 2 ** 16 - 1)
66 self.assertEqual(GObject.G_MAXINT32, 2 ** 31 - 1)
67 self.assertEqual(GObject.G_MININT32, -2 ** 31)
68 self.assertEqual(GObject.G_MAXUINT32, 2 ** 32 - 1)
70 self.assertEqual(GObject.G_MAXINT64, 2 ** 63 - 1)
71 self.assertEqual(GObject.G_MININT64, -2 ** 63)
72 self.assertEqual(GObject.G_MAXUINT64, 2 ** 64 - 1)
75 class TestReferenceCounting(unittest.TestCase):
76 def test_regular_object(self):
77 obj = GObject.GObject()
78 self.assertEqual(obj.__grefcount__, 1)
80 obj = GObject.new(GObject.GObject)
81 self.assertEqual(obj.__grefcount__, 1)
83 def test_floating(self):
84 obj = testhelper.Floating()
85 self.assertEqual(obj.__grefcount__, 1)
87 obj = GObject.new(testhelper.Floating)
88 self.assertEqual(obj.__grefcount__, 1)
90 def test_owned_by_library(self):
91 # Upon creation, the refcount of the object should be 2:
92 # - someone already has a reference on the new object.
93 # - the python wrapper should hold its own reference.
94 obj = testhelper.OwnedByLibrary()
95 self.assertEqual(obj.__grefcount__, 2)
97 # We ask the library to release its reference, so the only
98 # remaining ref should be our wrapper's. Once the wrapper
99 # will run out of scope, the object will get finalized.
101 self.assertEqual(obj.__grefcount__, 1)
103 def test_owned_by_library_out_of_scope(self):
104 obj = testhelper.OwnedByLibrary()
105 self.assertEqual(obj.__grefcount__, 2)
107 # We are manually taking the object out of scope. This means
108 # that our wrapper has been freed, and its reference dropped. We
109 # cannot check it but the refcount should now be 1 (the ref held
110 # by the library is still there, we didn't call release()
113 # When we get the object back from the lib, the wrapper is
114 # re-created, so our refcount will be 2 once again.
115 obj = testhelper.owned_by_library_get_instance_list()[0]
116 self.assertEqual(obj.__grefcount__, 2)
119 self.assertEqual(obj.__grefcount__, 1)
121 def test_owned_by_library_using_gobject_new(self):
122 # Upon creation, the refcount of the object should be 2:
123 # - someone already has a reference on the new object.
124 # - the python wrapper should hold its own reference.
125 obj = GObject.new(testhelper.OwnedByLibrary)
126 self.assertEqual(obj.__grefcount__, 2)
128 # We ask the library to release its reference, so the only
129 # remaining ref should be our wrapper's. Once the wrapper
130 # will run out of scope, the object will get finalized.
132 self.assertEqual(obj.__grefcount__, 1)
134 def test_owned_by_library_out_of_scope_using_gobject_new(self):
135 obj = GObject.new(testhelper.OwnedByLibrary)
136 self.assertEqual(obj.__grefcount__, 2)
138 # We are manually taking the object out of scope. This means
139 # that our wrapper has been freed, and its reference dropped. We
140 # cannot check it but the refcount should now be 1 (the ref held
141 # by the library is still there, we didn't call release()
144 # When we get the object back from the lib, the wrapper is
145 # re-created, so our refcount will be 2 once again.
146 obj = testhelper.owned_by_library_get_instance_list()[0]
147 self.assertEqual(obj.__grefcount__, 2)
150 self.assertEqual(obj.__grefcount__, 1)
152 def test_floating_and_sunk(self):
153 # Upon creation, the refcount of the object should be 2:
154 # - someone already has a reference on the new object.
155 # - the python wrapper should hold its own reference.
156 obj = testhelper.FloatingAndSunk()
157 self.assertEqual(obj.__grefcount__, 2)
159 # We ask the library to release its reference, so the only
160 # remaining ref should be our wrapper's. Once the wrapper
161 # will run out of scope, the object will get finalized.
163 self.assertEqual(obj.__grefcount__, 1)
165 def test_floating_and_sunk_out_of_scope(self):
166 obj = testhelper.FloatingAndSunk()
167 self.assertEqual(obj.__grefcount__, 2)
169 # We are manually taking the object out of scope. This means
170 # that our wrapper has been freed, and its reference dropped. We
171 # cannot check it but the refcount should now be 1 (the ref held
172 # by the library is still there, we didn't call release()
175 # When we get the object back from the lib, the wrapper is
176 # re-created, so our refcount will be 2 once again.
177 obj = testhelper.floating_and_sunk_get_instance_list()[0]
178 self.assertEqual(obj.__grefcount__, 2)
181 self.assertEqual(obj.__grefcount__, 1)
183 def test_floating_and_sunk_using_gobject_new(self):
184 # Upon creation, the refcount of the object should be 2:
185 # - someone already has a reference on the new object.
186 # - the python wrapper should hold its own reference.
187 obj = GObject.new(testhelper.FloatingAndSunk)
188 self.assertEqual(obj.__grefcount__, 2)
190 # We ask the library to release its reference, so the only
191 # remaining ref should be our wrapper's. Once the wrapper
192 # will run out of scope, the object will get finalized.
194 self.assertEqual(obj.__grefcount__, 1)
196 def test_floating_and_sunk_out_of_scope_using_gobject_new(self):
197 obj = GObject.new(testhelper.FloatingAndSunk)
198 self.assertEqual(obj.__grefcount__, 2)
200 # We are manually taking the object out of scope. This means
201 # that our wrapper has been freed, and its reference dropped. We
202 # cannot check it but the refcount should now be 1 (the ref held
203 # by the library is still there, we didn't call release()
206 # When we get the object back from the lib, the wrapper is
207 # re-created, so our refcount will be 2 once again.
208 obj = testhelper.floating_and_sunk_get_instance_list()[0]
209 self.assertEqual(obj.__grefcount__, 2)
212 self.assertEqual(obj.__grefcount__, 1)
214 def test_uninitialized_object(self):
215 class Obj(GObject.GObject):
217 x = self.__grefcount__
218 super(Obj, self).__init__()
219 assert x >= 0 # quiesce pyflakes
221 # Accessing __grefcount__ before the object is initialized is wrong.
222 # Ensure we get a proper exception instead of a crash.
223 self.assertRaises(TypeError, Obj)
226 class A(GObject.GObject):
228 super(A, self).__init__()
231 class TestPythonReferenceCounting(unittest.TestCase):
232 # Newly created instances should alwayshave two references: one for
233 # the GC, and one for the bound variable in the local scope.
235 def test_new_instance_has_two_refs(self):
236 obj = GObject.GObject()
237 self.assertEqual(sys.getrefcount(obj), 2)
239 def test_new_instance_has_two_refs_using_gobject_new(self):
240 obj = GObject.new(GObject.GObject)
241 self.assertEqual(sys.getrefcount(obj), 2)
243 def test_new_subclass_instance_has_two_refs(self):
245 self.assertEqual(sys.getrefcount(obj), 2)
247 def test_new_subclass_instance_has_two_refs_using_gobject_new(self):
249 self.assertEqual(sys.getrefcount(obj), 2)
252 class TestContextManagers(unittest.TestCase):
253 class ContextTestObject(GObject.GObject):
254 prop = GObject.Property(default=0, type=int)
256 def on_prop_set(self, obj, prop):
257 # Handler which tracks property changed notifications.
258 self.tracking.append(obj.get_property(prop.name))
262 self.obj = self.ContextTestObject()
263 self.handler = self.obj.connect('notify::prop', self.on_prop_set)
265 def test_freeze_notify_context(self):
266 # Verify prop tracking list
267 self.assertEqual(self.tracking, [])
268 self.obj.props.prop = 1
269 self.assertEqual(self.tracking, [1])
270 self.obj.props.prop = 2
271 self.assertEqual(self.tracking, [1, 2])
272 self.assertEqual(self.obj.__grefcount__, 1)
274 pyref_count = sys.getrefcount(self.obj)
276 # Using the context manager the tracking list should not be affected.
277 # The GObject reference count should stay the same and the python
278 # object ref-count should go up.
279 with self.obj.freeze_notify():
280 self.assertEqual(self.obj.__grefcount__, 1)
281 self.assertEqual(sys.getrefcount(self.obj), pyref_count + 1)
282 self.obj.props.prop = 3
283 self.assertEqual(self.obj.props.prop, 3)
284 self.assertEqual(self.tracking, [1, 2])
286 # After the context manager, the prop should have been modified,
287 # the tracking list will be modified, and the python object ref
288 # count goes back down.
290 self.assertEqual(self.obj.props.prop, 3)
291 self.assertEqual(self.tracking, [1, 2, 3])
292 self.assertEqual(self.obj.__grefcount__, 1)
293 self.assertEqual(sys.getrefcount(self.obj), pyref_count)
295 def test_handler_block_context(self):
296 # Verify prop tracking list
297 self.assertEqual(self.tracking, [])
298 self.obj.props.prop = 1
299 self.assertEqual(self.tracking, [1])
300 self.obj.props.prop = 2
301 self.assertEqual(self.tracking, [1, 2])
302 self.assertEqual(self.obj.__grefcount__, 1)
304 pyref_count = sys.getrefcount(self.obj)
306 # Using the context manager the tracking list should not be affected.
307 # The GObject reference count should stay the same and the python
308 # object ref-count should go up.
309 with self.obj.handler_block(self.handler):
310 self.assertEqual(self.obj.__grefcount__, 1)
311 self.assertEqual(sys.getrefcount(self.obj), pyref_count + 1)
312 self.obj.props.prop = 3
313 self.assertEqual(self.obj.props.prop, 3)
314 self.assertEqual(self.tracking, [1, 2])
316 # After the context manager, the prop should have been modified
317 # the tracking list should have stayed the same and the GObject ref
318 # count goes back down.
320 self.assertEqual(self.obj.props.prop, 3)
321 self.assertEqual(self.tracking, [1, 2])
322 self.assertEqual(self.obj.__grefcount__, 1)
323 self.assertEqual(sys.getrefcount(self.obj), pyref_count)
325 def test_freeze_notify_context_nested(self):
326 self.assertEqual(self.tracking, [])
327 with self.obj.freeze_notify():
328 self.obj.props.prop = 1
329 self.assertEqual(self.tracking, [])
331 with self.obj.freeze_notify():
332 self.obj.props.prop = 2
333 self.assertEqual(self.tracking, [])
335 with self.obj.freeze_notify():
336 self.obj.props.prop = 3
337 self.assertEqual(self.tracking, [])
338 self.assertEqual(self.tracking, [])
339 self.assertEqual(self.tracking, [])
341 # Finally after last context, the notifications should have collapsed
342 # and the last one sent.
343 self.assertEqual(self.tracking, [3])
345 def test_handler_block_context_nested(self):
346 self.assertEqual(self.tracking, [])
347 with self.obj.handler_block(self.handler):
348 self.obj.props.prop = 1
349 self.assertEqual(self.tracking, [])
351 with self.obj.handler_block(self.handler):
352 self.obj.props.prop = 2
353 self.assertEqual(self.tracking, [])
355 with self.obj.handler_block(self.handler):
356 self.obj.props.prop = 3
357 self.assertEqual(self.tracking, [])
358 self.assertEqual(self.tracking, [])
359 self.assertEqual(self.tracking, [])
361 # Finally after last context, the notifications should have collapsed
362 # and the last one sent.
363 self.assertEqual(self.obj.props.prop, 3)
364 self.assertEqual(self.tracking, [])
366 def test_freeze_notify_normal_usage_ref_counts(self):
367 # Ensure ref counts without using methods as context managers
368 # maintain the same count.
369 self.assertEqual(self.obj.__grefcount__, 1)
370 self.obj.freeze_notify()
371 self.assertEqual(self.obj.__grefcount__, 1)
372 self.obj.thaw_notify()
373 self.assertEqual(self.obj.__grefcount__, 1)
375 def test_handler_block_normal_usage_ref_counts(self):
376 self.assertEqual(self.obj.__grefcount__, 1)
377 self.obj.handler_block(self.handler)
378 self.assertEqual(self.obj.__grefcount__, 1)
379 self.obj.handler_unblock(self.handler)
380 self.assertEqual(self.obj.__grefcount__, 1)
382 def test_freeze_notify_context_error(self):
383 # Test an exception occurring within a freeze context exits the context
385 with self.obj.freeze_notify():
386 self.obj.props.prop = 1
387 self.assertEqual(self.tracking, [])
388 raise ValueError('Simulation')
392 # Verify the property set within the context called notify.
393 self.assertEqual(self.obj.props.prop, 1)
394 self.assertEqual(self.tracking, [1])
396 # Verify we are still not in a frozen context.
397 self.obj.props.prop = 2
398 self.assertEqual(self.tracking, [1, 2])
400 def test_handler_block_context_error(self):
401 # Test an exception occurring within a handler block exits the context
403 with self.obj.handler_block(self.handler):
404 self.obj.props.prop = 1
405 self.assertEqual(self.tracking, [])
406 raise ValueError('Simulation')
410 # Verify the property set within the context didn't call notify.
411 self.assertEqual(self.obj.props.prop, 1)
412 self.assertEqual(self.tracking, [])
414 # Verify we are still not in a handler block context.
415 self.obj.props.prop = 2
416 self.assertEqual(self.tracking, [2])
419 class TestPropertyBindings(unittest.TestCase):
420 class TestObject(GObject.GObject):
421 int_prop = GObject.Property(default=0, type=int)
424 self.source = self.TestObject()
425 self.target = self.TestObject()
427 def test_default_binding(self):
428 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
429 GObject.BindingFlags.DEFAULT)
430 binding = binding # PyFlakes
432 # Test setting value on source gets pushed to target
433 self.source.int_prop = 1
434 self.assertEqual(self.source.int_prop, 1)
435 self.assertEqual(self.target.int_prop, 1)
437 # Test setting value on target does not change source
438 self.target.props.int_prop = 2
439 self.assertEqual(self.source.int_prop, 1)
440 self.assertEqual(self.target.int_prop, 2)
442 def test_bidirectional_binding(self):
443 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
444 GObject.BindingFlags.BIDIRECTIONAL)
445 binding = binding # PyFlakes
447 # Test setting value on source gets pushed to target
448 self.source.int_prop = 1
449 self.assertEqual(self.source.int_prop, 1)
450 self.assertEqual(self.target.int_prop, 1)
452 # Test setting value on target also changes source
453 self.target.props.int_prop = 2
454 self.assertEqual(self.source.int_prop, 2)
455 self.assertEqual(self.target.int_prop, 2)
457 def test_transform_to_only(self):
458 def transform_to(binding, value, user_data=None):
459 self.assertEqual(user_data, 'test-data')
462 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
463 GObject.BindingFlags.DEFAULT,
464 transform_to, None, 'test-data')
465 binding = binding # PyFlakes
467 self.source.int_prop = 1
468 self.assertEqual(self.source.int_prop, 1)
469 self.assertEqual(self.target.int_prop, 2)
471 self.target.props.int_prop = 1
472 self.assertEqual(self.source.int_prop, 1)
473 self.assertEqual(self.target.int_prop, 1)
475 def test_transform_from_only(self):
476 def transform_from(binding, value, user_data=None):
477 self.assertEqual(user_data, None)
480 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
481 GObject.BindingFlags.BIDIRECTIONAL,
482 None, transform_from)
483 binding = binding # PyFlakes
485 self.source.int_prop = 1
486 self.assertEqual(self.source.int_prop, 1)
487 self.assertEqual(self.target.int_prop, 1)
489 self.target.props.int_prop = 1
490 self.assertEqual(self.source.int_prop, 2)
491 self.assertEqual(self.target.int_prop, 1)
493 def test_transform_bidirectional(self):
496 def transform_to(binding, value, user_data=None):
497 self.assertEqual(user_data, test_data)
500 def transform_from(binding, value, user_data=None):
501 self.assertEqual(user_data, test_data)
504 test_data_ref_count = sys.getrefcount(test_data)
505 transform_to_ref_count = sys.getrefcount(transform_to)
506 transform_from_ref_count = sys.getrefcount(transform_from)
508 # bidirectional bindings
509 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
510 GObject.BindingFlags.BIDIRECTIONAL,
511 transform_to, transform_from, test_data)
512 binding = binding # PyFlakes
513 binding_ref_count = sys.getrefcount(binding())
514 binding_gref_count = binding().__grefcount__
516 self.source.int_prop = 1
517 self.assertEqual(self.source.int_prop, 1)
518 self.assertEqual(self.target.int_prop, 2)
520 self.target.props.int_prop = 4
521 self.assertEqual(self.source.int_prop, 2)
522 self.assertEqual(self.target.int_prop, 4)
524 self.assertEqual(sys.getrefcount(binding()), binding_ref_count)
525 self.assertEqual(binding().__grefcount__, binding_gref_count)
527 # test_data ref count increases by 2, once for each callback.
528 self.assertEqual(sys.getrefcount(test_data), test_data_ref_count + 2)
529 self.assertEqual(sys.getrefcount(transform_to), transform_to_ref_count + 1)
530 self.assertEqual(sys.getrefcount(transform_from), transform_from_ref_count + 1)
532 # Unbind should clear out the binding and its transforms
534 self.assertEqual(binding(), None)
538 # Setting source or target should not change the other.
539 self.target.int_prop = 3
540 self.source.int_prop = 5
541 self.assertEqual(self.target.int_prop, 3)
542 self.assertEqual(self.source.int_prop, 5)
544 self.assertEqual(sys.getrefcount(test_data), test_data_ref_count)
545 self.assertEqual(sys.getrefcount(transform_to), transform_to_ref_count)
546 self.assertEqual(sys.getrefcount(transform_from), transform_from_ref_count)
548 def test_explicit_unbind_clears_connection(self):
549 self.assertEqual(self.source.int_prop, 0)
550 self.assertEqual(self.target.int_prop, 0)
552 # Test deleting binding reference removes binding.
553 binding = self.source.bind_property('int_prop', self.target, 'int_prop')
554 self.source.int_prop = 1
555 self.assertEqual(self.source.int_prop, 1)
556 self.assertEqual(self.target.int_prop, 1)
559 self.assertEqual(binding(), None)
561 self.source.int_prop = 10
562 self.assertEqual(self.source.int_prop, 10)
563 self.assertEqual(self.target.int_prop, 1)
565 # An already unbound BindingWeakRef will raise if unbind is attempted a second time.
566 self.assertRaises(ValueError, binding.unbind)
568 def test_reference_counts(self):
569 self.assertEqual(self.source.__grefcount__, 1)
570 self.assertEqual(self.target.__grefcount__, 1)
572 # Binding ref count will be 2 do to the initial ref implicitly held by
573 # the act of binding and the ref incurred by using __call__ to generate
574 # a wrapper from the weak binding ref within python.
575 binding = self.source.bind_property('int_prop', self.target, 'int_prop')
576 self.assertEqual(binding().__grefcount__, 2)
578 # Creating a binding does not inc refs on source and target (they are weak
579 # on the binding object itself)
580 self.assertEqual(self.source.__grefcount__, 1)
581 self.assertEqual(self.target.__grefcount__, 1)
583 # Use GObject.get_property because the "props" accessor leaks.
584 # Note property names are canonicalized.
585 self.assertEqual(binding().get_property('source'), self.source)
586 self.assertEqual(binding().get_property('source_property'), 'int-prop')
587 self.assertEqual(binding().get_property('target'), self.target)
588 self.assertEqual(binding().get_property('target_property'), 'int-prop')
589 self.assertEqual(binding().get_property('flags'), GObject.BindingFlags.DEFAULT)
591 # Delete reference to source or target and the binding should listen.
592 ref = self.source.weak_ref()
595 self.assertEqual(ref(), None)
596 self.assertEqual(binding(), None)
599 class TestGValue(unittest.TestCase):
600 def test_no_type(self):
601 value = GObject.Value()
602 self.assertEqual(value.g_type, GObject.TYPE_INVALID)
603 self.assertRaises(TypeError, value.set_value, 23)
604 self.assertEqual(value.get_value(), None)
607 value = GObject.Value(GObject.TYPE_UINT)
608 self.assertEqual(value.g_type, GObject.TYPE_UINT)
610 self.assertEqual(value.get_value(), 23)
611 value.set_value(42.0)
612 self.assertEqual(value.get_value(), 42)
614 def test_string(self):
615 value = GObject.Value(str, 'foo_bar')
616 self.assertEqual(value.g_type, GObject.TYPE_STRING)
617 self.assertEqual(value.get_value(), 'foo_bar')
619 def test_float(self):
620 # python float is G_TYPE_DOUBLE
621 value = GObject.Value(float, 23.4)
622 self.assertEqual(value.g_type, GObject.TYPE_DOUBLE)
624 value = GObject.Value(GObject.TYPE_FLOAT, 23.4)
625 self.assertEqual(value.g_type, GObject.TYPE_FLOAT)
626 self.assertRaises(TypeError, value.set_value, 'string')
629 value = GObject.Value(GLib.FileError, GLib.FileError.FAILED)
630 self.assertEqual(value.get_value(), GLib.FileError.FAILED)
632 def test_flags(self):
633 value = GObject.Value(GLib.IOFlags, GLib.IOFlags.IS_READABLE)
634 self.assertEqual(value.get_value(), GLib.IOFlags.IS_READABLE)
636 def test_object(self):
637 class TestObject(GObject.Object):
640 value = GObject.Value(GObject.TYPE_OBJECT, obj)
641 self.assertEqual(value.get_value(), obj)
643 if __name__ == '__main__':