1import os
2import gc
3import sys
4import struct
5import types
6import unittest
7import tempfile
8
9import pytest
10
11from gi.repository import GObject
12from gi.repository.GObject import ParamFlags, GType, new
13from gi.repository.GObject import \
14    TYPE_INT, TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_INT64, \
15    TYPE_UINT64, TYPE_GTYPE, TYPE_INVALID, TYPE_NONE, TYPE_STRV, \
16    TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR, TYPE_BOOLEAN, TYPE_FLOAT, \
17    TYPE_DOUBLE, TYPE_POINTER, TYPE_BOXED, TYPE_PARAM, TYPE_OBJECT, \
18    TYPE_STRING, TYPE_PYOBJECT, TYPE_VARIANT
19
20from gi.repository.GLib import \
21    MININT, MAXINT, MAXUINT, MINLONG, MAXLONG, MAXULONG, \
22    MAXUINT64, MAXINT64, MININT64
23
24from gi.repository import Gio
25from gi.repository import GLib
26from gi.repository import GIMarshallingTests
27from gi.repository import Regress
28from gi import _propertyhelper as propertyhelper
29
30from .helper import capture_glib_warnings
31
32
33class PropertyObject(GObject.GObject):
34    normal = GObject.Property(type=str)
35    construct = GObject.Property(
36        type=str,
37        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT,
38        default='default')
39
40    construct_only = GObject.Property(
41        type=str,
42        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT_ONLY)
43
44    uint64 = GObject.Property(
45        type=TYPE_UINT64,
46        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT)
47
48    enum = GObject.Property(
49        type=Gio.SocketType, default=Gio.SocketType.STREAM)
50
51    boxed = GObject.Property(
52        type=GLib.Regex,
53        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT)
54
55    flags = GObject.Property(
56        type=GIMarshallingTests.Flags,
57        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT,
58        default=GIMarshallingTests.Flags.VALUE1)
59
60    gtype = GObject.Property(
61        type=TYPE_GTYPE,
62        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT)
63
64    strings = GObject.Property(
65        type=TYPE_STRV,
66        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT)
67
68    variant = GObject.Property(
69        type=TYPE_VARIANT,
70        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT)
71
72    variant_def = GObject.Property(
73        type=TYPE_VARIANT,
74        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT,
75        default=GLib.Variant('i', 42))
76
77    interface = GObject.Property(
78        type=Gio.File,
79        flags=ParamFlags.READABLE | ParamFlags.WRITABLE | ParamFlags.CONSTRUCT)
80
81
82class PropertyInheritanceObject(Regress.TestObj):
83    # override property from the base class, with a different type
84    string = GObject.Property(type=int)
85
86    # a property entirely defined at the Python level
87    python_prop = GObject.Property(type=str)
88
89
90class PropertySubClassObject(PropertyInheritanceObject):
91    # override property from the base class, with a different type
92    python_prop = GObject.Property(type=int)
93
94
95class TestPropertyInheritanceObject(unittest.TestCase):
96    def test_override_gi_property(self):
97        self.assertNotEqual(Regress.TestObj.props.string.value_type,
98                            PropertyInheritanceObject.props.string.value_type)
99        obj = PropertyInheritanceObject()
100        self.assertEqual(type(obj.props.string), int)
101        obj.props.string = 4
102        self.assertEqual(obj.props.string, 4)
103
104    def test_override_python_property(self):
105        obj = PropertySubClassObject()
106        self.assertEqual(type(obj.props.python_prop), int)
107        obj.props.python_prop = 5
108        self.assertEqual(obj.props.python_prop, 5)
109
110
111class TestPropertyObject(unittest.TestCase):
112    def test_get_set(self):
113        obj = PropertyObject()
114        obj.props.normal = "value"
115        self.assertEqual(obj.props.normal, "value")
116
117    def test_hasattr_on_object(self):
118        obj = PropertyObject()
119        self.assertTrue(hasattr(obj.props, "normal"))
120
121    def test_hasattr_on_class(self):
122        self.assertTrue(hasattr(PropertyObject.props, "normal"))
123
124    def test_set_on_class(self):
125        def set(obj):
126            obj.props.normal = "foobar"
127
128        self.assertRaises(TypeError, set, PropertyObject)
129
130    def test_iteration(self):
131        for obj in (PropertyObject.props, PropertyObject().props):
132            names = []
133            for pspec in obj:
134                gtype = GType(pspec)
135                self.assertEqual(gtype.parent.name, 'GParam')
136                names.append(pspec.name)
137
138            names.sort()
139            self.assertEqual(names, ['boxed',
140                                     'construct',
141                                     'construct-only',
142                                     'enum',
143                                     'flags',
144                                     'gtype',
145                                     'interface',
146                                     'normal',
147                                     'strings',
148                                     'uint64',
149                                     'variant',
150                                     'variant-def'])
151
152    def test_normal(self):
153        obj = new(PropertyObject, normal="123")
154        self.assertEqual(obj.props.normal, "123")
155        obj.set_property('normal', '456')
156        self.assertEqual(obj.props.normal, "456")
157        obj.props.normal = '789'
158        self.assertEqual(obj.props.normal, "789")
159
160    def test_construct(self):
161        obj = new(PropertyObject, construct="123")
162        self.assertEqual(obj.props.construct, "123")
163        obj.set_property('construct', '456')
164        self.assertEqual(obj.props.construct, "456")
165        obj.props.construct = '789'
166        self.assertEqual(obj.props.construct, "789")
167
168    def test_utf8(self):
169        test_utf8 = "♥"
170        unicode_utf8 = u"♥"
171        obj = new(PropertyObject, construct_only=unicode_utf8)
172        self.assertEqual(obj.props.construct_only, test_utf8)
173        obj.set_property('construct', unicode_utf8)
174        self.assertEqual(obj.props.construct, test_utf8)
175        obj.props.normal = unicode_utf8
176        self.assertEqual(obj.props.normal, test_utf8)
177
178    def test_utf8_lone_surrogate(self):
179        obj = PropertyObject()
180        with pytest.raises(TypeError):
181            obj.set_property('construct', '\ud83d')
182
183    def test_int_to_str(self):
184        obj = new(PropertyObject, construct_only=1)
185        self.assertEqual(obj.props.construct_only, '1')
186        obj.set_property('construct', '2')
187        self.assertEqual(obj.props.construct, '2')
188        obj.props.normal = 3
189        self.assertEqual(obj.props.normal, '3')
190
191    def test_construct_only(self):
192        obj = new(PropertyObject, construct_only="123")
193        self.assertEqual(obj.props.construct_only, "123")
194        self.assertRaises(TypeError,
195                          setattr, obj.props, 'construct_only', '456')
196        self.assertRaises(TypeError,
197                          obj.set_property, 'construct-only', '456')
198
199    def test_uint64(self):
200        obj = new(PropertyObject)
201        self.assertEqual(obj.props.uint64, 0)
202        obj.props.uint64 = 1
203        self.assertEqual(obj.props.uint64, 1)
204        obj.props.uint64 = 1
205        self.assertEqual(obj.props.uint64, 1)
206
207        self.assertRaises((TypeError, OverflowError), obj.set_property, "uint64", -1)
208        self.assertRaises((TypeError, OverflowError), obj.set_property, "uint64", -1)
209
210    def test_uint64_default_value(self):
211        try:
212            class TimeControl(GObject.GObject):
213                __gproperties__ = {
214                    'time': (TYPE_UINT64, 'Time', 'Time',
215                             0, (1 << 64) - 1, 0,
216                             ParamFlags.READABLE)
217                    }
218        except OverflowError:
219            (etype, ex) = sys.exc_info()[2:]
220            self.fail(str(ex))
221
222    def test_enum(self):
223        obj = new(PropertyObject)
224        self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
225        self.assertEqual(obj.enum, Gio.SocketType.STREAM)
226        obj.enum = Gio.SocketType.DATAGRAM
227        self.assertEqual(obj.props.enum, Gio.SocketType.DATAGRAM)
228        self.assertEqual(obj.enum, Gio.SocketType.DATAGRAM)
229        obj.props.enum = Gio.SocketType.STREAM
230        self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
231        self.assertEqual(obj.enum, Gio.SocketType.STREAM)
232        obj.props.enum = 2
233        self.assertEqual(obj.props.enum, Gio.SocketType.DATAGRAM)
234        self.assertEqual(obj.enum, Gio.SocketType.DATAGRAM)
235        obj.enum = 1
236        self.assertEqual(obj.props.enum, Gio.SocketType.STREAM)
237        self.assertEqual(obj.enum, Gio.SocketType.STREAM)
238
239        self.assertRaises(TypeError, setattr, obj, 'enum', 'foo')
240        self.assertRaises(TypeError, setattr, obj, 'enum', object())
241
242        self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType)
243        self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
244                          default=Gio.SocketProtocol.TCP)
245        self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
246                          default=object())
247        self.assertRaises(TypeError, GObject.Property, type=Gio.SocketType,
248                          default=1)
249
250    def test_repr(self):
251        prop = GObject.Property(type=int)
252        assert repr(prop) == "<GObject Property (uninitialized) (gint)>"
253
254    def test_flags(self):
255        obj = new(PropertyObject)
256        self.assertEqual(obj.props.flags, GIMarshallingTests.Flags.VALUE1)
257        self.assertEqual(obj.flags, GIMarshallingTests.Flags.VALUE1)
258
259        obj.flags = GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3
260        self.assertEqual(obj.props.flags, GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3)
261        self.assertEqual(obj.flags, GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE3)
262
263        self.assertRaises(TypeError, setattr, obj, 'flags', 'foo')
264        self.assertRaises(TypeError, setattr, obj, 'flags', object())
265        self.assertRaises(TypeError, setattr, obj, 'flags', None)
266
267        self.assertRaises(TypeError, GObject.Property,
268                          type=GIMarshallingTests.Flags, default='foo')
269        self.assertRaises(TypeError, GObject.Property,
270                          type=GIMarshallingTests.Flags, default=object())
271        self.assertRaises(TypeError, GObject.Property,
272                          type=GIMarshallingTests.Flags, default=None)
273
274    def test_gtype(self):
275        obj = new(PropertyObject)
276
277        self.assertEqual(obj.props.gtype, TYPE_NONE)
278        self.assertEqual(obj.gtype, TYPE_NONE)
279
280        obj.gtype = TYPE_UINT64
281        self.assertEqual(obj.props.gtype, TYPE_UINT64)
282        self.assertEqual(obj.gtype, TYPE_UINT64)
283
284        obj.gtype = TYPE_INVALID
285        self.assertEqual(obj.props.gtype, TYPE_INVALID)
286        self.assertEqual(obj.gtype, TYPE_INVALID)
287
288        # GType parameters do not support defaults in GLib
289        self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
290                          default=TYPE_INT)
291
292        # incompatible type
293        self.assertRaises(TypeError, setattr, obj, 'gtype', 'foo')
294        self.assertRaises(TypeError, setattr, obj, 'gtype', object())
295
296        self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
297                          default='foo')
298        self.assertRaises(TypeError, GObject.Property, type=TYPE_GTYPE,
299                          default=object())
300
301        # set in constructor
302        obj = new(PropertyObject, gtype=TYPE_UINT)
303        self.assertEqual(obj.props.gtype, TYPE_UINT)
304        self.assertEqual(obj.gtype, TYPE_UINT)
305
306    def test_boxed(self):
307        obj = new(PropertyObject)
308
309        regex = GLib.Regex.new('[a-z]*', 0, 0)
310        obj.props.boxed = regex
311        self.assertEqual(obj.props.boxed.get_pattern(), '[a-z]*')
312        self.assertEqual(obj.boxed.get_pattern(), '[a-z]*')
313
314        self.assertRaises(TypeError, setattr, obj, 'boxed', 'foo')
315        self.assertRaises(TypeError, setattr, obj, 'boxed', object())
316
317    def test_strings(self):
318        obj = new(PropertyObject)
319
320        # Should work with actual GStrv objects as well as
321        # Python string lists
322        class GStrv(list):
323            __gtype__ = GObject.TYPE_STRV
324
325        self.assertEqual(obj.props.strings, GStrv([]))
326        self.assertEqual(obj.strings, GStrv([]))
327        self.assertEqual(obj.props.strings, [])
328        self.assertEqual(obj.strings, [])
329
330        obj.strings = ['hello', 'world']
331        self.assertEqual(obj.props.strings, ['hello', 'world'])
332        self.assertEqual(obj.strings, ['hello', 'world'])
333
334        obj.strings = GStrv(['hello', 'world'])
335        self.assertEqual(obj.props.strings, GStrv(['hello', 'world']))
336        self.assertEqual(obj.strings, GStrv(['hello', 'world']))
337
338        obj.strings = []
339        self.assertEqual(obj.strings, [])
340        obj.strings = GStrv([])
341        self.assertEqual(obj.strings, GStrv([]))
342
343        p = GObject.Property(type=TYPE_STRV, default=['hello', '1'])
344        self.assertEqual(p.default, ['hello', '1'])
345        self.assertEqual(p.type, TYPE_STRV)
346        p = GObject.Property(type=TYPE_STRV, default=GStrv(['hello', '1']))
347        self.assertEqual(p.default, ['hello', '1'])
348        self.assertEqual(p.type, TYPE_STRV)
349
350        # set in constructor
351        obj = new(PropertyObject, strings=['hello', 'world'])
352        self.assertEqual(obj.props.strings, ['hello', 'world'])
353        self.assertEqual(obj.strings, ['hello', 'world'])
354
355        # wrong types
356        self.assertRaises(TypeError, setattr, obj, 'strings', 1)
357        self.assertRaises(TypeError, setattr, obj, 'strings', 'foo')
358        self.assertRaises(TypeError, setattr, obj, 'strings', ['foo', 1])
359
360        self.assertRaises(TypeError, GObject.Property, type=TYPE_STRV,
361                          default=1)
362        self.assertRaises(TypeError, GObject.Property, type=TYPE_STRV,
363                          default='foo')
364        self.assertRaises(TypeError, GObject.Property, type=TYPE_STRV,
365                          default=['hello', 1])
366
367    def test_variant(self):
368        obj = new(PropertyObject)
369
370        self.assertEqual(obj.props.variant, None)
371        self.assertEqual(obj.variant, None)
372
373        obj.variant = GLib.Variant('s', 'hello')
374        self.assertEqual(obj.variant.print_(True), "'hello'")
375
376        obj.variant = GLib.Variant('b', True)
377        self.assertEqual(obj.variant.print_(True), "true")
378
379        obj.props.variant = GLib.Variant('y', 2)
380        self.assertEqual(obj.variant.print_(True), "byte 0x02")
381
382        obj.variant = None
383        self.assertEqual(obj.variant, None)
384
385        # set in constructor
386        obj = new(PropertyObject, variant=GLib.Variant('u', 5))
387        self.assertEqual(obj.props.variant.print_(True), 'uint32 5')
388
389        GObject.Property(type=TYPE_VARIANT, default=GLib.Variant('i', 1))
390
391        # incompatible types
392        self.assertRaises(TypeError, setattr, obj, 'variant', 'foo')
393        self.assertRaises(TypeError, setattr, obj, 'variant', 42)
394
395        self.assertRaises(TypeError, GObject.Property, type=TYPE_VARIANT,
396                          default='foo')
397        self.assertRaises(TypeError, GObject.Property, type=TYPE_VARIANT,
398                          default=object())
399
400    def test_variant_default(self):
401        obj = new(PropertyObject)
402
403        self.assertEqual(obj.props.variant_def.print_(True), '42')
404        self.assertEqual(obj.variant_def.print_(True), '42')
405
406        obj.props.variant_def = GLib.Variant('y', 2)
407        self.assertEqual(obj.variant_def.print_(True), "byte 0x02")
408
409        # set in constructor
410        obj = new(PropertyObject, variant_def=GLib.Variant('u', 5))
411        self.assertEqual(obj.props.variant_def.print_(True), 'uint32 5')
412
413    def test_interface(self):
414        obj = new(PropertyObject)
415
416        path = os.path.join(tempfile.gettempdir(), "some", "path")
417        file = Gio.File.new_for_path(path)
418        obj.props.interface = file
419        self.assertEqual(obj.props.interface.get_path(), path)
420        self.assertEqual(obj.interface.get_path(), path)
421
422        self.assertRaises(TypeError, setattr, obj, 'interface', 'foo')
423        self.assertRaises(TypeError, setattr, obj, 'interface', object())
424
425    def test_range(self):
426        # kiwi code
427        def max(c):
428            return 2 ** ((8 * struct.calcsize(c)) - 1) - 1
429
430        def umax(c):
431            return 2 ** (8 * struct.calcsize(c)) - 1
432
433        maxint = max('i')
434        minint = -maxint - 1
435        maxuint = umax('I')
436        maxlong = max('l')
437        minlong = -maxlong - 1
438        maxulong = umax('L')
439        maxint64 = max('q')
440        minint64 = -maxint64 - 1
441        maxuint64 = umax('Q')
442
443        types_ = dict(int=(TYPE_INT, minint, maxint),
444                      uint=(TYPE_UINT, 0, maxuint),
445                      long=(TYPE_LONG, minlong, maxlong),
446                      ulong=(TYPE_ULONG, 0, maxulong),
447                      int64=(TYPE_INT64, minint64, maxint64),
448                      uint64=(TYPE_UINT64, 0, maxuint64))
449
450        def build_gproperties(types_):
451            d = {}
452            for key, (gtype, min, max) in types_.items():
453                d[key] = (gtype, 'blurb', 'desc', min, max, 0,
454                          ParamFlags.READABLE | ParamFlags.WRITABLE)
455            return d
456
457        class RangeCheck(GObject.GObject):
458            __gproperties__ = build_gproperties(types_)
459
460            def __init__(self):
461                self.values = {}
462                GObject.GObject.__init__(self)
463
464            def do_set_property(self, pspec, value):
465                self.values[pspec.name] = value
466
467            def do_get_property(self, pspec):
468                return self.values.get(pspec.name, pspec.default_value)
469
470        self.assertEqual(RangeCheck.props.int.minimum, minint)
471        self.assertEqual(RangeCheck.props.int.maximum, maxint)
472        self.assertEqual(RangeCheck.props.uint.minimum, 0)
473        self.assertEqual(RangeCheck.props.uint.maximum, maxuint)
474        self.assertEqual(RangeCheck.props.long.minimum, minlong)
475        self.assertEqual(RangeCheck.props.long.maximum, maxlong)
476        self.assertEqual(RangeCheck.props.ulong.minimum, 0)
477        self.assertEqual(RangeCheck.props.ulong.maximum, maxulong)
478        self.assertEqual(RangeCheck.props.int64.minimum, minint64)
479        self.assertEqual(RangeCheck.props.int64.maximum, maxint64)
480        self.assertEqual(RangeCheck.props.uint64.minimum, 0)
481        self.assertEqual(RangeCheck.props.uint64.maximum, maxuint64)
482
483        obj = RangeCheck()
484        for key, (gtype, min, max) in types_.items():
485            self.assertEqual(obj.get_property(key),
486                             getattr(RangeCheck.props, key).default_value)
487
488            obj.set_property(key, min)
489            self.assertEqual(obj.get_property(key), min)
490
491            obj.set_property(key, max)
492            self.assertEqual(obj.get_property(key), max)
493
494    def test_multi(self):
495        obj = PropertyObject()
496        obj.set_properties(normal="foo",
497                           uint64=7)
498        normal, uint64 = obj.get_properties("normal", "uint64")
499        self.assertEqual(normal, "foo")
500        self.assertEqual(uint64, 7)
501
502
503class TestProperty(unittest.TestCase):
504    def test_simple(self):
505        class C(GObject.GObject):
506            str = GObject.Property(type=str)
507            float = GObject.Property(type=float)
508            long = GObject.Property(type=int)
509            int = GObject.Property(type=int)
510
511        self.assertTrue(hasattr(C.props, 'str'))
512        self.assertTrue(hasattr(C.props, 'int'))
513        self.assertTrue(hasattr(C.props, 'float'))
514        self.assertTrue(hasattr(C.props, 'long'))
515
516        o = C()
517        self.assertEqual(o.str, '')
518        o.str = 'str'
519        self.assertEqual(o.str, 'str')
520
521        self.assertEqual(o.int, 0)
522        o.int = 1138
523        self.assertEqual(o.int, 1138)
524
525        self.assertEqual(o.float, 0.0)
526        o.float = 3.14
527        self.assertEqual(o.float, 3.14)
528
529        self.assertEqual(o.long, 0)
530        o.long = 100
531        self.assertEqual(o.long, 100)
532
533    def test_custom_getter(self):
534        class C(GObject.GObject):
535            def get_prop(self):
536                return 'value'
537            prop = GObject.Property(getter=get_prop)
538
539        o = C()
540        self.assertEqual(o.prop, 'value')
541        self.assertRaises(TypeError, setattr, o, 'prop', 'xxx')
542
543    def test_getter_exception(self):
544        class C(GObject.Object):
545            @GObject.Property(type=int)
546            def prop(self):
547                raise ValueError('something bad happend')
548
549        o = C()
550
551        with self.assertRaisesRegex(ValueError, 'something bad happend'):
552            o.prop
553
554        with self.assertRaisesRegex(ValueError, 'something bad happend'):
555            o.get_property('prop')
556
557        with self.assertRaisesRegex(ValueError, 'something bad happend'):
558            o.props.prop
559
560    def test_custom_setter(self):
561        class C(GObject.GObject):
562            def set_prop(self, value):
563                self._value = value
564            prop = GObject.Property(setter=set_prop)
565
566            def __init__(self):
567                self._value = None
568                GObject.GObject.__init__(self)
569
570        o = C()
571        self.assertEqual(o._value, None)
572        o.prop = 'bar'
573        self.assertEqual(o._value, 'bar')
574        self.assertRaises(TypeError, getattr, o, 'prop')
575
576    def test_decorator_default(self):
577        class C(GObject.GObject):
578            _value = 'value'
579
580            @GObject.Property
581            def value(self):
582                return self._value
583
584            @value.setter
585            def value_setter(self, value):
586                self._value = value
587
588        o = C()
589        self.assertEqual(o.value, 'value')
590        o.value = 'blah'
591        self.assertEqual(o.value, 'blah')
592        self.assertEqual(o.props.value, 'blah')
593
594    def test_decorator_private_setter(self):
595        class C(GObject.GObject):
596            _value = 'value'
597
598            @GObject.Property
599            def value(self):
600                return self._value
601
602            @value.setter
603            def _set_value(self, value):
604                self._value = value
605
606        o = C()
607        self.assertEqual(o.value, 'value')
608        o.value = 'blah'
609        self.assertEqual(o.value, 'blah')
610        self.assertEqual(o.props.value, 'blah')
611
612    def test_decorator_with_call(self):
613        class C(GObject.GObject):
614            _value = 1
615
616            @GObject.Property(type=int, default=1, minimum=1, maximum=10)
617            def typedValue(self):
618                return self._value
619
620            @typedValue.setter
621            def typedValue_setter(self, value):
622                self._value = value
623
624        o = C()
625        self.assertEqual(o.typedValue, 1)
626        o.typedValue = 5
627        self.assertEqual(o.typedValue, 5)
628        self.assertEqual(o.props.typedValue, 5)
629
630    def test_errors(self):
631        self.assertRaises(TypeError, GObject.Property, type='str')
632        self.assertRaises(TypeError, GObject.Property, nick=False)
633        self.assertRaises(TypeError, GObject.Property, blurb=False)
634        # this never fail while bool is a subclass of int
635        # >>> bool.__bases__
636        # (<type 'int'>,)
637        # self.assertRaises(TypeError, GObject.Property, type=bool, default=0)
638        self.assertRaises(TypeError, GObject.Property, type=bool, default='ciao mamma')
639        self.assertRaises(TypeError, GObject.Property, type=bool)
640        self.assertRaises(TypeError, GObject.Property, type=object, default=0)
641        self.assertRaises(TypeError, GObject.Property, type=complex)
642
643    def test_defaults(self):
644        GObject.Property(type=bool, default=True)
645        GObject.Property(type=bool, default=False)
646
647    def test_name_with_underscore(self):
648        class C(GObject.GObject):
649            prop_name = GObject.Property(type=int)
650        o = C()
651        o.prop_name = 10
652        self.assertEqual(o.prop_name, 10)
653
654    def test_range(self):
655        types_ = [
656            (TYPE_INT, MININT, MAXINT),
657            (TYPE_UINT, 0, MAXUINT),
658            (TYPE_LONG, MINLONG, MAXLONG),
659            (TYPE_ULONG, 0, MAXULONG),
660            (TYPE_INT64, MININT64, MAXINT64),
661            (TYPE_UINT64, 0, MAXUINT64),
662            ]
663
664        for gtype, min, max in types_:
665            # Normal, everything is alright
666            prop = GObject.Property(type=gtype, minimum=min, maximum=max)
667            subtype = type('', (GObject.GObject,), dict(prop=prop))
668            self.assertEqual(subtype.props.prop.minimum, min)
669            self.assertEqual(subtype.props.prop.maximum, max)
670
671            # Lower than minimum
672            self.assertRaises(TypeError,
673                              GObject.Property, type=gtype, minimum=min - 1,
674                              maximum=max)
675
676            # Higher than maximum
677            self.assertRaises(TypeError,
678                              GObject.Property, type=gtype, minimum=min,
679                              maximum=max + 1)
680
681    def test_min_max(self):
682        class C(GObject.GObject):
683            prop_int = GObject.Property(type=int, minimum=1, maximum=100, default=1)
684            prop_float = GObject.Property(type=float, minimum=0.1, maximum=10.5, default=1.1)
685
686            def __init__(self):
687                GObject.GObject.__init__(self)
688
689        # we test known-bad values here which cause Gtk-WARNING logs.
690        # Explicitly allow these for this test.
691        with capture_glib_warnings(allow_warnings=True):
692            o = C()
693            self.assertEqual(o.prop_int, 1)
694
695            o.prop_int = 5
696            self.assertEqual(o.prop_int, 5)
697
698            o.prop_int = 0
699            self.assertEqual(o.prop_int, 5)
700
701            o.prop_int = 101
702            self.assertEqual(o.prop_int, 5)
703
704            self.assertEqual(o.prop_float, 1.1)
705
706            o.prop_float = 7.75
707            self.assertEqual(o.prop_float, 7.75)
708
709            o.prop_float = 0.09
710            self.assertEqual(o.prop_float, 7.75)
711
712            o.prop_float = 10.51
713            self.assertEqual(o.prop_float, 7.75)
714
715    def test_multiple_instances(self):
716        class C(GObject.GObject):
717            prop = GObject.Property(type=str, default='default')
718
719        o1 = C()
720        o2 = C()
721        self.assertEqual(o1.prop, 'default')
722        self.assertEqual(o2.prop, 'default')
723        o1.prop = 'value'
724        self.assertEqual(o1.prop, 'value')
725        self.assertEqual(o2.prop, 'default')
726
727    def test_object_property(self):
728        class PropertyObject(GObject.GObject):
729            obj = GObject.Property(type=GObject.GObject)
730
731        pobj1 = PropertyObject()
732        obj1_hash = hash(pobj1)
733        pobj2 = PropertyObject()
734
735        pobj2.obj = pobj1
736        del pobj1
737        pobj1 = pobj2.obj
738        self.assertEqual(hash(pobj1), obj1_hash)
739
740    def test_object_subclass_property(self):
741        class ObjectSubclass(GObject.GObject):
742            __gtype_name__ = 'ObjectSubclass'
743
744        class PropertyObjectSubclass(GObject.GObject):
745            obj = GObject.Property(type=ObjectSubclass)
746
747        PropertyObjectSubclass(obj=ObjectSubclass())
748
749    def test_property_subclass(self):
750        # test for #470718
751        class A(GObject.GObject):
752            prop1 = GObject.Property(type=int)
753
754        class B(A):
755            prop2 = GObject.Property(type=int)
756
757        b = B()
758        b.prop2 = 10
759        self.assertEqual(b.prop2, 10)
760        b.prop1 = 20
761        self.assertEqual(b.prop1, 20)
762
763    def test_property_subclass_c(self):
764        class A(Regress.TestSubObj):
765            prop1 = GObject.Property(type=int)
766
767        a = A()
768        a.prop1 = 10
769        self.assertEqual(a.prop1, 10)
770
771        # also has parent properties
772        a.props.int = 20
773        self.assertEqual(a.props.int, 20)
774
775        # Some of which are unusable without introspection
776        a.props.list = ("str1", "str2")
777        self.assertEqual(a.props.list, ["str1", "str2"])
778
779        a.set_property("list", ("str3", "str4"))
780        self.assertEqual(a.props.list, ["str3", "str4"])
781
782    def test_property_subclass_custom_setter(self):
783        # test for #523352
784        class A(GObject.GObject):
785            def get_first(self):
786                return 'first'
787            first = GObject.Property(type=str, getter=get_first)
788
789        class B(A):
790            def get_second(self):
791                return 'second'
792            second = GObject.Property(type=str, getter=get_second)
793
794        a = A()
795        self.assertEqual(a.first, 'first')
796        self.assertRaises(TypeError, setattr, a, 'first', 'foo')
797
798        b = B()
799        self.assertEqual(b.first, 'first')
800        self.assertRaises(TypeError, setattr, b, 'first', 'foo')
801        self.assertEqual(b.second, 'second')
802        self.assertRaises(TypeError, setattr, b, 'second', 'foo')
803
804    def test_property_subclass_custom_setter_error(self):
805        try:
806            class A(GObject.GObject):
807                def get_first(self):
808                    return 'first'
809                first = GObject.Property(type=str, getter=get_first)
810
811                def do_get_property(self, pspec):
812                    pass
813        except TypeError:
814            pass
815        else:
816            raise AssertionError
817
818    # Bug 587637.
819
820    def test_float_min(self):
821        GObject.Property(type=float, minimum=-1)
822        GObject.Property(type=GObject.TYPE_FLOAT, minimum=-1)
823        GObject.Property(type=GObject.TYPE_DOUBLE, minimum=-1)
824
825    # Bug 644039
826    @unittest.skipUnless(hasattr(sys, "getrefcount"), "no sys.getrefcount")
827    def test_reference_count(self):
828        # We can check directly if an object gets finalized, so we will
829        # observe it indirectly through the refcount of a member object.
830
831        # We create our dummy object and store its current refcount
832        o = object()
833        rc = sys.getrefcount(o)
834
835        # We add our object as a member to our newly created object we
836        # want to observe. Its refcount is increased by one.
837        t = PropertyObject(normal="test")
838        t.o = o
839        self.assertEqual(sys.getrefcount(o), rc + 1)
840
841        # Now we want to ensure we do not leak any references to our
842        # object with properties. If no ref is leaked, then when deleting
843        # the local reference to this object, its reference count shoud
844        # drop to zero, and our dummy object should loose one reference.
845        del t
846        self.assertEqual(sys.getrefcount(o), rc)
847
848    def test_doc_strings(self):
849        class C(GObject.GObject):
850            foo_blurbed = GObject.Property(type=int, blurb='foo_blurbed doc string')
851
852            @GObject.Property
853            def foo_getter(self):
854                """foo_getter doc string"""
855                return 0
856
857        self.assertEqual(C.foo_blurbed.blurb, 'foo_blurbed doc string')
858        self.assertEqual(C.foo_blurbed.__doc__, 'foo_blurbed doc string')
859
860        self.assertEqual(C.foo_getter.blurb, 'foo_getter doc string')
861        self.assertEqual(C.foo_getter.__doc__, 'foo_getter doc string')
862
863    def test_python_to_glib_type_mapping(self):
864        tester = GObject.Property()
865        self.assertEqual(tester._type_from_python(int), GObject.TYPE_INT)
866        self.assertEqual(tester._type_from_python(bool), GObject.TYPE_BOOLEAN)
867        self.assertEqual(tester._type_from_python(float), GObject.TYPE_DOUBLE)
868        self.assertEqual(tester._type_from_python(str), GObject.TYPE_STRING)
869        self.assertEqual(tester._type_from_python(object), GObject.TYPE_PYOBJECT)
870
871        self.assertEqual(tester._type_from_python(GObject.GObject), GObject.GObject.__gtype__)
872        self.assertEqual(tester._type_from_python(GObject.GEnum), GObject.GEnum.__gtype__)
873        self.assertEqual(tester._type_from_python(GObject.GFlags), GObject.GFlags.__gtype__)
874        self.assertEqual(tester._type_from_python(GObject.GBoxed), GObject.GBoxed.__gtype__)
875        self.assertEqual(tester._type_from_python(GObject.GInterface), GObject.GInterface.__gtype__)
876
877        for type_ in [TYPE_NONE, TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR,
878                      TYPE_INT, TYPE_UINT, TYPE_BOOLEAN, TYPE_LONG,
879                      TYPE_ULONG, TYPE_INT64, TYPE_UINT64,
880                      TYPE_FLOAT, TYPE_DOUBLE, TYPE_POINTER,
881                      TYPE_BOXED, TYPE_PARAM, TYPE_OBJECT, TYPE_STRING,
882                      TYPE_PYOBJECT, TYPE_GTYPE, TYPE_STRV]:
883            self.assertEqual(tester._type_from_python(type_), type_)
884
885        self.assertRaises(TypeError, tester._type_from_python, types.CodeType)
886
887
888class TestInstallProperties(unittest.TestCase):
889    # These tests only test how signalhelper.install_signals works
890    # with the __gsignals__ dict and therefore does not need to use
891    # GObject as a base class because that would automatically call
892    # install_signals within the meta-class.
893    class Base(object):
894        __gproperties__ = {'test': (0, '', '', 0, 0, 0, 0)}
895
896    class Sub1(Base):
897        pass
898
899    class Sub2(Base):
900        @GObject.Property(type=int)
901        def sub2test(self):
902            return 123
903
904    class ClassWithPropertyAndGetterVFunc(object):
905        @GObject.Property(type=int)
906        def sub2test(self):
907            return 123
908
909        def do_get_property(self, name):
910            return 321
911
912    class ClassWithPropertyRedefined(object):
913        __gproperties__ = {'test': (0, '', '', 0, 0, 0, 0)}
914        test = GObject.Property(type=int)
915
916    def setUp(self):
917        self.assertEqual(len(self.Base.__gproperties__), 1)
918        propertyhelper.install_properties(self.Base)
919        self.assertEqual(len(self.Base.__gproperties__), 1)
920
921    def test_subclass_without_properties_is_not_modified(self):
922        self.assertFalse('__gproperties__' in self.Sub1.__dict__)
923        propertyhelper.install_properties(self.Sub1)
924        self.assertFalse('__gproperties__' in self.Sub1.__dict__)
925
926    def test_subclass_with_decorator_gets_gproperties_dict(self):
927        # Sub2 has Property instances but will not have a __gproperties__
928        # until install_properties is called
929        self.assertFalse('__gproperties__' in self.Sub2.__dict__)
930        self.assertFalse('do_get_property' in self.Sub2.__dict__)
931        self.assertFalse('do_set_property' in self.Sub2.__dict__)
932
933        propertyhelper.install_properties(self.Sub2)
934        self.assertTrue('__gproperties__' in self.Sub2.__dict__)
935        self.assertEqual(len(self.Base.__gproperties__), 1)
936        self.assertEqual(len(self.Sub2.__gproperties__), 1)
937        self.assertTrue('sub2test' in self.Sub2.__gproperties__)
938
939        # get/set vfuncs should have been added
940        self.assertTrue('do_get_property' in self.Sub2.__dict__)
941        self.assertTrue('do_set_property' in self.Sub2.__dict__)
942
943    def test_object_with_property_and_do_get_property_vfunc_raises(self):
944        self.assertRaises(TypeError, propertyhelper.install_properties,
945                          self.ClassWithPropertyAndGetterVFunc)
946
947    def test_same_name_property_definitions_raises(self):
948        self.assertRaises(ValueError, propertyhelper.install_properties,
949                          self.ClassWithPropertyRedefined)
950
951
952class CPropertiesTestBase(object):
953    # Tests for properties implemented in C not Python.
954
955    def setUp(self):
956        self.obj = GIMarshallingTests.PropertiesObject()
957
958    def get_prop(self, obj, name):
959        raise NotImplementedError
960
961    def set_prop(self, obj, name, value):
962        raise NotImplementedError
963
964    # https://bugzilla.gnome.org/show_bug.cgi?id=780652
965    @unittest.skipUnless(
966        "some_flags" in dir(GIMarshallingTests.PropertiesObject.props),
967        "tool old gi")
968    def test_flags(self):
969        self.assertEqual(
970            self.get_prop(self.obj, 'some-flags'),
971            GIMarshallingTests.Flags.VALUE1)
972        self.set_prop(self.obj, 'some-flags', GIMarshallingTests.Flags.VALUE2)
973        self.assertEqual(self.get_prop(self.obj, 'some-flags'),
974                         GIMarshallingTests.Flags.VALUE2)
975
976        obj = GIMarshallingTests.PropertiesObject(
977            some_flags=GIMarshallingTests.Flags.VALUE3)
978        self.assertEqual(self.get_prop(obj, 'some-flags'),
979                         GIMarshallingTests.Flags.VALUE3)
980
981    # https://bugzilla.gnome.org/show_bug.cgi?id=780652
982    @unittest.skipUnless(
983        "some_enum" in dir(GIMarshallingTests.PropertiesObject.props),
984        "tool old gi")
985    def test_enum(self):
986        self.assertEqual(
987            self.get_prop(self.obj, 'some-enum'),
988            GIMarshallingTests.GEnum.VALUE1)
989        self.set_prop(self.obj, 'some-enum', GIMarshallingTests.GEnum.VALUE2)
990        self.assertEqual(self.get_prop(self.obj, 'some-enum'),
991                         GIMarshallingTests.GEnum.VALUE2)
992
993        obj = GIMarshallingTests.PropertiesObject(
994            some_enum=GIMarshallingTests.GEnum.VALUE3)
995        self.assertEqual(self.get_prop(obj, 'some-enum'),
996                         GIMarshallingTests.GEnum.VALUE3)
997
998    def test_boolean(self):
999        self.assertEqual(self.get_prop(self.obj, 'some-boolean'), False)
1000        self.set_prop(self.obj, 'some-boolean', True)
1001        self.assertEqual(self.get_prop(self.obj, 'some-boolean'), True)
1002
1003        obj = GIMarshallingTests.PropertiesObject(some_boolean=True)
1004        self.assertEqual(self.get_prop(obj, 'some-boolean'), True)
1005
1006    def test_char(self):
1007        self.assertEqual(self.get_prop(self.obj, 'some-char'), 0)
1008        self.set_prop(self.obj, 'some-char', GLib.MAXINT8)
1009        self.assertEqual(self.get_prop(self.obj, 'some-char'), GLib.MAXINT8)
1010
1011        obj = GIMarshallingTests.PropertiesObject(some_char=-42)
1012        self.assertEqual(self.get_prop(obj, 'some-char'), -42)
1013
1014        with pytest.raises(OverflowError):
1015            self.set_prop(obj, 'some-char', GLib.MAXINT8 + 1)
1016        with pytest.raises(OverflowError):
1017            self.set_prop(obj, 'some-char', GLib.MININT8 - 1)
1018
1019        self.set_prop(obj, 'some-char', b"\x44")
1020        assert self.get_prop(obj, 'some-char') == 0x44
1021
1022        self.set_prop(obj, 'some-char', b"\xff")
1023        assert self.get_prop(obj, 'some-char') == -1
1024
1025        obj = GIMarshallingTests.PropertiesObject(some_char=u"\x7f")
1026        assert self.get_prop(obj, 'some-char') == 0x7f
1027
1028        with pytest.raises(TypeError):
1029            GIMarshallingTests.PropertiesObject(some_char=u"€")
1030
1031        with pytest.raises(TypeError):
1032            GIMarshallingTests.PropertiesObject(some_char=u"\ud83d")
1033
1034    def test_uchar(self):
1035        self.assertEqual(self.get_prop(self.obj, 'some-uchar'), 0)
1036        self.set_prop(self.obj, 'some-uchar', GLib.MAXUINT8)
1037        self.assertEqual(self.get_prop(self.obj, 'some-uchar'), GLib.MAXUINT8)
1038
1039        obj = GIMarshallingTests.PropertiesObject(some_uchar=42)
1040        self.assertEqual(self.get_prop(obj, 'some-uchar'), 42)
1041
1042        with pytest.raises(OverflowError):
1043            self.set_prop(obj, 'some-uchar', GLib.MAXUINT8 + 1)
1044        with pytest.raises(OverflowError):
1045            self.set_prop(obj, 'some-uchar', -1)
1046
1047        self.set_prop(obj, 'some-uchar', b"\x57")
1048        assert self.get_prop(obj, 'some-uchar') == 0x57
1049
1050        self.set_prop(obj, 'some-uchar', b"\xff")
1051        assert self.get_prop(obj, 'some-uchar') == 255
1052
1053        obj = GIMarshallingTests.PropertiesObject(some_uchar=u"\x7f")
1054        assert self.get_prop(obj, 'some-uchar') == 127
1055
1056        with pytest.raises(TypeError):
1057            GIMarshallingTests.PropertiesObject(some_uchar=u"\x80")
1058
1059        with pytest.raises(TypeError):
1060            GIMarshallingTests.PropertiesObject(some_uchar=u"\ud83d")
1061
1062    def test_int(self):
1063        self.assertEqual(self.get_prop(self.obj, 'some_int'), 0)
1064        self.set_prop(self.obj, 'some-int', GLib.MAXINT)
1065        self.assertEqual(self.get_prop(self.obj, 'some_int'), GLib.MAXINT)
1066
1067        obj = GIMarshallingTests.PropertiesObject(some_int=-42)
1068        self.assertEqual(self.get_prop(obj, 'some-int'), -42)
1069
1070        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-int', 'foo')
1071        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-int', None)
1072
1073        self.assertEqual(self.get_prop(obj, 'some-int'), -42)
1074
1075    def test_uint(self):
1076        self.assertEqual(self.get_prop(self.obj, 'some_uint'), 0)
1077        self.set_prop(self.obj, 'some-uint', GLib.MAXUINT)
1078        self.assertEqual(self.get_prop(self.obj, 'some_uint'), GLib.MAXUINT)
1079
1080        obj = GIMarshallingTests.PropertiesObject(some_uint=42)
1081        self.assertEqual(self.get_prop(obj, 'some-uint'), 42)
1082
1083        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-uint', 'foo')
1084        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-uint', None)
1085
1086        self.assertEqual(self.get_prop(obj, 'some-uint'), 42)
1087
1088    def test_long(self):
1089        self.assertEqual(self.get_prop(self.obj, 'some_long'), 0)
1090        self.set_prop(self.obj, 'some-long', GLib.MAXLONG)
1091        self.assertEqual(self.get_prop(self.obj, 'some_long'), GLib.MAXLONG)
1092
1093        obj = GIMarshallingTests.PropertiesObject(some_long=-42)
1094        self.assertEqual(self.get_prop(obj, 'some-long'), -42)
1095
1096        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-long', 'foo')
1097        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-long', None)
1098
1099        self.assertEqual(self.get_prop(obj, 'some-long'), -42)
1100
1101    def test_ulong(self):
1102        self.assertEqual(self.get_prop(self.obj, 'some_ulong'), 0)
1103        self.set_prop(self.obj, 'some-ulong', GLib.MAXULONG)
1104        self.assertEqual(self.get_prop(self.obj, 'some_ulong'), GLib.MAXULONG)
1105
1106        obj = GIMarshallingTests.PropertiesObject(some_ulong=42)
1107        self.assertEqual(self.get_prop(obj, 'some-ulong'), 42)
1108
1109        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-ulong', 'foo')
1110        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-ulong', None)
1111
1112        self.assertEqual(self.get_prop(obj, 'some-ulong'), 42)
1113
1114    def test_int64(self):
1115        self.assertEqual(self.get_prop(self.obj, 'some-int64'), 0)
1116        self.set_prop(self.obj, 'some-int64', GLib.MAXINT64)
1117        self.assertEqual(self.get_prop(self.obj, 'some-int64'), GLib.MAXINT64)
1118
1119        obj = GIMarshallingTests.PropertiesObject(some_int64=-4200000000000000)
1120        self.assertEqual(self.get_prop(obj, 'some-int64'), -4200000000000000)
1121
1122    def test_uint64(self):
1123        self.assertEqual(self.get_prop(self.obj, 'some-uint64'), 0)
1124        self.set_prop(self.obj, 'some-uint64', GLib.MAXUINT64)
1125        self.assertEqual(self.get_prop(self.obj, 'some-uint64'), GLib.MAXUINT64)
1126
1127        obj = GIMarshallingTests.PropertiesObject(some_uint64=4200000000000000)
1128        self.assertEqual(self.get_prop(obj, 'some-uint64'), 4200000000000000)
1129
1130    def test_float(self):
1131        self.assertEqual(self.get_prop(self.obj, 'some-float'), 0)
1132        self.set_prop(self.obj, 'some-float', GLib.MAXFLOAT)
1133        self.assertEqual(self.get_prop(self.obj, 'some-float'), GLib.MAXFLOAT)
1134
1135        obj = GIMarshallingTests.PropertiesObject(some_float=42.42)
1136        self.assertAlmostEqual(self.get_prop(obj, 'some-float'), 42.42, places=4)
1137
1138        obj = GIMarshallingTests.PropertiesObject(some_float=42)
1139        self.assertAlmostEqual(self.get_prop(obj, 'some-float'), 42.0, places=4)
1140
1141        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-float', 'foo')
1142        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-float', None)
1143
1144        self.assertAlmostEqual(self.get_prop(obj, 'some-float'), 42.0, places=4)
1145
1146    def test_double(self):
1147        self.assertEqual(self.get_prop(self.obj, 'some-double'), 0)
1148        self.set_prop(self.obj, 'some-double', GLib.MAXDOUBLE)
1149        self.assertEqual(self.get_prop(self.obj, 'some-double'), GLib.MAXDOUBLE)
1150
1151        obj = GIMarshallingTests.PropertiesObject(some_double=42.42)
1152        self.assertAlmostEqual(self.get_prop(obj, 'some-double'), 42.42)
1153
1154        obj = GIMarshallingTests.PropertiesObject(some_double=42)
1155        self.assertAlmostEqual(self.get_prop(obj, 'some-double'), 42.0)
1156
1157        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-double', 'foo')
1158        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-double', None)
1159
1160        self.assertAlmostEqual(self.get_prop(obj, 'some-double'), 42.0)
1161
1162    def test_strv(self):
1163        self.assertEqual(self.get_prop(self.obj, 'some-strv'), [])
1164        self.set_prop(self.obj, 'some-strv', ['hello', 'world'])
1165        self.assertEqual(self.get_prop(self.obj, 'some-strv'), ['hello', 'world'])
1166
1167        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-strv', 1)
1168        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-strv', 'foo')
1169        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-strv', [1, 2])
1170        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-strv', ['foo', 1])
1171
1172        self.assertEqual(self.get_prop(self.obj, 'some-strv'), ['hello', 'world'])
1173
1174        obj = GIMarshallingTests.PropertiesObject(some_strv=['hello', 'world'])
1175        self.assertEqual(self.get_prop(obj, 'some-strv'), ['hello', 'world'])
1176
1177        # unicode on py2
1178        obj = GIMarshallingTests.PropertiesObject(some_strv=[u'foo'])
1179        self.assertEqual(self.get_prop(obj, 'some-strv'), [u'foo'])
1180        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-strv',
1181                          [u'foo', 1])
1182
1183    def test_boxed_struct(self):
1184        self.assertEqual(self.get_prop(self.obj, 'some-boxed-struct'), None)
1185
1186        class GStrv(list):
1187            __gtype__ = GObject.TYPE_STRV
1188
1189        struct1 = GIMarshallingTests.BoxedStruct()
1190        struct1.long_ = 1
1191
1192        self.set_prop(self.obj, 'some-boxed-struct', struct1)
1193        self.assertEqual(self.get_prop(self.obj, 'some-boxed-struct').long_, 1)
1194        self.assertEqual(self.obj.some_boxed_struct.long_, 1)
1195
1196        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-boxed-struct', 1)
1197        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-boxed-struct', 'foo')
1198
1199        obj = GIMarshallingTests.PropertiesObject(some_boxed_struct=struct1)
1200        self.assertEqual(self.get_prop(obj, 'some-boxed-struct').long_, 1)
1201
1202    def test_boxed_glist(self):
1203        self.assertEqual(self.get_prop(self.obj, 'some-boxed-glist'), [])
1204
1205        list_ = [GLib.MININT, 42, GLib.MAXINT]
1206        self.set_prop(self.obj, 'some-boxed-glist', list_)
1207        self.assertEqual(self.get_prop(self.obj, 'some-boxed-glist'), list_)
1208        self.set_prop(self.obj, 'some-boxed-glist', [])
1209        self.assertEqual(self.get_prop(self.obj, 'some-boxed-glist'), [])
1210
1211        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-boxed-glist', 1)
1212        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-boxed-glist', 'foo')
1213        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-boxed-glist', ['a'])
1214
1215    def test_annotated_glist(self):
1216        obj = Regress.TestObj()
1217        self.assertEqual(self.get_prop(obj, 'list'), [])
1218
1219        self.set_prop(obj, 'list', ['1', '2', '3'])
1220        self.assertTrue(isinstance(self.get_prop(obj, 'list'), list))
1221        self.assertEqual(self.get_prop(obj, 'list'), ['1', '2', '3'])
1222
1223    @unittest.expectedFailure
1224    def test_boxed_glist_ctor(self):
1225        list_ = [GLib.MININT, 42, GLib.MAXINT]
1226        obj = GIMarshallingTests.PropertiesObject(some_boxed_glist=list_)
1227        self.assertEqual(self.get_prop(obj, 'some-boxed-glist'), list_)
1228
1229    def test_variant(self):
1230        self.assertEqual(self.get_prop(self.obj, 'some-variant'), None)
1231
1232        self.set_prop(self.obj, 'some-variant', GLib.Variant('o', '/myobj'))
1233        self.assertEqual(self.get_prop(self.obj, 'some-variant').get_type_string(), 'o')
1234        self.assertEqual(self.get_prop(self.obj, 'some-variant').print_(False), "'/myobj'")
1235
1236        self.set_prop(self.obj, 'some-variant', None)
1237        self.assertEqual(self.get_prop(self.obj, 'some-variant'), None)
1238
1239        obj = GIMarshallingTests.PropertiesObject(some_variant=GLib.Variant('b', True))
1240        self.assertEqual(self.get_prop(obj, 'some-variant').get_type_string(), 'b')
1241        self.assertEqual(self.get_prop(obj, 'some-variant').get_boolean(), True)
1242
1243        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-variant', 'foo')
1244        self.assertRaises(TypeError, self.set_prop, self.obj, 'some-variant', 23)
1245
1246        self.assertEqual(self.get_prop(obj, 'some-variant').get_type_string(), 'b')
1247        self.assertEqual(self.get_prop(obj, 'some-variant').get_boolean(), True)
1248
1249    def test_setting_several_properties(self):
1250        obj = GIMarshallingTests.PropertiesObject()
1251        obj.set_properties(some_uchar=54, some_int=42)
1252        self.assertEqual(42, self.get_prop(obj, 'some-int'))
1253        self.assertEqual(54, self.get_prop(obj, 'some-uchar'))
1254
1255    def test_gtype(self):
1256        obj = Regress.TestObj()
1257        self.assertEqual(self.get_prop(obj, 'gtype'), GObject.TYPE_INVALID)
1258        self.set_prop(obj, 'gtype', int)
1259        self.assertEqual(self.get_prop(obj, 'gtype'), GObject.TYPE_INT)
1260
1261        obj = Regress.TestObj(gtype=int)
1262        self.assertEqual(self.get_prop(obj, 'gtype'), GObject.TYPE_INT)
1263        self.set_prop(obj, 'gtype', str)
1264        self.assertEqual(self.get_prop(obj, 'gtype'), GObject.TYPE_STRING)
1265
1266    def test_hash_table(self):
1267        obj = Regress.TestObj()
1268        self.assertEqual(self.get_prop(obj, 'hash-table'), None)
1269
1270        self.set_prop(obj, 'hash-table', {'mec': 56})
1271        self.assertTrue(isinstance(self.get_prop(obj, 'hash-table'), dict))
1272        self.assertEqual(list(self.get_prop(obj, 'hash-table').items())[0],
1273                         ('mec', 56))
1274
1275    def test_parent_class(self):
1276        class A(Regress.TestObj):
1277            prop1 = GObject.Property(type=int)
1278
1279        a = A()
1280        self.set_prop(a, 'int', 20)
1281        self.assertEqual(self.get_prop(a, 'int'), 20)
1282
1283        # test parent property which needs introspection
1284        self.set_prop(a, 'list', ("str1", "str2"))
1285        self.assertEqual(self.get_prop(a, 'list'), ["str1", "str2"])
1286
1287    def test_held_object_ref_count_getter(self):
1288        holder = GIMarshallingTests.PropertiesObject()
1289        held = GObject.Object()
1290
1291        self.assertEqual(holder.__grefcount__, 1)
1292        self.assertEqual(held.__grefcount__, 1)
1293
1294        self.set_prop(holder, 'some-object', held)
1295        self.assertEqual(holder.__grefcount__, 1)
1296
1297        initial_ref_count = held.__grefcount__
1298        self.get_prop(holder, 'some-object')
1299        gc.collect()
1300        self.assertEqual(held.__grefcount__, initial_ref_count)
1301
1302    def test_held_object_ref_count_setter(self):
1303        holder = GIMarshallingTests.PropertiesObject()
1304        held = GObject.Object()
1305
1306        self.assertEqual(holder.__grefcount__, 1)
1307        self.assertEqual(held.__grefcount__, 1)
1308
1309        # Setting property should only increase ref count by 1
1310        self.set_prop(holder, 'some-object', held)
1311        self.assertEqual(holder.__grefcount__, 1)
1312        self.assertEqual(held.__grefcount__, 2)
1313
1314        # Clearing should pull it back down
1315        self.set_prop(holder, 'some-object', None)
1316        self.assertEqual(held.__grefcount__, 1)
1317
1318    def test_set_object_property_to_invalid_type(self):
1319        obj = GIMarshallingTests.PropertiesObject()
1320        self.assertRaises(TypeError, self.set_prop, obj, 'some-object', 'not_an_object')
1321
1322
1323class TestCPropsAccessor(CPropertiesTestBase, unittest.TestCase):
1324    # C property tests using the "props" accessor.
1325    def get_prop(self, obj, name):
1326        return getattr(obj.props, name.replace('-', '_'))
1327
1328    def set_prop(self, obj, name, value):
1329        setattr(obj.props, name.replace('-', '_'), value)
1330
1331    def test_props_accessor_dir(self):
1332        # Test class
1333        props = dir(GIMarshallingTests.PropertiesObject.props)
1334        self.assertTrue('some_float' in props)
1335        self.assertTrue('some_double' in props)
1336        self.assertTrue('some_variant' in props)
1337
1338        # Test instance
1339        obj = GIMarshallingTests.PropertiesObject()
1340        props = dir(obj.props)
1341        self.assertTrue('some_float' in props)
1342        self.assertTrue('some_double' in props)
1343        self.assertTrue('some_variant' in props)
1344
1345    def test_param_spec_dir(self):
1346        attrs = dir(GIMarshallingTests.PropertiesObject.props.some_float)
1347        self.assertTrue('name' in attrs)
1348        self.assertTrue('nick' in attrs)
1349        self.assertTrue('blurb' in attrs)
1350        self.assertTrue('flags' in attrs)
1351        self.assertTrue('default_value' in attrs)
1352        self.assertTrue('minimum' in attrs)
1353        self.assertTrue('maximum' in attrs)
1354
1355
1356class TestCGetPropertyMethod(CPropertiesTestBase, unittest.TestCase):
1357    # C property tests using the "props" accessor.
1358    def get_prop(self, obj, name):
1359        return obj.get_property(name)
1360
1361    def set_prop(self, obj, name, value):
1362        obj.set_property(name, value)
1363