d19970f38bf955c53364f071eabe713ddf724952
[platform/upstream/pygobject2.git] / tests / test_properties.py
1 # coding=utf-8
2
3 import sys
4 import struct
5 import types
6 import unittest
7
8 from gi.repository import GObject
9 from gi.repository.GObject import GType, new, PARAM_READWRITE, \
10     PARAM_CONSTRUCT, PARAM_READABLE, PARAM_WRITABLE, PARAM_CONSTRUCT_ONLY
11 from gi.repository.GObject import \
12     TYPE_INT, TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_INT64, \
13     TYPE_UINT64, TYPE_GTYPE, TYPE_INVALID, TYPE_NONE, TYPE_STRV, \
14     TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR, TYPE_BOOLEAN, TYPE_FLOAT, \
15     TYPE_DOUBLE, TYPE_POINTER, TYPE_BOXED, TYPE_PARAM, TYPE_OBJECT, \
16     TYPE_STRING, TYPE_PYOBJECT, TYPE_VARIANT
17
18 from gi.repository.GObject import \
19     G_MININT, G_MAXINT, G_MAXUINT, G_MINLONG, G_MAXLONG, G_MAXULONG, \
20     G_MAXUINT64, G_MAXINT64, G_MININT64
21
22 from gi.repository import Gio
23 from gi.repository import GLib
24 from gi.repository import Regress
25 from gi.repository import GIMarshallingTests
26 from gi._gobject import propertyhelper
27
28 if sys.version_info < (3, 0):
29     TEST_UTF8 = "\xe2\x99\xa5"
30     UNICODE_UTF8 = unicode(TEST_UTF8, 'UTF-8')
31 else:
32     TEST_UTF8 = "♥"
33     UNICODE_UTF8 = TEST_UTF8
34
35 from compathelper import _long
36
37
38 class PropertyObject(GObject.GObject):
39     normal = GObject.Property(type=str)
40     construct = GObject.Property(
41         type=str,
42         flags=PARAM_READWRITE | PARAM_CONSTRUCT, default='default')
43     construct_only = GObject.Property(
44         type=str,
45         flags=PARAM_READWRITE | PARAM_CONSTRUCT_ONLY)
46     uint64 = GObject.Property(
47         type=TYPE_UINT64, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
48
49     enum = GObject.Property(
50         type=Gio.SocketType, default=Gio.SocketType.STREAM)
51
52     boxed = GObject.Property(
53         type=GLib.Regex, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
54
55     flags = GObject.Property(
56         type=GIMarshallingTests.Flags, flags=PARAM_READWRITE | PARAM_CONSTRUCT,
57         default=GIMarshallingTests.Flags.VALUE1)
58
59     gtype = GObject.Property(
60         type=TYPE_GTYPE, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
61
62     strings = GObject.Property(
63         type=TYPE_STRV, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
64
65     variant = GObject.Property(
66         type=TYPE_VARIANT, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
67
68     variant_def = GObject.Property(
69         type=TYPE_VARIANT, flags=PARAM_READWRITE | PARAM_CONSTRUCT,
70         default=GLib.Variant('i', 42))
71
72
73 class PropertyInheritanceObject(Regress.TestObj):
74     # override property from the base class, with a different type
75     string = GObject.Property(type=int)
76
77     # a property entirely defined at the Python level
78     python_prop = GObject.Property(type=str)
79
80
81 class PropertySubClassObject(PropertyInheritanceObject):
82     # override property from the base class, with a different type
83     python_prop = GObject.Property(type=int)
84
85
86 class TestPropertyInheritanceObject(unittest.TestCase):
87     def test_override_gi_property(self):
88         self.assertNotEqual(Regress.TestObj.props.string.value_type,
89                             PropertyInheritanceObject.props.string.value_type)
90         obj = PropertyInheritanceObject()
91         self.assertEqual(type(obj.props.string), int)
92         obj.props.string = 4
93         self.assertEqual(obj.props.string, 4)
94
95     def test_override_python_property(self):
96         obj = PropertySubClassObject()
97         self.assertEqual(type(obj.props.python_prop), int)
98         obj.props.python_prop = 5
99         self.assertEqual(obj.props.python_prop, 5)
100
101
102 class TestPropertyObject(unittest.TestCase):
103     def test_get_set(self):
104         obj = PropertyObject()
105         obj.props.normal = "value"
106         self.assertEqual(obj.props.normal, "value")
107
108     def test_hasattr_on_object(self):
109         obj = PropertyObject()
110         self.assertTrue(hasattr(obj.props, "normal"))
111
112     def test_hasattr_on_class(self):
113         self.assertTrue(hasattr(PropertyObject.props, "normal"))
114
115     def test_set_on_class(self):
116         def set(obj):
117             obj.props.normal = "foobar"
118
119         self.assertRaises(TypeError, set, PropertyObject)
120
121     def test_iteration(self):
122         for obj in (PropertyObject.props, PropertyObject().props):
123             names = []
124             for pspec in obj:
125                 gtype = GType(pspec)
126                 self.assertEqual(gtype.parent.name, 'GParam')
127                 names.append(pspec.name)
128
129             names.sort()
130             self.assertEqual(names, ['boxed',
131                                      'construct',
132                                      'construct-only',
133                                      'enum',
134                                      'flags',
135                                      'gtype',
136                                      'normal',
137                                      'strings',
138                                      'uint64',
139                                      'variant',
140                                      'variant-def'])
141
142     def test_normal(self):
143         obj = new(PropertyObject, normal="123")
144         self.assertEqual(obj.props.normal, "123")
145         obj.set_property('normal', '456')
146         self.assertEqual(obj.props.normal, "456")
147         obj.props.normal = '789'
148         self.assertEqual(obj.props.normal, "789")
149
150     def test_construct(self):
151         obj = new(PropertyObject, construct="123")
152         self.assertEqual(obj.props.construct, "123")
153         obj.set_property('construct', '456')
154         self.assertEqual(obj.props.construct, "456")
155         obj.props.construct = '789'
156         self.assertEqual(obj.props.construct, "789")
157
158     def test_utf8(self):
159         obj = new(PropertyObject, construct_only=UNICODE_UTF8)
160         self.assertEqual(obj.props.construct_only, TEST_UTF8)
161         obj.set_property('construct', UNICODE_UTF8)
162         self.assertEqual(obj.props.construct, TEST_UTF8)
163         obj.props.normal = UNICODE_UTF8
164         self.assertEqual(obj.props.normal, TEST_UTF8)
165
166     def test_int_to_str(self):
167         obj = new(PropertyObject, construct_only=1)
168         self.assertEqual(obj.props.construct_only, '1')
169         obj.set_property('construct', '2')
170         self.assertEqual(obj.props.construct, '2')
171         obj.props.normal = 3
172         self.assertEqual(obj.props.normal, '3')
173
174     def test_construct_only(self):
175         obj = new(PropertyObject, construct_only="123")
176         self.assertEqual(obj.props.construct_only, "123")
177         self.assertRaises(TypeError,
178                           setattr, obj.props, 'construct_only', '456')
179         self.assertRaises(TypeError,
180                           obj.set_property, 'construct-only', '456')
181
182     def test_uint64(self):
183         obj = new(PropertyObject)
184         self.assertEqual(obj.props.uint64, 0)
185         obj.props.uint64 = _long(1)
186         self.assertEqual(obj.props.uint64, _long(1))
187         obj.props.uint64 = 1
188         self.assertEqual(obj.props.uint64, _long(1))
189
190         self.assertRaises((TypeError, OverflowError), obj.set_property, "uint64", _long(-1))
191         self.assertRaises((TypeError, OverflowError), obj.set_property, "uint64", -1)
192
193     def test_uint64_default_value(self):
194         try:
195             class TimeControl(GObject.GObject):
196                 __gproperties__ = {
197                     'time': (TYPE_UINT64, 'Time', 'Time',
198                              _long(0), (1 << 64) - 1, _long(0),
199                              PARAM_READABLE)
200                     }
201         except OverflowError:
202             (etype, ex) = sys.exc_info()[2:]
203             self.fail(str(ex))
204
205     def test_enum(self):
206         obj = new(PropertyObject)
207         self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
208         self.assertEqual(obj.enum, Gio.SocketType.STREAM)
209         obj.enum = Gio.SocketType.DATAGRAM
210         self.assertEqual(obj.props.enum, Gio.SocketType.DATAGRAM)
211         self.assertEqual(obj.enum, Gio.SocketType.DATAGRAM)
212         obj.props.enum = Gio.SocketType.STREAM
213         self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
214         self.assertEqual(obj.enum, Gio.SocketType.STREAM)
215         obj.props.enum = 2
216         self.assertEqual(obj.props.enum, Gio.SocketType.DATAGRAM)
217         self.assertEqual(obj.enum, Gio.SocketType.DATAGRAM)
218         obj.enum = 1
219         self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
220         self.assertEqual(obj.enum, Gio.SocketType.STREAM)
221
222         self.assertRaises(TypeError, setattr, obj, 'enum', 'foo')
223         self.assertRaises(TypeError, setattr, obj, 'enum', object())
224
225         self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType)
226         self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
227                           default=Gio.SocketProtocol.TCP)
228         self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
229                           default=object())
230         self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
231                           default=1)
232
233     def test_flags(self):
234         obj = new(PropertyObject)
235         self.assertEqual(obj.props.flags, GIMarshallingTests.Flags.VALUE1)
236         self.assertEqual(obj.flags, GIMarshallingTests.Flags.VALUE1)
237
238         obj.flags = GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3
239         self.assertEqual(obj.props.flags, GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3)
240         self.assertEqual(obj.flags, GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3)
241
242         self.assertRaises(TypeError, setattr, obj, 'flags', 'foo')
243         self.assertRaises(TypeError, setattr, obj, 'flags', object())
244         self.assertRaises(TypeError, setattr, obj, 'flags', None)
245
246         self.assertRaises(TypeError, GObject.Property,
247                           type=GIMarshallingTests.Flags, default='foo')
248         self.assertRaises(TypeError, GObject.Property,
249                           type=GIMarshallingTests.Flags, default=object())
250         self.assertRaises(TypeError, GObject.Property,
251                           type=GIMarshallingTests.Flags, default=None)
252
253     def test_gtype(self):
254         obj = new(PropertyObject)
255
256         self.assertEqual(obj.props.gtype, TYPE_NONE)
257         self.assertEqual(obj.gtype, TYPE_NONE)
258
259         obj.gtype = TYPE_UINT64
260         self.assertEqual(obj.props.gtype, TYPE_UINT64)
261         self.assertEqual(obj.gtype, TYPE_UINT64)
262
263         obj.gtype = TYPE_INVALID
264         self.assertEqual(obj.props.gtype, TYPE_INVALID)
265         self.assertEqual(obj.gtype, TYPE_INVALID)
266
267         # GType parameters do not support defaults in GLib
268         self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
269                           default=TYPE_INT)
270
271         # incompatible type
272         self.assertRaises(TypeError, setattr, obj, 'gtype', 'foo')
273         self.assertRaises(TypeError, setattr, obj, 'gtype', object())
274
275         self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
276                           default='foo')
277         self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
278                           default=object())
279
280         # set in constructor
281         obj = new(PropertyObject, gtype=TYPE_UINT)
282         self.assertEqual(obj.props.gtype, TYPE_UINT)
283         self.assertEqual(obj.gtype, TYPE_UINT)
284
285     def test_boxed(self):
286         obj = new(PropertyObject)
287
288         regex = GLib.Regex.new('[a-z]*', 0, 0)
289         obj.props.boxed = regex
290         self.assertEqual(obj.props.boxed.get_pattern(), '[a-z]*')
291         self.assertEqual(obj.boxed.get_pattern(), '[a-z]*')
292
293         self.assertRaises(TypeError, setattr, obj, 'boxed', 'foo')
294         self.assertRaises(TypeError, setattr, obj, 'boxed', object())
295
296     def test_strings(self):
297         obj = new(PropertyObject)
298
299         # Should work with actual GStrv objects as well as
300         # Python string lists
301         class GStrv(list):
302             __gtype__ = GObject.TYPE_STRV
303
304         self.assertEqual(obj.props.strings, GStrv([]))
305         self.assertEqual(obj.strings, GStrv([]))
306         self.assertEqual(obj.props.strings, [])
307         self.assertEqual(obj.strings, [])
308
309         obj.strings = ['hello', 'world']
310         self.assertEqual(obj.props.strings, ['hello', 'world'])
311         self.assertEqual(obj.strings, ['hello', 'world'])
312
313         obj.strings = GStrv(['hello', 'world'])
314         self.assertEqual(obj.props.strings, GStrv(['hello', 'world']))
315         self.assertEqual(obj.strings, GStrv(['hello', 'world']))
316
317         obj.strings = []
318         self.assertEqual(obj.strings, [])
319         obj.strings = GStrv([])
320         self.assertEqual(obj.strings, GStrv([]))
321
322         p = GObject.Property(type=TYPE_STRV, default=['hello', '1'])
323         self.assertEqual(p.default, ['hello', '1'])
324         self.assertEqual(p.type, TYPE_STRV)
325         p = GObject.Property(type=TYPE_STRV, default=GStrv(['hello', '1']))
326         self.assertEqual(p.default, ['hello', '1'])
327         self.assertEqual(p.type, TYPE_STRV)
328
329         # set in constructor
330         obj = new(PropertyObject, strings=['hello', 'world'])
331         self.assertEqual(obj.props.strings, ['hello', 'world'])
332         self.assertEqual(obj.strings, ['hello', 'world'])
333
334         # wrong types
335         self.assertRaises(TypeError, setattr, obj, 'strings', 1)
336         self.assertRaises(TypeError, setattr, obj, 'strings', 'foo')
337         self.assertRaises(TypeError, setattr, obj, 'strings', ['foo', 1])
338
339         self.assertRaises(TypeError, GObject.Property, type=TYPE_STRV,
340                           default=1)
341         self.assertRaises(TypeError, GObject.Property, type=TYPE_STRV,
342                           default='foo')
343         self.assertRaises(TypeError, GObject.Property, type=TYPE_STRV,
344                           default=['hello', 1])
345
346     def test_variant(self):
347         obj = new(PropertyObject)
348
349         self.assertEqual(obj.props.variant, None)
350         self.assertEqual(obj.variant, None)
351
352         obj.variant = GLib.Variant('s', 'hello')
353         self.assertEqual(obj.variant.print_(True), "'hello'")
354
355         obj.variant = GLib.Variant('b', True)
356         self.assertEqual(obj.variant.print_(True), "true")
357
358         obj.props.variant = GLib.Variant('y', 2)
359         self.assertEqual(obj.variant.print_(True), "byte 0x02")
360
361         obj.variant = None
362         self.assertEqual(obj.variant, None)
363
364         # set in constructor
365         obj = new(PropertyObject, variant=GLib.Variant('u', 5))
366         self.assertEqual(obj.props.variant.print_(True), 'uint32 5')
367
368         GObject.Property(type=TYPE_VARIANT, default=GLib.Variant('i', 1))
369
370         # incompatible types
371         self.assertRaises(TypeError, setattr, obj, 'variant', 'foo')
372         self.assertRaises(TypeError, setattr, obj, 'variant', 42)
373
374         self.assertRaises(TypeError, GObject.Property, type=TYPE_VARIANT,
375                           default='foo')
376         self.assertRaises(TypeError, GObject.Property, type=TYPE_VARIANT,
377                           default=object())
378
379     def test_variant_default(self):
380         obj = new(PropertyObject)
381
382         self.assertEqual(obj.props.variant_def.print_(True), '42')
383         self.assertEqual(obj.variant_def.print_(True), '42')
384
385         obj.props.variant_def = GLib.Variant('y', 2)
386         self.assertEqual(obj.variant_def.print_(True), "byte 0x02")
387
388         # set in constructor
389         obj = new(PropertyObject, variant_def=GLib.Variant('u', 5))
390         self.assertEqual(obj.props.variant_def.print_(True), 'uint32 5')
391
392     def test_range(self):
393         # kiwi code
394         def max(c):
395             return 2 ** ((8 * struct.calcsize(c)) - 1) - 1
396
397         def umax(c):
398             return 2 ** (8 * struct.calcsize(c)) - 1
399
400         maxint = max('i')
401         minint = -maxint - 1
402         maxuint = umax('I')
403         maxlong = max('l')
404         minlong = -maxlong - 1
405         maxulong = umax('L')
406         maxint64 = max('q')
407         minint64 = -maxint64 - 1
408         maxuint64 = umax('Q')
409
410         types_ = dict(int=(TYPE_INT, minint, maxint),
411                       uint=(TYPE_UINT, 0, maxuint),
412                       long=(TYPE_LONG, minlong, maxlong),
413                       ulong=(TYPE_ULONG, 0, maxulong),
414                       int64=(TYPE_INT64, minint64, maxint64),
415                       uint64=(TYPE_UINT64, 0, maxuint64))
416
417         def build_gproperties(types_):
418             d = {}
419             for key, (gtype, min, max) in types_.items():
420                 d[key] = (gtype, 'blurb', 'desc', min, max, 0,
421                           PARAM_READABLE | PARAM_WRITABLE)
422             return d
423
424         class RangeCheck(GObject.GObject):
425             __gproperties__ = build_gproperties(types_)
426
427             def __init__(self):
428                 self.values = {}
429                 GObject.GObject.__init__(self)
430
431             def do_set_property(self, pspec, value):
432                 self.values[pspec.name] = value
433
434             def do_get_property(self, pspec):
435                 return self.values.get(pspec.name, pspec.default_value)
436
437         self.assertEqual(RangeCheck.props.int.minimum, minint)
438         self.assertEqual(RangeCheck.props.int.maximum, maxint)
439         self.assertEqual(RangeCheck.props.uint.minimum, 0)
440         self.assertEqual(RangeCheck.props.uint.maximum, maxuint)
441         self.assertEqual(RangeCheck.props.long.minimum, minlong)
442         self.assertEqual(RangeCheck.props.long.maximum, maxlong)
443         self.assertEqual(RangeCheck.props.ulong.minimum, 0)
444         self.assertEqual(RangeCheck.props.ulong.maximum, maxulong)
445         self.assertEqual(RangeCheck.props.int64.minimum, minint64)
446         self.assertEqual(RangeCheck.props.int64.maximum, maxint64)
447         self.assertEqual(RangeCheck.props.uint64.minimum, 0)
448         self.assertEqual(RangeCheck.props.uint64.maximum, maxuint64)
449
450         obj = RangeCheck()
451         for key, (gtype, min, max) in types_.items():
452             self.assertEqual(obj.get_property(key),
453                              getattr(RangeCheck.props, key).default_value)
454
455             obj.set_property(key, min)
456             self.assertEqual(obj.get_property(key), min)
457
458             obj.set_property(key, max)
459             self.assertEqual(obj.get_property(key), max)
460
461     def test_multi(self):
462         obj = PropertyObject()
463         obj.set_properties(normal="foo",
464                            uint64=7)
465         normal, uint64 = obj.get_properties("normal", "uint64")
466         self.assertEqual(normal, "foo")
467         self.assertEqual(uint64, 7)
468
469
470 class TestProperty(unittest.TestCase):
471     def test_simple(self):
472         class C(GObject.GObject):
473             str = GObject.Property(type=str)
474             int = GObject.Property(type=int)
475             float = GObject.Property(type=float)
476             long = GObject.Property(type=_long)
477
478         self.assertTrue(hasattr(C.props, 'str'))
479         self.assertTrue(hasattr(C.props, 'int'))
480         self.assertTrue(hasattr(C.props, 'float'))
481         self.assertTrue(hasattr(C.props, 'long'))
482
483         o = C()
484         self.assertEqual(o.str, '')
485         o.str = 'str'
486         self.assertEqual(o.str, 'str')
487
488         self.assertEqual(o.int, 0)
489         o.int = 1138
490         self.assertEqual(o.int, 1138)
491
492         self.assertEqual(o.float, 0.0)
493         o.float = 3.14
494         self.assertEqual(o.float, 3.14)
495
496         self.assertEqual(o.long, _long(0))
497         o.long = _long(100)
498         self.assertEqual(o.long, _long(100))
499
500     def test_custom_getter(self):
501         class C(GObject.GObject):
502             def get_prop(self):
503                 return 'value'
504             prop = GObject.Property(getter=get_prop)
505
506         o = C()
507         self.assertEqual(o.prop, 'value')
508         self.assertRaises(TypeError, setattr, o, 'prop', 'xxx')
509
510     def test_custom_setter(self):
511         class C(GObject.GObject):
512             def set_prop(self, value):
513                 self._value = value
514             prop = GObject.Property(setter=set_prop)
515
516             def __init__(self):
517                 self._value = None
518                 GObject.GObject.__init__(self)
519
520         o = C()
521         self.assertEqual(o._value, None)
522         o.prop = 'bar'
523         self.assertEqual(o._value, 'bar')
524         self.assertRaises(TypeError, getattr, o, 'prop')
525
526     def test_decorator_default(self):
527         class C(GObject.GObject):
528             _value = 'value'
529
530             @GObject.Property
531             def value(self):
532                 return self._value
533
534             @value.setter
535             def value_setter(self, value):
536                 self._value = value
537
538         o = C()
539         self.assertEqual(o.value, 'value')
540         o.value = 'blah'
541         self.assertEqual(o.value, 'blah')
542         self.assertEqual(o.props.value, 'blah')
543
544     def test_decorator_private_setter(self):
545         class C(GObject.GObject):
546             _value = 'value'
547
548             @GObject.Property
549             def value(self):
550                 return self._value
551
552             @value.setter
553             def _set_value(self, value):
554                 self._value = value
555
556         o = C()
557         self.assertEqual(o.value, 'value')
558         o.value = 'blah'
559         self.assertEqual(o.value, 'blah')
560         self.assertEqual(o.props.value, 'blah')
561
562     def test_decorator_with_call(self):
563         class C(GObject.GObject):
564             _value = 1
565
566             @GObject.Property(type=int, default=1, minimum=1, maximum=10)
567             def typedValue(self):
568                 return self._value
569
570             @typedValue.setter
571             def typedValue_setter(self, value):
572                 self._value = value
573
574         o = C()
575         self.assertEqual(o.typedValue, 1)
576         o.typedValue = 5
577         self.assertEqual(o.typedValue, 5)
578         self.assertEqual(o.props.typedValue, 5)
579
580     def test_errors(self):
581         self.assertRaises(TypeError, GObject.Property, type='str')
582         self.assertRaises(TypeError, GObject.Property, nick=False)
583         self.assertRaises(TypeError, GObject.Property, blurb=False)
584         # this never fail while bool is a subclass of int
585         # >>> bool.__bases__
586         # (<type 'int'>,)
587         # self.assertRaises(TypeError, GObject.Property, type=bool, default=0)
588         self.assertRaises(TypeError, GObject.Property, type=bool, default='ciao mamma')
589         self.assertRaises(TypeError, GObject.Property, type=bool)
590         self.assertRaises(TypeError, GObject.Property, type=object, default=0)
591         self.assertRaises(TypeError, GObject.Property, type=complex)
592         self.assertRaises(TypeError, GObject.Property, flags=-10)
593
594     def test_defaults(self):
595         GObject.Property(type=bool, default=True)
596         GObject.Property(type=bool, default=False)
597
598     def test_name_with_underscore(self):
599         class C(GObject.GObject):
600             prop_name = GObject.Property(type=int)
601         o = C()
602         o.prop_name = 10
603         self.assertEqual(o.prop_name, 10)
604
605     def test_range(self):
606         types_ = [
607             (TYPE_INT, G_MININT, G_MAXINT),
608             (TYPE_UINT, 0, G_MAXUINT),
609             (TYPE_LONG, G_MINLONG, G_MAXLONG),
610             (TYPE_ULONG, 0, G_MAXULONG),
611             (TYPE_INT64, G_MININT64, G_MAXINT64),
612             (TYPE_UINT64, 0, G_MAXUINT64),
613             ]
614
615         for gtype, min, max in types_:
616             # Normal, everything is alright
617             prop = GObject.Property(type=gtype, minimum=min, maximum=max)
618             subtype = type('', (GObject.GObject,), dict(prop=prop))
619             self.assertEqual(subtype.props.prop.minimum, min)
620             self.assertEqual(subtype.props.prop.maximum, max)
621
622             # Lower than minimum
623             self.assertRaises(TypeError,
624                               GObject.Property, type=gtype, minimum=min - 1,
625                               maximum=max)
626
627             # Higher than maximum
628             self.assertRaises(TypeError,
629                               GObject.Property, type=gtype, minimum=min,
630                               maximum=max + 1)
631
632     def test_min_max(self):
633         class C(GObject.GObject):
634             prop_int = GObject.Property(type=int, minimum=1, maximum=100, default=1)
635             prop_float = GObject.Property(type=float, minimum=0.1, maximum=10.5, default=1.1)
636
637             def __init__(self):
638                 GObject.GObject.__init__(self)
639
640         # we test known-bad values here which cause Gtk-WARNING logs.
641         # Explicitly allow these for this test.
642         old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL)
643         try:
644             o = C()
645             self.assertEqual(o.prop_int, 1)
646
647             o.prop_int = 5
648             self.assertEqual(o.prop_int, 5)
649
650             o.prop_int = 0
651             self.assertEqual(o.prop_int, 5)
652
653             o.prop_int = 101
654             self.assertEqual(o.prop_int, 5)
655
656             self.assertEqual(o.prop_float, 1.1)
657
658             o.prop_float = 7.75
659             self.assertEqual(o.prop_float, 7.75)
660
661             o.prop_float = 0.09
662             self.assertEqual(o.prop_float, 7.75)
663
664             o.prop_float = 10.51
665             self.assertEqual(o.prop_float, 7.75)
666         finally:
667             GLib.log_set_always_fatal(old_mask)
668
669     def test_multiple_instances(self):
670         class C(GObject.GObject):
671             prop = GObject.Property(type=str, default='default')
672
673         o1 = C()
674         o2 = C()
675         self.assertEqual(o1.prop, 'default')
676         self.assertEqual(o2.prop, 'default')
677         o1.prop = 'value'
678         self.assertEqual(o1.prop, 'value')
679         self.assertEqual(o2.prop, 'default')
680
681     def test_object_property(self):
682         class PropertyObject(GObject.GObject):
683             obj = GObject.Property(type=GObject.GObject)
684
685         pobj1 = PropertyObject()
686         obj1_hash = hash(pobj1)
687         pobj2 = PropertyObject()
688
689         pobj2.obj = pobj1
690         del pobj1
691         pobj1 = pobj2.obj
692         self.assertEqual(hash(pobj1), obj1_hash)
693
694     def test_object_subclass_property(self):
695         class ObjectSubclass(GObject.GObject):
696             __gtype_name__ = 'ObjectSubclass'
697
698         class PropertyObjectSubclass(GObject.GObject):
699             obj = GObject.Property(type=ObjectSubclass)
700
701         PropertyObjectSubclass(obj=ObjectSubclass())
702
703     def test_property_subclass(self):
704         # test for #470718
705         class A(GObject.GObject):
706             prop1 = GObject.Property(type=int)
707
708         class B(A):
709             prop2 = GObject.Property(type=int)
710
711         b = B()
712         b.prop2 = 10
713         self.assertEqual(b.prop2, 10)
714         b.prop1 = 20
715         self.assertEqual(b.prop1, 20)
716
717     def test_property_subclass_c(self):
718         class A(GIMarshallingTests.PropertiesObject):
719             prop1 = GObject.Property(type=int)
720
721         a = A()
722         a.prop1 = 10
723         self.assertEqual(a.prop1, 10)
724
725         # also has parent properties
726         a.props.some_int = 20
727         self.assertEqual(a.props.some_int, 20)
728
729     def test_property_subclass_custom_setter(self):
730         # test for #523352
731         class A(GObject.GObject):
732             def get_first(self):
733                 return 'first'
734             first = GObject.Property(type=str, getter=get_first)
735
736         class B(A):
737             def get_second(self):
738                 return 'second'
739             second = GObject.Property(type=str, getter=get_second)
740
741         a = A()
742         self.assertEqual(a.first, 'first')
743         self.assertRaises(TypeError, setattr, a, 'first', 'foo')
744
745         b = B()
746         self.assertEqual(b.first, 'first')
747         self.assertRaises(TypeError, setattr, b, 'first', 'foo')
748         self.assertEqual(b.second, 'second')
749         self.assertRaises(TypeError, setattr, b, 'second', 'foo')
750
751     def test_property_subclass_custom_setter_error(self):
752         try:
753             class A(GObject.GObject):
754                 def get_first(self):
755                     return 'first'
756                 first = GObject.Property(type=str, getter=get_first)
757
758                 def do_get_property(self, pspec):
759                     pass
760         except TypeError:
761             pass
762         else:
763             raise AssertionError
764
765     # Bug 587637.
766
767     def test_float_min(self):
768         GObject.Property(type=float, minimum=-1)
769         GObject.Property(type=GObject.TYPE_FLOAT, minimum=-1)
770         GObject.Property(type=GObject.TYPE_DOUBLE, minimum=-1)
771
772     # Bug 644039
773
774     def test_reference_count(self):
775         # We can check directly if an object gets finalized, so we will
776         # observe it indirectly through the refcount of a member object.
777
778         # We create our dummy object and store its current refcount
779         o = object()
780         rc = sys.getrefcount(o)
781
782         # We add our object as a member to our newly created object we
783         # want to observe. Its refcount is increased by one.
784         t = PropertyObject(normal="test")
785         t.o = o
786         self.assertEqual(sys.getrefcount(o), rc + 1)
787
788         # Now we want to ensure we do not leak any references to our
789         # object with properties. If no ref is leaked, then when deleting
790         # the local reference to this object, its reference count shoud
791         # drop to zero, and our dummy object should loose one reference.
792         del t
793         self.assertEqual(sys.getrefcount(o), rc)
794
795     def test_doc_strings(self):
796         class C(GObject.GObject):
797             foo_blurbed = GObject.Property(type=int, blurb='foo_blurbed doc string')
798
799             @GObject.Property
800             def foo_getter(self):
801                 """foo_getter doc string"""
802                 return 0
803
804         self.assertEqual(C.foo_blurbed.blurb, 'foo_blurbed doc string')
805         self.assertEqual(C.foo_blurbed.__doc__, 'foo_blurbed doc string')
806
807         self.assertEqual(C.foo_getter.blurb, 'foo_getter doc string')
808         self.assertEqual(C.foo_getter.__doc__, 'foo_getter doc string')
809
810     def test_python_to_glib_type_mapping(self):
811         tester = GObject.Property()
812         self.assertEqual(tester._type_from_python(int), GObject.TYPE_INT)
813         if sys.version_info < (3, 0):
814             self.assertEqual(tester._type_from_python(long), GObject.TYPE_LONG)
815         self.assertEqual(tester._type_from_python(bool), GObject.TYPE_BOOLEAN)
816         self.assertEqual(tester._type_from_python(float), GObject.TYPE_DOUBLE)
817         self.assertEqual(tester._type_from_python(str), GObject.TYPE_STRING)
818         self.assertEqual(tester._type_from_python(object), GObject.TYPE_PYOBJECT)
819
820         self.assertEqual(tester._type_from_python(GObject.GObject), GObject.GObject.__gtype__)
821         self.assertEqual(tester._type_from_python(GObject.GEnum), GObject.GEnum.__gtype__)
822         self.assertEqual(tester._type_from_python(GObject.GFlags), GObject.GFlags.__gtype__)
823         self.assertEqual(tester._type_from_python(GObject.GBoxed), GObject.GBoxed.__gtype__)
824
825         for type_ in [TYPE_NONE, TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR,
826                       TYPE_INT, TYPE_UINT, TYPE_BOOLEAN, TYPE_LONG,
827                       TYPE_ULONG, TYPE_INT64, TYPE_UINT64,
828                       TYPE_FLOAT, TYPE_DOUBLE, TYPE_POINTER,
829                       TYPE_BOXED, TYPE_PARAM, TYPE_OBJECT, TYPE_STRING,
830                       TYPE_PYOBJECT, TYPE_GTYPE, TYPE_STRV]:
831             self.assertEqual(tester._type_from_python(type_), type_)
832
833         self.assertRaises(TypeError, tester._type_from_python, types.CodeType)
834
835
836 class TestInstallProperties(unittest.TestCase):
837     # These tests only test how signalhelper.install_signals works
838     # with the __gsignals__ dict and therefore does not need to use
839     # GObject as a base class because that would automatically call
840     # install_signals within the meta-class.
841     class Base(object):
842         __gproperties__ = {'test': (0, '', '', 0, 0, 0, 0)}
843
844     class Sub1(Base):
845         pass
846
847     class Sub2(Base):
848         @GObject.Property(type=int)
849         def sub2test(self):
850             return 123
851
852     class ClassWithPropertyAndGetterVFunc(object):
853         @GObject.Property(type=int)
854         def sub2test(self):
855             return 123
856
857         def do_get_property(self, name):
858             return 321
859
860     class ClassWithPropertyRedefined(object):
861         __gproperties__ = {'test': (0, '', '', 0, 0, 0, 0)}
862         test = GObject.Property(type=int)
863
864     def setUp(self):
865         self.assertEqual(len(self.Base.__gproperties__), 1)
866         propertyhelper.install_properties(self.Base)
867         self.assertEqual(len(self.Base.__gproperties__), 1)
868
869     def test_subclass_without_properties_is_not_modified(self):
870         self.assertFalse('__gproperties__' in self.Sub1.__dict__)
871         propertyhelper.install_properties(self.Sub1)
872         self.assertFalse('__gproperties__' in self.Sub1.__dict__)
873
874     def test_subclass_with_decorator_gets_gproperties_dict(self):
875         # Sub2 has Property instances but will not have a __gproperties__
876         # until install_properties is called
877         self.assertFalse('__gproperties__' in self.Sub2.__dict__)
878         self.assertFalse('do_get_property' in self.Sub2.__dict__)
879         self.assertFalse('do_set_property' in self.Sub2.__dict__)
880
881         propertyhelper.install_properties(self.Sub2)
882         self.assertTrue('__gproperties__' in self.Sub2.__dict__)
883         self.assertEqual(len(self.Base.__gproperties__), 1)
884         self.assertEqual(len(self.Sub2.__gproperties__), 1)
885         self.assertTrue('sub2test' in self.Sub2.__gproperties__)
886
887         # get/set vfuncs should have been added
888         self.assertTrue('do_get_property' in self.Sub2.__dict__)
889         self.assertTrue('do_set_property' in self.Sub2.__dict__)
890
891     def test_object_with_property_and_do_get_property_vfunc_raises(self):
892         self.assertRaises(TypeError, propertyhelper.install_properties,
893                           self.ClassWithPropertyAndGetterVFunc)
894
895     def test_same_name_property_definitions_raises(self):
896         self.assertRaises(ValueError, propertyhelper.install_properties,
897                           self.ClassWithPropertyRedefined)
898
899 if __name__ == '__main__':
900     unittest.main()