Imported Upstream version 3.3.3.1
[platform/upstream/pygobject2.git] / tests / test_gobject.py
1 # -*- Mode: Python -*-
2
3 import gc
4 import unittest
5
6 from gi.repository import GObject
7 import sys
8 import testhelper
9
10
11 class TestGObjectAPI(unittest.TestCase):
12     def testGObjectModule(self):
13         obj = GObject.GObject()
14
15         self.assertEqual(obj.__module__,
16                          'gi._gobject._gobject')
17
18
19 class TestReferenceCounting(unittest.TestCase):
20     def testRegularObject(self):
21         obj = GObject.GObject()
22         self.assertEqual(obj.__grefcount__, 1)
23
24         obj = GObject.new(GObject.GObject)
25         self.assertEqual(obj.__grefcount__, 1)
26
27     def testFloating(self):
28         obj = testhelper.Floating()
29         self.assertEqual(obj.__grefcount__, 1)
30
31         obj = GObject.new(testhelper.Floating)
32         self.assertEqual(obj.__grefcount__, 1)
33
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)
40
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.
44         obj.release()
45         self.assertEqual(obj.__grefcount__, 1)
46
47     def testOwnedByLibraryOutOfScope(self):
48         obj = testhelper.OwnedByLibrary()
49         self.assertEqual(obj.__grefcount__, 2)
50
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()
55         obj = None
56
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)
61
62         obj.release()
63         self.assertEqual(obj.__grefcount__, 1)
64
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)
71
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.
75         obj.release()
76         self.assertEqual(obj.__grefcount__, 1)
77
78     def testOwnedByLibraryOutOfScopeUsingGobjectNew(self):
79         obj = GObject.new(testhelper.OwnedByLibrary)
80         self.assertEqual(obj.__grefcount__, 2)
81
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()
86         obj = None
87
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)
92
93         obj.release()
94         self.assertEqual(obj.__grefcount__, 1)
95
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)
102
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.
106         obj.release()
107         self.assertEqual(obj.__grefcount__, 1)
108
109     def testFloatingAndSunkOutOfScope(self):
110         obj = testhelper.FloatingAndSunk()
111         self.assertEqual(obj.__grefcount__, 2)
112
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()
117         obj = None
118
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)
123
124         obj.release()
125         self.assertEqual(obj.__grefcount__, 1)
126
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)
133
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.
137         obj.release()
138         self.assertEqual(obj.__grefcount__, 1)
139
140     def testFloatingAndSunkOutOfScopeUsingGObjectNew(self):
141         obj = GObject.new(testhelper.FloatingAndSunk)
142         self.assertEqual(obj.__grefcount__, 2)
143
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()
148         obj = None
149
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)
154
155         obj.release()
156         self.assertEqual(obj.__grefcount__, 1)
157
158     def testUninitializedObject(self):
159         class Obj(GObject.GObject):
160             def __init__(self):
161                 x = self.__grefcount__
162                 super(Obj, self).__init__()
163                 assert x >= 0  # quiesce pyflakes
164
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)
168
169
170 class A(GObject.GObject):
171     def __init__(self):
172         super(A, self).__init__()
173
174
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.
178
179     def testNewInstanceHasTwoRefs(self):
180         obj = GObject.GObject()
181         self.assertEqual(sys.getrefcount(obj), 2)
182
183     def testNewInstanceHasTwoRefsUsingGObjectNew(self):
184         obj = GObject.new(GObject.GObject)
185         self.assertEqual(sys.getrefcount(obj), 2)
186
187     def testNewSubclassInstanceHasTwoRefs(self):
188         obj = A()
189         self.assertEqual(sys.getrefcount(obj), 2)
190
191     def testNewSubclassInstanceHasTwoRefsUsingGObjectNew(self):
192         obj = GObject.new(A)
193         self.assertEqual(sys.getrefcount(obj), 2)
194
195
196 class TestContextManagers(unittest.TestCase):
197     class ContextTestObject(GObject.GObject):
198         prop = GObject.Property(default=0, type=int)
199
200     def on_prop_set(self, obj, prop):
201         # Handler which tracks property changed notifications.
202         self.tracking.append(obj.get_property(prop.name))
203
204     def setUp(self):
205         self.tracking = []
206         self.obj = self.ContextTestObject()
207         self.handler = self.obj.connect('notify::prop', self.on_prop_set)
208
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)
217
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])
225
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)
232
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)
241
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])
249
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)
256
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, [])
262
263             with self.obj.freeze_notify():
264                 self.obj.props.prop = 2
265                 self.assertEqual(self.tracking, [])
266
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, [])
272
273         # Finally after last context, the notifications should have collapsed
274         # and the last one sent.
275         self.assertEqual(self.tracking, [3])
276
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, [])
282
283             with self.obj.handler_block(self.handler):
284                 self.obj.props.prop = 2
285                 self.assertEqual(self.tracking, [])
286
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, [])
292
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, [])
297
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)
306
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)
313
314     def testFreezeNotifyContextError(self):
315         # Test an exception occurring within a freeze context exits the context
316         try:
317             with self.obj.freeze_notify():
318                 self.obj.props.prop = 1
319                 self.assertEqual(self.tracking, [])
320                 raise ValueError('Simulation')
321         except ValueError:
322             pass
323
324         # Verify the property set within the context called notify.
325         self.assertEqual(self.obj.props.prop, 1)
326         self.assertEqual(self.tracking, [1])
327
328         # Verify we are still not in a frozen context.
329         self.obj.props.prop = 2
330         self.assertEqual(self.tracking, [1, 2])
331
332     def testHandlerBlockContextError(self):
333         # Test an exception occurring within a handler block exits the context
334         try:
335             with self.obj.handler_block(self.handler):
336                 self.obj.props.prop = 1
337                 self.assertEqual(self.tracking, [])
338                 raise ValueError('Simulation')
339         except ValueError:
340             pass
341
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, [])
345
346         # Verify we are still not in a handler block context.
347         self.obj.props.prop = 2
348         self.assertEqual(self.tracking, [2])
349
350
351 class TestPropertyBindings(unittest.TestCase):
352     class TestObject(GObject.GObject):
353         int_prop = GObject.Property(default=0, type=int)
354
355     def setUp(self):
356         self.source = self.TestObject()
357         self.target = self.TestObject()
358
359     def testDefaultBinding(self):
360         binding = self.source.bind_property('int_prop', self.target, 'int_prop',
361                                             GObject.BindingFlags.DEFAULT)
362         binding = binding  # PyFlakes
363
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)
368
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)
373
374     def testBiDirectionalBinding(self):
375         binding = self.source.bind_property('int_prop', self.target, 'int_prop',
376                                             GObject.BindingFlags.BIDIRECTIONAL)
377         binding = binding  # PyFlakes
378
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)
383
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)
388
389     def testTransformToOnly(self):
390         def transform_to(binding, value, user_data=None):
391             self.assertEqual(user_data, 'test-data')
392             return value * 2
393
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
398
399         self.source.int_prop = 1
400         self.assertEqual(self.source.int_prop, 1)
401         self.assertEqual(self.target.int_prop, 2)
402
403         self.target.props.int_prop = 1
404         self.assertEqual(self.source.int_prop, 1)
405         self.assertEqual(self.target.int_prop, 1)
406
407     def testTransformFromOnly(self):
408         def transform_from(binding, value, user_data=None):
409             self.assertEqual(user_data, None)
410             return value * 2
411
412         binding = self.source.bind_property('int_prop', self.target, 'int_prop',
413                                             GObject.BindingFlags.BIDIRECTIONAL,
414                                             None, transform_from)
415         binding = binding  # PyFlakes
416
417         self.source.int_prop = 1
418         self.assertEqual(self.source.int_prop, 1)
419         self.assertEqual(self.target.int_prop, 1)
420
421         self.target.props.int_prop = 1
422         self.assertEqual(self.source.int_prop, 2)
423         self.assertEqual(self.target.int_prop, 1)
424
425     def testTransformBidrectional(self):
426         def transform_to(binding, value, user_data=None):
427             self.assertEqual(user_data, 'test-data')
428             return value * 2
429
430         def transform_from(binding, value, user_data=None):
431             self.assertEqual(user_data, 'test-data')
432             return value / 2
433
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
439
440         self.source.int_prop = 1
441         self.assertEqual(self.source.int_prop, 1)
442         self.assertEqual(self.target.int_prop, 2)
443
444         self.target.props.int_prop = 4
445         self.assertEqual(self.source.int_prop, 2)
446         self.assertEqual(self.target.int_prop, 4)
447
448     def testExplicitUnbindClearsConnection(self):
449         self.assertEqual(self.source.int_prop, 0)
450         self.assertEqual(self.target.int_prop, 0)
451
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)
457
458         binding.unbind()
459         self.assertEqual(binding(), None)
460
461         self.source.int_prop = 10
462         self.assertEqual(self.source.int_prop, 10)
463         self.assertEqual(self.target.int_prop, 1)
464
465         # An already unbound BindingWeakRef will raise if unbind is attempted a second time.
466         self.assertRaises(ValueError, binding.unbind)
467
468     def testReferenceCounts(self):
469         self.assertEqual(self.source.__grefcount__, 1)
470         self.assertEqual(self.target.__grefcount__, 1)
471
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)
477
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)
482
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)
490
491         # Delete reference to source or target and the binding should listen.
492         ref = self.source.weak_ref()
493         del self.source
494         gc.collect()
495         self.assertEqual(ref(), None)
496         self.assertEqual(binding(), None)
497
498 if __name__ == '__main__':
499     unittest.main()