Imported Upstream version 3.7.3
[platform/upstream/python-gobject.git] / tests / test_gobject.py
1 # -*- Mode: Python -*-
2
3 import sys
4 import gc
5 import unittest
6 import warnings
7
8 from gi.repository import GObject
9 from gi import PyGIDeprecationWarning
10 from gi.module import get_introspection_module
11 from gi._gobject import _gobject
12
13 import testhelper
14
15
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))
23
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)
27
28         # The pytype wrapper should hold the outer most Object class from overrides.
29         self.assertEqual(GObject.TYPE_OBJECT.pytype, GObject.Object)
30
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()
34
35         with self.assertRaisesRegex(RuntimeError, 'Data access methods are unsupported.*'):
36             obj.get_data()
37
38         with self.assertRaisesRegex(RuntimeError, 'This method is currently unsupported.*'):
39             obj.force_floating()
40
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')
46
47             ml = GObject.MainLoop()
48             self.assertFalse(ml.is_running())
49
50             context = GObject.main_context_default()
51             self.assertTrue(context.pending() in [False, True])
52
53             context = GObject.MainContext()
54             self.assertFalse(context.pending())
55
56             self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning))
57             self.assertTrue('GLib.markup_escape_text' in str(w[0]), str(w[0]))
58
59             self.assertLess(GObject.PRIORITY_HIGH, GObject.PRIORITY_DEFAULT)
60
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)
65
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)
69
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)
73
74
75 class TestReferenceCounting(unittest.TestCase):
76     def test_regular_object(self):
77         obj = GObject.GObject()
78         self.assertEqual(obj.__grefcount__, 1)
79
80         obj = GObject.new(GObject.GObject)
81         self.assertEqual(obj.__grefcount__, 1)
82
83     def test_floating(self):
84         obj = testhelper.Floating()
85         self.assertEqual(obj.__grefcount__, 1)
86
87         obj = GObject.new(testhelper.Floating)
88         self.assertEqual(obj.__grefcount__, 1)
89
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)
96
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.
100         obj.release()
101         self.assertEqual(obj.__grefcount__, 1)
102
103     def test_owned_by_library_out_of_scope(self):
104         obj = testhelper.OwnedByLibrary()
105         self.assertEqual(obj.__grefcount__, 2)
106
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()
111         obj = None
112
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)
117
118         obj.release()
119         self.assertEqual(obj.__grefcount__, 1)
120
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)
127
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.
131         obj.release()
132         self.assertEqual(obj.__grefcount__, 1)
133
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)
137
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()
142         obj = None
143
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)
148
149         obj.release()
150         self.assertEqual(obj.__grefcount__, 1)
151
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)
158
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.
162         obj.release()
163         self.assertEqual(obj.__grefcount__, 1)
164
165     def test_floating_and_sunk_out_of_scope(self):
166         obj = testhelper.FloatingAndSunk()
167         self.assertEqual(obj.__grefcount__, 2)
168
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()
173         obj = None
174
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)
179
180         obj.release()
181         self.assertEqual(obj.__grefcount__, 1)
182
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)
189
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.
193         obj.release()
194         self.assertEqual(obj.__grefcount__, 1)
195
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)
199
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()
204         obj = None
205
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)
210
211         obj.release()
212         self.assertEqual(obj.__grefcount__, 1)
213
214     def test_uninitialized_object(self):
215         class Obj(GObject.GObject):
216             def __init__(self):
217                 x = self.__grefcount__
218                 super(Obj, self).__init__()
219                 assert x >= 0  # quiesce pyflakes
220
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)
224
225
226 class A(GObject.GObject):
227     def __init__(self):
228         super(A, self).__init__()
229
230
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.
234
235     def test_new_instance_has_two_refs(self):
236         obj = GObject.GObject()
237         self.assertEqual(sys.getrefcount(obj), 2)
238
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)
242
243     def test_new_subclass_instance_has_two_refs(self):
244         obj = A()
245         self.assertEqual(sys.getrefcount(obj), 2)
246
247     def test_new_subclass_instance_has_two_refs_using_gobject_new(self):
248         obj = GObject.new(A)
249         self.assertEqual(sys.getrefcount(obj), 2)
250
251
252 class TestContextManagers(unittest.TestCase):
253     class ContextTestObject(GObject.GObject):
254         prop = GObject.Property(default=0, type=int)
255
256     def on_prop_set(self, obj, prop):
257         # Handler which tracks property changed notifications.
258         self.tracking.append(obj.get_property(prop.name))
259
260     def setUp(self):
261         self.tracking = []
262         self.obj = self.ContextTestObject()
263         self.handler = self.obj.connect('notify::prop', self.on_prop_set)
264
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)
273
274         pyref_count = sys.getrefcount(self.obj)
275
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])
285
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.
289         gc.collect()
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)
294
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)
303
304         pyref_count = sys.getrefcount(self.obj)
305
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])
315
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.
319         gc.collect()
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)
324
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, [])
330
331             with self.obj.freeze_notify():
332                 self.obj.props.prop = 2
333                 self.assertEqual(self.tracking, [])
334
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, [])
340
341         # Finally after last context, the notifications should have collapsed
342         # and the last one sent.
343         self.assertEqual(self.tracking, [3])
344
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, [])
350
351             with self.obj.handler_block(self.handler):
352                 self.obj.props.prop = 2
353                 self.assertEqual(self.tracking, [])
354
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, [])
360
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, [])
365
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)
374
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)
381
382     def test_freeze_notify_context_error(self):
383         # Test an exception occurring within a freeze context exits the context
384         try:
385             with self.obj.freeze_notify():
386                 self.obj.props.prop = 1
387                 self.assertEqual(self.tracking, [])
388                 raise ValueError('Simulation')
389         except ValueError:
390             pass
391
392         # Verify the property set within the context called notify.
393         self.assertEqual(self.obj.props.prop, 1)
394         self.assertEqual(self.tracking, [1])
395
396         # Verify we are still not in a frozen context.
397         self.obj.props.prop = 2
398         self.assertEqual(self.tracking, [1, 2])
399
400     def test_handler_block_context_error(self):
401         # Test an exception occurring within a handler block exits the context
402         try:
403             with self.obj.handler_block(self.handler):
404                 self.obj.props.prop = 1
405                 self.assertEqual(self.tracking, [])
406                 raise ValueError('Simulation')
407         except ValueError:
408             pass
409
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, [])
413
414         # Verify we are still not in a handler block context.
415         self.obj.props.prop = 2
416         self.assertEqual(self.tracking, [2])
417
418
419 class TestPropertyBindings(unittest.TestCase):
420     class TestObject(GObject.GObject):
421         int_prop = GObject.Property(default=0, type=int)
422
423     def setUp(self):
424         self.source = self.TestObject()
425         self.target = self.TestObject()
426
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
431
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)
436
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)
441
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
446
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)
451
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)
456
457     def test_transform_to_only(self):
458         def transform_to(binding, value, user_data=None):
459             self.assertEqual(user_data, 'test-data')
460             return value * 2
461
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
466
467         self.source.int_prop = 1
468         self.assertEqual(self.source.int_prop, 1)
469         self.assertEqual(self.target.int_prop, 2)
470
471         self.target.props.int_prop = 1
472         self.assertEqual(self.source.int_prop, 1)
473         self.assertEqual(self.target.int_prop, 1)
474
475     def test_transform_from_only(self):
476         def transform_from(binding, value, user_data=None):
477             self.assertEqual(user_data, None)
478             return value * 2
479
480         binding = self.source.bind_property('int_prop', self.target, 'int_prop',
481                                             GObject.BindingFlags.BIDIRECTIONAL,
482                                             None, transform_from)
483         binding = binding  # PyFlakes
484
485         self.source.int_prop = 1
486         self.assertEqual(self.source.int_prop, 1)
487         self.assertEqual(self.target.int_prop, 1)
488
489         self.target.props.int_prop = 1
490         self.assertEqual(self.source.int_prop, 2)
491         self.assertEqual(self.target.int_prop, 1)
492
493     def test_transform_bidirectional(self):
494         def transform_to(binding, value, user_data=None):
495             self.assertEqual(user_data, 'test-data')
496             return value * 2
497
498         def transform_from(binding, value, user_data=None):
499             self.assertEqual(user_data, 'test-data')
500             return value / 2
501
502         # bidirectional bindings
503         binding = self.source.bind_property('int_prop', self.target, 'int_prop',
504                                             GObject.BindingFlags.BIDIRECTIONAL,
505                                             transform_to, transform_from, 'test-data')
506         binding = binding  # PyFlakes
507
508         self.source.int_prop = 1
509         self.assertEqual(self.source.int_prop, 1)
510         self.assertEqual(self.target.int_prop, 2)
511
512         self.target.props.int_prop = 4
513         self.assertEqual(self.source.int_prop, 2)
514         self.assertEqual(self.target.int_prop, 4)
515
516     def test_explicit_unbind_clears_connection(self):
517         self.assertEqual(self.source.int_prop, 0)
518         self.assertEqual(self.target.int_prop, 0)
519
520         # Test deleting binding reference removes binding.
521         binding = self.source.bind_property('int_prop', self.target, 'int_prop')
522         self.source.int_prop = 1
523         self.assertEqual(self.source.int_prop, 1)
524         self.assertEqual(self.target.int_prop, 1)
525
526         binding.unbind()
527         self.assertEqual(binding(), None)
528
529         self.source.int_prop = 10
530         self.assertEqual(self.source.int_prop, 10)
531         self.assertEqual(self.target.int_prop, 1)
532
533         # An already unbound BindingWeakRef will raise if unbind is attempted a second time.
534         self.assertRaises(ValueError, binding.unbind)
535
536     def test_reference_counts(self):
537         self.assertEqual(self.source.__grefcount__, 1)
538         self.assertEqual(self.target.__grefcount__, 1)
539
540         # Binding ref count will be 2 do to the initial ref implicitly held by
541         # the act of binding and the ref incurred by using __call__ to generate
542         # a wrapper from the weak binding ref within python.
543         binding = self.source.bind_property('int_prop', self.target, 'int_prop')
544         self.assertEqual(binding().__grefcount__, 2)
545
546         # Creating a binding does not inc refs on source and target (they are weak
547         # on the binding object itself)
548         self.assertEqual(self.source.__grefcount__, 1)
549         self.assertEqual(self.target.__grefcount__, 1)
550
551         # Use GObject.get_property because the "props" accessor leaks.
552         # Note property names are canonicalized.
553         self.assertEqual(binding().get_property('source'), self.source)
554         self.assertEqual(binding().get_property('source_property'), 'int-prop')
555         self.assertEqual(binding().get_property('target'), self.target)
556         self.assertEqual(binding().get_property('target_property'), 'int-prop')
557         self.assertEqual(binding().get_property('flags'), GObject.BindingFlags.DEFAULT)
558
559         # Delete reference to source or target and the binding should listen.
560         ref = self.source.weak_ref()
561         del self.source
562         gc.collect()
563         self.assertEqual(ref(), None)
564         self.assertEqual(binding(), None)
565
566 if __name__ == '__main__':
567     unittest.main()