6 from gi.repository import GObject
11 class TestGObjectAPI(unittest.TestCase):
12 def testGObjectModule(self):
13 obj = GObject.GObject()
15 self.assertEqual(obj.__module__,
16 'gi._gobject._gobject')
19 class TestReferenceCounting(unittest.TestCase):
20 def testRegularObject(self):
21 obj = GObject.GObject()
22 self.assertEqual(obj.__grefcount__, 1)
24 obj = GObject.new(GObject.GObject)
25 self.assertEqual(obj.__grefcount__, 1)
27 def testFloating(self):
28 obj = testhelper.Floating()
29 self.assertEqual(obj.__grefcount__, 1)
31 obj = GObject.new(testhelper.Floating)
32 self.assertEqual(obj.__grefcount__, 1)
34 def testOwnedByLibrary(self):
35 # Upon creation, the refcount of the object should be 2:
36 # - someone already has a reference on the new object.
37 # - the python wrapper should hold its own reference.
38 obj = testhelper.OwnedByLibrary()
39 self.assertEqual(obj.__grefcount__, 2)
41 # We ask the library to release its reference, so the only
42 # remaining ref should be our wrapper's. Once the wrapper
43 # will run out of scope, the object will get finalized.
45 self.assertEqual(obj.__grefcount__, 1)
47 def testOwnedByLibraryOutOfScope(self):
48 obj = testhelper.OwnedByLibrary()
49 self.assertEqual(obj.__grefcount__, 2)
51 # We are manually taking the object out of scope. This means
52 # that our wrapper has been freed, and its reference dropped. We
53 # cannot check it but the refcount should now be 1 (the ref held
54 # by the library is still there, we didn't call release()
57 # When we get the object back from the lib, the wrapper is
58 # re-created, so our refcount will be 2 once again.
59 obj = testhelper.owned_by_library_get_instance_list()[0]
60 self.assertEqual(obj.__grefcount__, 2)
63 self.assertEqual(obj.__grefcount__, 1)
65 def testOwnedByLibraryUsingGObjectNew(self):
66 # Upon creation, the refcount of the object should be 2:
67 # - someone already has a reference on the new object.
68 # - the python wrapper should hold its own reference.
69 obj = GObject.new(testhelper.OwnedByLibrary)
70 self.assertEqual(obj.__grefcount__, 2)
72 # We ask the library to release its reference, so the only
73 # remaining ref should be our wrapper's. Once the wrapper
74 # will run out of scope, the object will get finalized.
76 self.assertEqual(obj.__grefcount__, 1)
78 def testOwnedByLibraryOutOfScopeUsingGobjectNew(self):
79 obj = GObject.new(testhelper.OwnedByLibrary)
80 self.assertEqual(obj.__grefcount__, 2)
82 # We are manually taking the object out of scope. This means
83 # that our wrapper has been freed, and its reference dropped. We
84 # cannot check it but the refcount should now be 1 (the ref held
85 # by the library is still there, we didn't call release()
88 # When we get the object back from the lib, the wrapper is
89 # re-created, so our refcount will be 2 once again.
90 obj = testhelper.owned_by_library_get_instance_list()[0]
91 self.assertEqual(obj.__grefcount__, 2)
94 self.assertEqual(obj.__grefcount__, 1)
96 def testFloatingAndSunk(self):
97 # Upon creation, the refcount of the object should be 2:
98 # - someone already has a reference on the new object.
99 # - the python wrapper should hold its own reference.
100 obj = testhelper.FloatingAndSunk()
101 self.assertEqual(obj.__grefcount__, 2)
103 # We ask the library to release its reference, so the only
104 # remaining ref should be our wrapper's. Once the wrapper
105 # will run out of scope, the object will get finalized.
107 self.assertEqual(obj.__grefcount__, 1)
109 def testFloatingAndSunkOutOfScope(self):
110 obj = testhelper.FloatingAndSunk()
111 self.assertEqual(obj.__grefcount__, 2)
113 # We are manually taking the object out of scope. This means
114 # that our wrapper has been freed, and its reference dropped. We
115 # cannot check it but the refcount should now be 1 (the ref held
116 # by the library is still there, we didn't call release()
119 # When we get the object back from the lib, the wrapper is
120 # re-created, so our refcount will be 2 once again.
121 obj = testhelper.floating_and_sunk_get_instance_list()[0]
122 self.assertEqual(obj.__grefcount__, 2)
125 self.assertEqual(obj.__grefcount__, 1)
127 def testFloatingAndSunkUsingGObjectNew(self):
128 # Upon creation, the refcount of the object should be 2:
129 # - someone already has a reference on the new object.
130 # - the python wrapper should hold its own reference.
131 obj = GObject.new(testhelper.FloatingAndSunk)
132 self.assertEqual(obj.__grefcount__, 2)
134 # We ask the library to release its reference, so the only
135 # remaining ref should be our wrapper's. Once the wrapper
136 # will run out of scope, the object will get finalized.
138 self.assertEqual(obj.__grefcount__, 1)
140 def testFloatingAndSunkOutOfScopeUsingGObjectNew(self):
141 obj = GObject.new(testhelper.FloatingAndSunk)
142 self.assertEqual(obj.__grefcount__, 2)
144 # We are manually taking the object out of scope. This means
145 # that our wrapper has been freed, and its reference dropped. We
146 # cannot check it but the refcount should now be 1 (the ref held
147 # by the library is still there, we didn't call release()
150 # When we get the object back from the lib, the wrapper is
151 # re-created, so our refcount will be 2 once again.
152 obj = testhelper.floating_and_sunk_get_instance_list()[0]
153 self.assertEqual(obj.__grefcount__, 2)
156 self.assertEqual(obj.__grefcount__, 1)
158 def testUninitializedObject(self):
159 class Obj(GObject.GObject):
161 x = self.__grefcount__
162 super(Obj, self).__init__()
163 assert x >= 0 # quiesce pyflakes
165 # Accessing __grefcount__ before the object is initialized is wrong.
166 # Ensure we get a proper exception instead of a crash.
167 self.assertRaises(TypeError, Obj)
170 class A(GObject.GObject):
172 super(A, self).__init__()
175 class TestPythonReferenceCounting(unittest.TestCase):
176 # Newly created instances should alwayshave two references: one for
177 # the GC, and one for the bound variable in the local scope.
179 def testNewInstanceHasTwoRefs(self):
180 obj = GObject.GObject()
181 self.assertEqual(sys.getrefcount(obj), 2)
183 def testNewInstanceHasTwoRefsUsingGObjectNew(self):
184 obj = GObject.new(GObject.GObject)
185 self.assertEqual(sys.getrefcount(obj), 2)
187 def testNewSubclassInstanceHasTwoRefs(self):
189 self.assertEqual(sys.getrefcount(obj), 2)
191 def testNewSubclassInstanceHasTwoRefsUsingGObjectNew(self):
193 self.assertEqual(sys.getrefcount(obj), 2)
196 class TestContextManagers(unittest.TestCase):
197 class ContextTestObject(GObject.GObject):
198 prop = GObject.Property(default=0, type=int)
200 def on_prop_set(self, obj, prop):
201 # Handler which tracks property changed notifications.
202 self.tracking.append(obj.get_property(prop.name))
206 self.obj = self.ContextTestObject()
207 self.handler = self.obj.connect('notify::prop', self.on_prop_set)
209 def testFreezeNotifyContext(self):
210 # Verify prop tracking list
211 self.assertEqual(self.tracking, [])
212 self.obj.props.prop = 1
213 self.assertEqual(self.tracking, [1])
214 self.obj.props.prop = 2
215 self.assertEqual(self.tracking, [1, 2])
216 self.assertEqual(self.obj.__grefcount__, 1)
218 # Using the context manager the tracking list should not be affected
219 # and the GObject reference count should go up.
220 with self.obj.freeze_notify():
221 self.assertEqual(self.obj.__grefcount__, 2)
222 self.obj.props.prop = 3
223 self.assertEqual(self.obj.props.prop, 3)
224 self.assertEqual(self.tracking, [1, 2])
226 # After the context manager, the prop should have been modified,
227 # the tracking list will be modified, and the GObject ref
228 # count goes back down.
229 self.assertEqual(self.obj.props.prop, 3)
230 self.assertEqual(self.tracking, [1, 2, 3])
231 self.assertEqual(self.obj.__grefcount__, 1)
233 def testHandlerBlockContext(self):
234 # Verify prop tracking list
235 self.assertEqual(self.tracking, [])
236 self.obj.props.prop = 1
237 self.assertEqual(self.tracking, [1])
238 self.obj.props.prop = 2
239 self.assertEqual(self.tracking, [1, 2])
240 self.assertEqual(self.obj.__grefcount__, 1)
242 # Using the context manager the tracking list should not be affected
243 # and the GObject reference count should go up.
244 with self.obj.handler_block(self.handler):
245 self.assertEqual(self.obj.__grefcount__, 2)
246 self.obj.props.prop = 3
247 self.assertEqual(self.obj.props.prop, 3)
248 self.assertEqual(self.tracking, [1, 2])
250 # After the context manager, the prop should have been modified
251 # the tracking list should have stayed the same and the GObject ref
252 # count goes back down.
253 self.assertEqual(self.obj.props.prop, 3)
254 self.assertEqual(self.tracking, [1, 2])
255 self.assertEqual(self.obj.__grefcount__, 1)
257 def testFreezeNotifyContextNested(self):
258 self.assertEqual(self.tracking, [])
259 with self.obj.freeze_notify():
260 self.obj.props.prop = 1
261 self.assertEqual(self.tracking, [])
263 with self.obj.freeze_notify():
264 self.obj.props.prop = 2
265 self.assertEqual(self.tracking, [])
267 with self.obj.freeze_notify():
268 self.obj.props.prop = 3
269 self.assertEqual(self.tracking, [])
270 self.assertEqual(self.tracking, [])
271 self.assertEqual(self.tracking, [])
273 # Finally after last context, the notifications should have collapsed
274 # and the last one sent.
275 self.assertEqual(self.tracking, [3])
277 def testHandlerBlockContextNested(self):
278 self.assertEqual(self.tracking, [])
279 with self.obj.handler_block(self.handler):
280 self.obj.props.prop = 1
281 self.assertEqual(self.tracking, [])
283 with self.obj.handler_block(self.handler):
284 self.obj.props.prop = 2
285 self.assertEqual(self.tracking, [])
287 with self.obj.handler_block(self.handler):
288 self.obj.props.prop = 3
289 self.assertEqual(self.tracking, [])
290 self.assertEqual(self.tracking, [])
291 self.assertEqual(self.tracking, [])
293 # Finally after last context, the notifications should have collapsed
294 # and the last one sent.
295 self.assertEqual(self.obj.props.prop, 3)
296 self.assertEqual(self.tracking, [])
298 def testFreezeNotifyNormalUsageRefCounts(self):
299 # Ensure ref counts without using methods as context managers
300 # maintain the same count.
301 self.assertEqual(self.obj.__grefcount__, 1)
302 self.obj.freeze_notify()
303 self.assertEqual(self.obj.__grefcount__, 1)
304 self.obj.thaw_notify()
305 self.assertEqual(self.obj.__grefcount__, 1)
307 def testHandlerBlockNormalUsageRefCounts(self):
308 self.assertEqual(self.obj.__grefcount__, 1)
309 self.obj.handler_block(self.handler)
310 self.assertEqual(self.obj.__grefcount__, 1)
311 self.obj.handler_unblock(self.handler)
312 self.assertEqual(self.obj.__grefcount__, 1)
314 def testFreezeNotifyContextError(self):
315 # Test an exception occurring within a freeze context exits the context
317 with self.obj.freeze_notify():
318 self.obj.props.prop = 1
319 self.assertEqual(self.tracking, [])
320 raise ValueError('Simulation')
324 # Verify the property set within the context called notify.
325 self.assertEqual(self.obj.props.prop, 1)
326 self.assertEqual(self.tracking, [1])
328 # Verify we are still not in a frozen context.
329 self.obj.props.prop = 2
330 self.assertEqual(self.tracking, [1, 2])
332 def testHandlerBlockContextError(self):
333 # Test an exception occurring within a handler block exits the context
335 with self.obj.handler_block(self.handler):
336 self.obj.props.prop = 1
337 self.assertEqual(self.tracking, [])
338 raise ValueError('Simulation')
342 # Verify the property set within the context didn't call notify.
343 self.assertEqual(self.obj.props.prop, 1)
344 self.assertEqual(self.tracking, [])
346 # Verify we are still not in a handler block context.
347 self.obj.props.prop = 2
348 self.assertEqual(self.tracking, [2])
351 class TestPropertyBindings(unittest.TestCase):
352 class TestObject(GObject.GObject):
353 int_prop = GObject.Property(default=0, type=int)
356 self.source = self.TestObject()
357 self.target = self.TestObject()
359 def testDefaultBinding(self):
360 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
361 GObject.BindingFlags.DEFAULT)
362 binding = binding # PyFlakes
364 # Test setting value on source gets pushed to target
365 self.source.int_prop = 1
366 self.assertEqual(self.source.int_prop, 1)
367 self.assertEqual(self.target.int_prop, 1)
369 # Test setting value on target does not change source
370 self.target.props.int_prop = 2
371 self.assertEqual(self.source.int_prop, 1)
372 self.assertEqual(self.target.int_prop, 2)
374 def testBiDirectionalBinding(self):
375 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
376 GObject.BindingFlags.BIDIRECTIONAL)
377 binding = binding # PyFlakes
379 # Test setting value on source gets pushed to target
380 self.source.int_prop = 1
381 self.assertEqual(self.source.int_prop, 1)
382 self.assertEqual(self.target.int_prop, 1)
384 # Test setting value on target also changes source
385 self.target.props.int_prop = 2
386 self.assertEqual(self.source.int_prop, 2)
387 self.assertEqual(self.target.int_prop, 2)
389 def testTransformToOnly(self):
390 def transform_to(binding, value, user_data=None):
391 self.assertEqual(user_data, 'test-data')
394 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
395 GObject.BindingFlags.DEFAULT,
396 transform_to, None, 'test-data')
397 binding = binding # PyFlakes
399 self.source.int_prop = 1
400 self.assertEqual(self.source.int_prop, 1)
401 self.assertEqual(self.target.int_prop, 2)
403 self.target.props.int_prop = 1
404 self.assertEqual(self.source.int_prop, 1)
405 self.assertEqual(self.target.int_prop, 1)
407 def testTransformFromOnly(self):
408 def transform_from(binding, value, user_data=None):
409 self.assertEqual(user_data, None)
412 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
413 GObject.BindingFlags.BIDIRECTIONAL,
414 None, transform_from)
415 binding = binding # PyFlakes
417 self.source.int_prop = 1
418 self.assertEqual(self.source.int_prop, 1)
419 self.assertEqual(self.target.int_prop, 1)
421 self.target.props.int_prop = 1
422 self.assertEqual(self.source.int_prop, 2)
423 self.assertEqual(self.target.int_prop, 1)
425 def testTransformBidrectional(self):
426 def transform_to(binding, value, user_data=None):
427 self.assertEqual(user_data, 'test-data')
430 def transform_from(binding, value, user_data=None):
431 self.assertEqual(user_data, 'test-data')
434 # bidirectional bindings
435 binding = self.source.bind_property('int_prop', self.target, 'int_prop',
436 GObject.BindingFlags.BIDIRECTIONAL,
437 transform_to, transform_from, 'test-data')
438 binding = binding # PyFlakes
440 self.source.int_prop = 1
441 self.assertEqual(self.source.int_prop, 1)
442 self.assertEqual(self.target.int_prop, 2)
444 self.target.props.int_prop = 4
445 self.assertEqual(self.source.int_prop, 2)
446 self.assertEqual(self.target.int_prop, 4)
448 def testExplicitUnbindClearsConnection(self):
449 self.assertEqual(self.source.int_prop, 0)
450 self.assertEqual(self.target.int_prop, 0)
452 # Test deleting binding reference removes binding.
453 binding = self.source.bind_property('int_prop', self.target, 'int_prop')
454 self.source.int_prop = 1
455 self.assertEqual(self.source.int_prop, 1)
456 self.assertEqual(self.target.int_prop, 1)
459 self.assertEqual(binding(), None)
461 self.source.int_prop = 10
462 self.assertEqual(self.source.int_prop, 10)
463 self.assertEqual(self.target.int_prop, 1)
465 # An already unbound BindingWeakRef will raise if unbind is attempted a second time.
466 self.assertRaises(ValueError, binding.unbind)
468 def testReferenceCounts(self):
469 self.assertEqual(self.source.__grefcount__, 1)
470 self.assertEqual(self.target.__grefcount__, 1)
472 # Binding ref count will be 2 do to the initial ref implicitly held by
473 # the act of binding and the ref incurred by using __call__ to generate
474 # a wrapper from the weak binding ref within python.
475 binding = self.source.bind_property('int_prop', self.target, 'int_prop')
476 self.assertEqual(binding().__grefcount__, 2)
478 # Creating a binding does not inc refs on source and target (they are weak
479 # on the binding object itself)
480 self.assertEqual(self.source.__grefcount__, 1)
481 self.assertEqual(self.target.__grefcount__, 1)
483 # Use GObject.get_property because the "props" accessor leaks.
484 # Note property names are canonicalized.
485 self.assertEqual(binding().get_property('source'), self.source)
486 self.assertEqual(binding().get_property('source_property'), 'int-prop')
487 self.assertEqual(binding().get_property('target'), self.target)
488 self.assertEqual(binding().get_property('target_property'), 'int-prop')
489 self.assertEqual(binding().get_property('flags'), GObject.BindingFlags.DEFAULT)
491 # Delete reference to source or target and the binding should listen.
492 ref = self.source.weak_ref()
495 self.assertEqual(ref(), None)
496 self.assertEqual(binding(), None)
498 if __name__ == '__main__':