Imported Upstream version 3.3.1
[platform/upstream/pygobject2.git] / tests / test_properties.py
1 # coding=utf-8
2
3 import sys
4 import struct
5 import unittest
6
7 from gi.repository import GObject
8 from  gi.repository.GObject import GType, new, PARAM_READWRITE, \
9      PARAM_CONSTRUCT, PARAM_READABLE, PARAM_WRITABLE, PARAM_CONSTRUCT_ONLY
10 from gi.repository.GObject import \
11      TYPE_INT, TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_INT64, \
12      TYPE_UINT64, TYPE_GTYPE, TYPE_INVALID, TYPE_NONE
13 from gi.repository.GObject import \
14      G_MININT, G_MAXINT, G_MAXUINT, G_MINLONG, G_MAXLONG, \
15      G_MAXULONG
16
17 from gi.repository import Gio
18 from gi.repository import GLib
19 from gi.repository import GIMarshallingTests
20
21 if sys.version_info < (3, 0):
22     TEST_UTF8 = "\xe2\x99\xa5"
23     UNICODE_UTF8 = unicode(TEST_UTF8, 'UTF-8')
24 else:
25     TEST_UTF8 = "♥"
26     UNICODE_UTF8 = TEST_UTF8
27
28 from compathelper import _long
29
30
31 class PropertyObject(GObject.GObject):
32     normal = GObject.Property(type=str)
33     construct = GObject.Property(
34         type=str,
35         flags=PARAM_READWRITE | PARAM_CONSTRUCT, default='default')
36     construct_only = GObject.Property(
37         type=str,
38         flags=PARAM_READWRITE | PARAM_CONSTRUCT_ONLY)
39     uint64 = GObject.Property(
40         type=TYPE_UINT64, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
41
42     enum = GObject.Property(
43         type=Gio.SocketType, default=Gio.SocketType.STREAM)
44
45     boxed = GObject.Property(
46         type=GLib.Regex, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
47
48     flags = GObject.Property(
49         type=GIMarshallingTests.Flags, flags=PARAM_READWRITE | PARAM_CONSTRUCT,
50         default=GIMarshallingTests.Flags.VALUE1)
51
52     gtype = GObject.Property(
53         type=TYPE_GTYPE, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
54
55
56 class TestProperties(unittest.TestCase):
57     def testGetSet(self):
58         obj = PropertyObject()
59         obj.props.normal = "value"
60         self.assertEqual(obj.props.normal, "value")
61
62     def testListWithInstance(self):
63         obj = PropertyObject()
64         self.assertTrue(hasattr(obj.props, "normal"))
65
66     def testListWithoutInstance(self):
67         self.assertTrue(hasattr(PropertyObject.props, "normal"))
68
69     def testSetNoInstance(self):
70         def set(obj):
71             obj.props.normal = "foobar"
72
73         self.assertRaises(TypeError, set, PropertyObject)
74
75     def testIterator(self):
76         for obj in (PropertyObject.props, PropertyObject().props):
77             for pspec in obj:
78                 gtype = GType(pspec)
79                 self.assertEqual(gtype.parent.name, 'GParam')
80                 self.assertTrue(pspec.name in ['normal',
81                                                'construct',
82                                                'construct-only',
83                                                'uint64',
84                                                'enum',
85                                                'flags',
86                                                'gtype',
87                                                'boxed'])
88             self.assertEqual(len(obj), 8)
89
90     def testNormal(self):
91         obj = new(PropertyObject, normal="123")
92         self.assertEqual(obj.props.normal, "123")
93         obj.set_property('normal', '456')
94         self.assertEqual(obj.props.normal, "456")
95         obj.props.normal = '789'
96         self.assertEqual(obj.props.normal, "789")
97
98     def testConstruct(self):
99         obj = new(PropertyObject, construct="123")
100         self.assertEqual(obj.props.construct, "123")
101         obj.set_property('construct', '456')
102         self.assertEqual(obj.props.construct, "456")
103         obj.props.construct = '789'
104         self.assertEqual(obj.props.construct, "789")
105
106     def testUTF8(self):
107         obj = new(PropertyObject, construct_only=UNICODE_UTF8)
108         self.assertEqual(obj.props.construct_only, TEST_UTF8)
109         obj.set_property('construct', UNICODE_UTF8)
110         self.assertEqual(obj.props.construct, TEST_UTF8)
111         obj.props.normal = UNICODE_UTF8
112         self.assertEqual(obj.props.normal, TEST_UTF8)
113
114     def testIntToStr(self):
115         obj = new(PropertyObject, construct_only=1)
116         self.assertEqual(obj.props.construct_only, '1')
117         obj.set_property('construct', '2')
118         self.assertEqual(obj.props.construct, '2')
119         obj.props.normal = 3
120         self.assertEqual(obj.props.normal, '3')
121
122     def testConstructOnly(self):
123         obj = new(PropertyObject, construct_only="123")
124         self.assertEqual(obj.props.construct_only, "123")
125         self.assertRaises(TypeError,
126                           setattr, obj.props, 'construct_only', '456')
127         self.assertRaises(TypeError,
128                           obj.set_property, 'construct-only', '456')
129
130     def testUint64(self):
131         obj = new(PropertyObject)
132         self.assertEqual(obj.props.uint64, 0)
133         obj.props.uint64 = _long(1)
134         self.assertEqual(obj.props.uint64, _long(1))
135         obj.props.uint64 = 1
136         self.assertEqual(obj.props.uint64, _long(1))
137
138         self.assertRaises((TypeError, OverflowError), obj.set_property, "uint64", _long(-1))
139         self.assertRaises((TypeError, OverflowError), obj.set_property, "uint64", -1)
140
141     def testUInt64DefaultValue(self):
142         try:
143             class TimeControl(GObject.GObject):
144                 __gproperties__ = {
145                     'time': (TYPE_UINT64, 'Time', 'Time',
146                              _long(0), (1 << 64) - 1, _long(0),
147                              PARAM_READABLE)
148                     }
149         except OverflowError:
150             (etype, ex) = sys.exc_info()[2:]
151             self.fail(str(ex))
152
153     def testEnum(self):
154         obj = new(PropertyObject)
155         self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
156         self.assertEqual(obj.enum, Gio.SocketType.STREAM)
157         obj.enum = Gio.SocketType.DATAGRAM
158         self.assertEqual(obj.props.enum, Gio.SocketType.DATAGRAM)
159         self.assertEqual(obj.enum, Gio.SocketType.DATAGRAM)
160         obj.props.enum = Gio.SocketType.STREAM
161         self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
162         self.assertEqual(obj.enum, Gio.SocketType.STREAM)
163         obj.props.enum = 2
164         self.assertEqual(obj.props.enum, Gio.SocketType.DATAGRAM)
165         self.assertEqual(obj.enum, Gio.SocketType.DATAGRAM)
166         obj.enum = 1
167         self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
168         self.assertEqual(obj.enum, Gio.SocketType.STREAM)
169
170         self.assertRaises(TypeError, setattr, obj, 'enum', 'foo')
171         self.assertRaises(TypeError, setattr, obj, 'enum', object())
172
173         self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType)
174         self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
175                           default=Gio.SocketProtocol.TCP)
176         self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
177                           default=object())
178         self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
179                           default=1)
180
181     def testFlags(self):
182         obj = new(PropertyObject)
183         self.assertEqual(obj.props.flags, GIMarshallingTests.Flags.VALUE1)
184         self.assertEqual(obj.flags, GIMarshallingTests.Flags.VALUE1)
185
186         obj.flags = GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3
187         self.assertEqual(obj.props.flags, GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3)
188         self.assertEqual(obj.flags, GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3)
189
190         self.assertRaises(TypeError, setattr, obj, 'flags', 'foo')
191         self.assertRaises(TypeError, setattr, obj, 'flags', object())
192         self.assertRaises(TypeError, setattr, obj, 'flags', None)
193
194         self.assertRaises(TypeError, GObject.Property,
195                 type=GIMarshallingTests.Flags, default='foo')
196         self.assertRaises(TypeError, GObject.Property,
197                 type=GIMarshallingTests.Flags, default=object())
198         self.assertRaises(TypeError, GObject.Property,
199                 type=GIMarshallingTests.Flags, default=None)
200
201     def testGType(self):
202         obj = new(PropertyObject)
203
204         self.assertEqual(obj.props.gtype, TYPE_NONE)
205         self.assertEqual(obj.gtype, TYPE_NONE)
206
207         obj.gtype = TYPE_UINT64
208         self.assertEqual(obj.props.gtype, TYPE_UINT64)
209         self.assertEqual(obj.gtype, TYPE_UINT64)
210
211         obj.gtype = TYPE_INVALID
212         self.assertEqual(obj.props.gtype, TYPE_INVALID)
213         self.assertEqual(obj.gtype, TYPE_INVALID)
214
215         # GType parameters do not support defaults in GLib
216         self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
217                 default=TYPE_INT)
218
219         # incompatible type
220         self.assertRaises(TypeError, setattr, obj, 'gtype', 'foo')
221         self.assertRaises(TypeError, setattr, obj, 'gtype', object())
222
223         self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
224                 default='foo')
225         self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
226                 default=object())
227
228         # set in constructor
229         obj = new(PropertyObject, gtype=TYPE_UINT)
230         self.assertEqual(obj.props.gtype, TYPE_UINT)
231         self.assertEqual(obj.gtype, TYPE_UINT)
232
233     def textBoxed(self):
234         obj = new(PropertyObject)
235
236         regex = GLib.Regex.new('[a-z]*', 0, 0)
237         obj.props.boxed = regex
238         self.assertEqual(obj.props.boxed.get_pattern(), '[a-z]*')
239         self.assertEqual(obj.boxed.get_patttern(), '[a-z]*')
240
241         self.assertRaises(TypeError, setattr, obj, 'boxed', 'foo')
242         self.assertRaises(TypeError, setattr, obj, 'boxed', object())
243
244     def testRange(self):
245         # kiwi code
246         def max(c):
247             return 2 ** ((8 * struct.calcsize(c)) - 1) - 1
248
249         def umax(c):
250             return 2 ** (8 * struct.calcsize(c)) - 1
251
252         maxint = max('i')
253         minint = -maxint - 1
254         maxuint = umax('I')
255         maxlong = max('l')
256         minlong = -maxlong - 1
257         maxulong = umax('L')
258         maxint64 = max('q')
259         minint64 = -maxint64 - 1
260         maxuint64 = umax('Q')
261
262         types = dict(int=(TYPE_INT, minint, maxint),
263                      uint=(TYPE_UINT, 0, maxuint),
264                      long=(TYPE_LONG, minlong, maxlong),
265                      ulong=(TYPE_ULONG, 0, maxulong),
266                      int64=(TYPE_INT64, minint64, maxint64),
267                      uint64=(TYPE_UINT64, 0, maxuint64))
268
269         def build_gproperties(types):
270             d = {}
271             for key, (gtype, min, max) in types.items():
272                 d[key] = (gtype, 'blurb', 'desc', min, max, 0,
273                           PARAM_READABLE | PARAM_WRITABLE)
274             return d
275
276         class RangeCheck(GObject.GObject):
277             __gproperties__ = build_gproperties(types)
278
279             def __init__(self):
280                 self.values = {}
281                 GObject.GObject.__init__(self)
282
283             def do_set_property(self, pspec, value):
284                 self.values[pspec.name] = value
285
286             def do_get_property(self, pspec):
287                 return self.values.get(pspec.name, pspec.default_value)
288
289         self.assertEqual(RangeCheck.props.int.minimum, minint)
290         self.assertEqual(RangeCheck.props.int.maximum, maxint)
291         self.assertEqual(RangeCheck.props.uint.minimum, 0)
292         self.assertEqual(RangeCheck.props.uint.maximum, maxuint)
293         self.assertEqual(RangeCheck.props.long.minimum, minlong)
294         self.assertEqual(RangeCheck.props.long.maximum, maxlong)
295         self.assertEqual(RangeCheck.props.ulong.minimum, 0)
296         self.assertEqual(RangeCheck.props.ulong.maximum, maxulong)
297         self.assertEqual(RangeCheck.props.int64.minimum, minint64)
298         self.assertEqual(RangeCheck.props.int64.maximum, maxint64)
299         self.assertEqual(RangeCheck.props.uint64.minimum, 0)
300         self.assertEqual(RangeCheck.props.uint64.maximum, maxuint64)
301
302         obj = RangeCheck()
303         for key, (gtype, min, max) in types.items():
304             self.assertEqual(obj.get_property(key),
305                              getattr(RangeCheck.props, key).default_value)
306
307             obj.set_property(key, min)
308             self.assertEqual(obj.get_property(key), min)
309
310             obj.set_property(key, max)
311             self.assertEqual(obj.get_property(key), max)
312
313     def testMulti(self):
314         obj = PropertyObject()
315         obj.set_properties(normal="foo",
316                            uint64=7)
317         normal, uint64 = obj.get_properties("normal", "uint64")
318         self.assertEqual(normal, "foo")
319         self.assertEqual(uint64, 7)
320
321
322 class TestProperty(unittest.TestCase):
323     def testSimple(self):
324         class C(GObject.GObject):
325             str = GObject.Property(type=str)
326             int = GObject.Property(type=int)
327             float = GObject.Property(type=float)
328             long = GObject.Property(type=_long)
329
330         self.assertTrue(hasattr(C.props, 'str'))
331         self.assertTrue(hasattr(C.props, 'int'))
332         self.assertTrue(hasattr(C.props, 'float'))
333         self.assertTrue(hasattr(C.props, 'long'))
334
335         o = C()
336         self.assertEqual(o.str, '')
337         o.str = 'str'
338         self.assertEqual(o.str, 'str')
339
340         self.assertEqual(o.int, 0)
341         o.int = 1138
342         self.assertEqual(o.int, 1138)
343
344         self.assertEqual(o.float, 0.0)
345         o.float = 3.14
346         self.assertEqual(o.float, 3.14)
347
348         self.assertEqual(o.long, _long(0))
349         o.long = _long(100)
350         self.assertEqual(o.long, _long(100))
351
352     def testCustomGetter(self):
353         class C(GObject.GObject):
354             def get_prop(self):
355                 return 'value'
356             prop = GObject.Property(getter=get_prop)
357
358         o = C()
359         self.assertEqual(o.prop, 'value')
360         self.assertRaises(TypeError, setattr, o, 'prop', 'xxx')
361
362     def testCustomSetter(self):
363         class C(GObject.GObject):
364             def set_prop(self, value):
365                 self._value = value
366             prop = GObject.Property(setter=set_prop)
367
368             def __init__(self):
369                 self._value = None
370                 GObject.GObject.__init__(self)
371
372         o = C()
373         self.assertEqual(o._value, None)
374         o.prop = 'bar'
375         self.assertEqual(o._value, 'bar')
376         self.assertRaises(TypeError, getattr, o, 'prop')
377
378     def testDecoratorDefault(self):
379         class C(GObject.GObject):
380             _value = 'value'
381
382             @GObject.Property
383             def value(self):
384                 return self._value
385
386             @value.setter
387             def value_setter(self, value):
388                 self._value = value
389
390         o = C()
391         self.assertEqual(o.value, 'value')
392         o.value = 'blah'
393         self.assertEqual(o.value, 'blah')
394         self.assertEqual(o.props.value, 'blah')
395
396     def testDecoratorWithCall(self):
397         class C(GObject.GObject):
398             _value = 1
399
400             @GObject.Property(type=int, default=1, minimum=1, maximum=10)
401             def typedValue(self):
402                 return self._value
403
404             @typedValue.setter
405             def typedValue_setter(self, value):
406                 self._value = value
407
408         o = C()
409         self.assertEqual(o.typedValue, 1)
410         o.typedValue = 5
411         self.assertEqual(o.typedValue, 5)
412         self.assertEqual(o.props.typedValue, 5)
413
414     def testErrors(self):
415         self.assertRaises(TypeError, GObject.Property, type='str')
416         self.assertRaises(TypeError, GObject.Property, nick=False)
417         self.assertRaises(TypeError, GObject.Property, blurb=False)
418         # this never fail while bool is a subclass of int
419         # >>> bool.__bases__
420         # (<type 'int'>,)
421         # self.assertRaises(TypeError, GObject.Property, type=bool, default=0)
422         self.assertRaises(TypeError, GObject.Property, type=bool, default='ciao mamma')
423         self.assertRaises(TypeError, GObject.Property, type=bool)
424         self.assertRaises(TypeError, GObject.Property, type=object, default=0)
425         self.assertRaises(TypeError, GObject.Property, type=complex)
426         self.assertRaises(TypeError, GObject.Property, flags=-10)
427
428     def testDefaults(self):
429         GObject.Property(type=bool, default=True)
430         GObject.Property(type=bool, default=False)
431
432     def testNameWithUnderscore(self):
433         class C(GObject.GObject):
434             prop_name = GObject.Property(type=int)
435         o = C()
436         o.prop_name = 10
437         self.assertEqual(o.prop_name, 10)
438
439     def testRange(self):
440         maxint64 = 2 ** 62 - 1
441         minint64 = -2 ** 62 - 1
442         maxuint64 = 2 ** 63 - 1
443
444         types = [
445             (TYPE_INT, G_MININT, G_MAXINT),
446             (TYPE_UINT, 0, G_MAXUINT),
447             (TYPE_LONG, G_MINLONG, G_MAXLONG),
448             (TYPE_ULONG, 0, G_MAXULONG),
449             (TYPE_INT64, minint64, maxint64),
450             (TYPE_UINT64, 0, maxuint64),
451             ]
452
453         for gtype, min, max in types:
454             # Normal, everything is alright
455             prop = GObject.Property(type=gtype, minimum=min, maximum=max)
456             subtype = type('', (GObject.GObject,),
457                          dict(prop=prop))
458             self.assertEqual(subtype.props.prop.minimum, min)
459             self.assertEqual(subtype.props.prop.maximum, max)
460
461             # Lower than minimum
462             self.assertRaises(TypeError,
463                               GObject.Property, type=gtype, minimum=min - 1,
464                               maximum=max)
465
466             # Higher than maximum
467             self.assertRaises(TypeError,
468                               GObject.Property, type=gtype, minimum=min,
469                               maximum=max + 1)
470
471     def testMinMax(self):
472         class C(GObject.GObject):
473             prop_int = GObject.Property(type=int, minimum=1, maximum=100, default=1)
474             prop_float = GObject.Property(type=float, minimum=0.1, maximum=10.5, default=1.1)
475
476             def __init__(self):
477                 GObject.GObject.__init__(self)
478
479         # we test known-bad values here which cause Gtk-WARNING logs.
480         # Explicitly allow these for this test.
481         old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL)
482         try:
483             o = C()
484             self.assertEqual(o.prop_int, 1)
485
486             o.prop_int = 5
487             self.assertEqual(o.prop_int, 5)
488
489             o.prop_int = 0
490             self.assertEqual(o.prop_int, 5)
491
492             o.prop_int = 101
493             self.assertEqual(o.prop_int, 5)
494
495             self.assertEqual(o.prop_float, 1.1)
496
497             o.prop_float = 7.75
498             self.assertEqual(o.prop_float, 7.75)
499
500             o.prop_float = 0.09
501             self.assertEqual(o.prop_float, 7.75)
502
503             o.prop_float = 10.51
504             self.assertEqual(o.prop_float, 7.75)
505         finally:
506             GLib.log_set_always_fatal(old_mask)
507
508     def testMultipleInstances(self):
509         class C(GObject.GObject):
510             prop = GObject.Property(type=str, default='default')
511
512         o1 = C()
513         o2 = C()
514         self.assertEqual(o1.prop, 'default')
515         self.assertEqual(o2.prop, 'default')
516         o1.prop = 'value'
517         self.assertEqual(o1.prop, 'value')
518         self.assertEqual(o2.prop, 'default')
519
520     def testObjectProperty(self):
521         class PropertyObject(GObject.GObject):
522             obj = GObject.Property(type=GObject.GObject)
523
524         pobj1 = PropertyObject()
525         obj1_hash = hash(pobj1)
526         pobj2 = PropertyObject()
527
528         pobj2.obj = pobj1
529         del pobj1
530         pobj1 = pobj2.obj
531         self.assertEqual(hash(pobj1), obj1_hash)
532
533     def testObjectSubclassProperty(self):
534         class ObjectSubclass(GObject.GObject):
535             __gtype_name__ = 'ObjectSubclass'
536
537         class PropertyObjectSubclass(GObject.GObject):
538             obj = GObject.Property(type=ObjectSubclass)
539
540         PropertyObjectSubclass(obj=ObjectSubclass())
541
542     def testPropertySubclass(self):
543         # test for #470718
544         class A(GObject.GObject):
545             prop1 = GObject.Property(type=int)
546
547         class B(A):
548             prop2 = GObject.Property(type=int)
549
550         b = B()
551         b.prop2 = 10
552         self.assertEqual(b.prop2, 10)
553         b.prop1 = 20
554         self.assertEqual(b.prop1, 20)
555
556     def testPropertySubclassCustomSetter(self):
557         # test for #523352
558         class A(GObject.GObject):
559             def get_first(self):
560                 return 'first'
561             first = GObject.Property(type=str, getter=get_first)
562
563         class B(A):
564             def get_second(self):
565                 return 'second'
566             second = GObject.Property(type=str, getter=get_second)
567
568         a = A()
569         self.assertEqual(a.first, 'first')
570         self.assertRaises(TypeError, setattr, a, 'first', 'foo')
571
572         b = B()
573         self.assertEqual(b.first, 'first')
574         self.assertRaises(TypeError, setattr, b, 'first', 'foo')
575         self.assertEqual(b.second, 'second')
576         self.assertRaises(TypeError, setattr, b, 'second', 'foo')
577
578     def testPropertySubclassCustomSetterError(self):
579         try:
580             class A(GObject.GObject):
581                 def get_first(self):
582                     return 'first'
583                 first = GObject.Property(type=str, getter=get_first)
584
585                 def do_get_property(self, pspec):
586                     pass
587         except TypeError:
588             pass
589         else:
590             raise AssertionError
591
592     # Bug 587637.
593
594     def test_float_min(self):
595         GObject.Property(type=float, minimum=-1)
596         GObject.Property(type=GObject.TYPE_FLOAT, minimum=-1)
597         GObject.Property(type=GObject.TYPE_DOUBLE, minimum=-1)
598
599     # Bug 644039
600
601     def testReferenceCount(self):
602         # We can check directly if an object gets finalized, so we will
603         # observe it indirectly through the refcount of a member object.
604
605         # We create our dummy object and store its current refcount
606         o = object()
607         rc = sys.getrefcount(o)
608
609         # We add our object as a member to our newly created object we
610         # want to observe. Its refcount is increased by one.
611         t = PropertyObject(normal="test")
612         t.o = o
613         self.assertEqual(sys.getrefcount(o), rc + 1)
614
615         # Now we want to ensure we do not leak any references to our
616         # object with properties. If no ref is leaked, then when deleting
617         # the local reference to this object, its reference count shoud
618         # drop to zero, and our dummy object should loose one reference.
619         del t
620         self.assertEqual(sys.getrefcount(o), rc)
621
622     def testDocStringAsBlurb(self):
623         class C(GObject.GObject):
624             @GObject.Property
625             def blurbed(self):
626                 """blurbed doc string"""
627                 return 0
628
629         self.assertEqual(C.blurbed.blurb, 'blurbed doc string')
630
631
632 if __name__ == '__main__':
633     unittest.main()