3 from __future__
import absolute_import
12 from gi
.repository
import GObject
, GLib
, Regress
, Gio
13 from gi
import _signalhelper
as signalhelper
14 from gi
.module
import repository
as repo
15 from gi
._compat
import PY3
, long_
18 from .helper
import capture_glib_warnings
, capture_gi_deprecation_warnings
21 class C(GObject
.GObject
):
22 __gsignals__
= {'my_signal': (GObject
.SignalFlags
.RUN_FIRST
, None,
25 def do_my_signal(self
, arg
):
30 def do_my_signal(self
, arg2
):
32 C
.do_my_signal(self
, arg2
)
35 class TestSignalCreation(unittest
.TestCase
):
37 def test_illegals(self
):
38 self
.assertRaises(TypeError, lambda: GObject
.signal_new('test',
42 (GObject
.TYPE_LONG
,)))
45 class TestChaining(unittest
.TestCase
):
48 self
.inst
.connect("my_signal", self
.my_signal_handler_cb
, 1, 2, 3)
50 def my_signal_handler_cb(self
, *args
):
52 assert isinstance(args
[0], C
)
53 assert args
[0] == self
.inst
55 assert isinstance(args
[1], int)
58 assert args
[2:] == (1, 2, 3)
60 def test_chaining(self
):
61 self
.inst
.emit("my_signal", 42)
62 assert self
.inst
.arg
== 42
64 def test_chaining2(self
):
66 inst2
.emit("my_signal", 44)
67 assert inst2
.arg
== 44
68 assert inst2
.arg2
== 44
70 # This is for bug 153718
73 class TestGSignalsError(unittest
.TestCase
):
74 def test_invalid_type(self
, *args
):
76 class Foo(GObject
.GObject
):
78 self
.assertRaises(TypeError, foo
)
81 def test_invalid_name(self
, *args
):
83 class Foo(GObject
.GObject
):
84 __gsignals__
= {'not-exists': 'override'}
86 with
capture_glib_warnings(allow_warnings
=True):
87 self
.assertRaises(TypeError, foo
)
91 class TestGPropertyError(unittest
.TestCase
):
92 def test_invalid_type(self
, *args
):
94 class Foo(GObject
.GObject
):
95 __gproperties__
= None
96 self
.assertRaises(TypeError, foo
)
99 def test_invalid_name(self
, *args
):
101 class Foo(GObject
.GObject
):
102 __gproperties__
= {None: None}
104 self
.assertRaises(TypeError, foo
)
108 class TestList(unittest
.TestCase
):
109 def test_list_names(self
):
110 self
.assertEqual(GObject
.signal_list_names(C
), ('my-signal',))
113 def my_accumulator(ihint
, return_accu
, handler_return
, user_data
):
114 """An accumulator that stops emission when the sum of handler
115 returned values reaches 3"""
116 assert user_data
== "accum data"
118 return False, return_accu
119 return True, return_accu
+ handler_return
122 class Foo(GObject
.GObject
):
123 my_acc_signal
= GObject
.Signal(return_type
=GObject
.TYPE_INT
,
124 flags
=GObject
.SignalFlags
.RUN_LAST
,
125 accumulator
=my_accumulator
,
126 accu_data
="accum data")
128 my_other_acc_signal
= GObject
.Signal(return_type
=GObject
.TYPE_BOOLEAN
,
129 flags
=GObject
.SignalFlags
.RUN_LAST
,
130 accumulator
=GObject
.signal_accumulator_true_handled
)
132 my_acc_first_wins
= GObject
.Signal(return_type
=GObject
.TYPE_BOOLEAN
,
133 flags
=GObject
.SignalFlags
.RUN_LAST
,
134 accumulator
=GObject
.signal_accumulator_first_wins
)
137 class TestAccumulator(unittest
.TestCase
):
139 def test_accumulator(self
):
141 inst
.my_acc_signal
.connect(lambda obj
: 1)
142 inst
.my_acc_signal
.connect(lambda obj
: 2)
143 # the value returned in the following handler will not be
144 # considered, because at this point the accumulator already
146 inst
.my_acc_signal
.connect(lambda obj
: 3)
147 retval
= inst
.my_acc_signal
.emit()
148 self
.assertEqual(retval
, 3)
150 def test_accumulator_true_handled(self
):
152 inst
.my_other_acc_signal
.connect(self
._true
_handler
1)
153 inst
.my_other_acc_signal
.connect(self
._true
_handler
2)
154 # the following handler will not be called because handler2
155 # returns True, so it should stop the emission.
156 inst
.my_other_acc_signal
.connect(self
._true
_handler
3)
157 self
.__true
_val
= None
158 inst
.my_other_acc_signal
.emit()
159 self
.assertEqual(self
.__true
_val
, 2)
161 def test_accumulator_first_wins(self
):
162 # First signal hit will always win
164 inst
.my_acc_first_wins
.connect(self
._true
_handler
3)
165 inst
.my_acc_first_wins
.connect(self
._true
_handler
1)
166 inst
.my_acc_first_wins
.connect(self
._true
_handler
2)
167 self
.__true
_val
= None
168 inst
.my_acc_first_wins
.emit()
169 self
.assertEqual(self
.__true
_val
, 3)
171 def _true_handler1(self
, obj
):
175 def _true_handler2(self
, obj
):
179 def _true_handler3(self
, obj
):
184 class E(GObject
.GObject
):
185 __gsignals__
= {'signal': (GObject
.SignalFlags
.RUN_FIRST
, None,
188 # Property used to test detailed signal
189 prop
= GObject
.Property(type=int, default
=0)
192 GObject
.GObject
.__init
__(self
)
196 assert self
.status
== 0
200 class F(GObject
.GObject
):
201 __gsignals__
= {'signal': (GObject
.SignalFlags
.RUN_FIRST
, None,
205 GObject
.GObject
.__init
__(self
)
212 class TestEmissionHook(unittest
.TestCase
):
216 e
.connect('signal', self
._callback
)
217 GObject
.add_emission_hook(E
, "signal", self
._emission
_hook
)
219 self
.assertEqual(e
.status
, 3)
221 def test_remove(self
):
224 e
.connect('signal', self
._callback
)
225 hook_id
= GObject
.add_emission_hook(E
, "signal", self
._emission
_hook
)
226 GObject
.remove_emission_hook(E
, "signal", hook_id
)
228 self
.assertEqual(e
.status
, 3)
230 def _emission_hook(self
, e
):
231 self
.assertEqual(e
.status
, 1)
234 def _callback(self
, e
):
236 self
.assertEqual(e
.status
, 2)
238 self
.assertEqual(e
.status
, 1)
241 def test_callback_return_false(self
):
245 def _emission_hook(obj
):
248 GObject
.add_emission_hook(obj
, "signal", _emission_hook
)
251 self
.assertEqual(obj
.status
, 3)
253 def test_callback_return_true(self
):
257 def _emission_hook(obj
):
260 hook_id
= GObject
.add_emission_hook(obj
, "signal", _emission_hook
)
263 GObject
.remove_emission_hook(obj
, "signal", hook_id
)
264 self
.assertEqual(obj
.status
, 4)
266 def test_callback_return_true_but_remove(self
):
270 def _emission_hook(obj
):
273 hook_id
= GObject
.add_emission_hook(obj
, "signal", _emission_hook
)
275 GObject
.remove_emission_hook(obj
, "signal", hook_id
)
277 self
.assertEqual(obj
.status
, 3)
280 class TestMatching(unittest
.TestCase
):
281 class Object(GObject
.Object
):
283 prop
= GObject
.Property(type=int, default
=0)
289 @unittest.expectedFailure
# https://bugzilla.gnome.org/show_bug.cgi?id=692918
290 def test_signal_handler_block_matching(self
):
292 "Hack to work around: "
298 handler_id
= GObject
.signal_connect_closure(obj
, 'my-signal', foo
, after
=False)
301 self
.assertEqual(obj
.status
, 0)
302 obj
.emit('my-signal')
303 self
.assertEqual(obj
.status
, 1)
305 # Blocking by match criteria disables the foo callback
306 signal_id
, detail
= GObject
.signal_parse_name('my-signal', obj
, True)
307 count
= GObject
.signal_handlers_block_matched(obj
,
308 GObject
.SignalMatchType
.ID | GObject
.SignalMatchType
.CLOSURE
,
309 signal_id
=signal_id
, detail
=detail
,
310 closure
=foo
, func
=dummy
, data
=dummy
)
311 self
.assertEqual(count
, 1)
312 obj
.emit('my-signal')
313 self
.assertEqual(obj
.status
, 1)
315 # Unblocking by the same match criteria allows callback to work again
316 count
= GObject
.signal_handlers_unblock_matched(obj
,
317 GObject
.SignalMatchType
.ID | GObject
.SignalMatchType
.CLOSURE
,
318 signal_id
=signal_id
, detail
=detail
,
319 closure
=foo
, func
=dummy
, data
=dummy
)
320 self
.assertEqual(count
, 1)
321 obj
.emit('my-signal')
322 self
.assertEqual(obj
.status
, 2)
324 # Disconnecting by match criteria completely removes the handler
325 count
= GObject
.signal_handlers_disconnect_matched(obj
,
326 GObject
.SignalMatchType
.ID | GObject
.SignalMatchType
.CLOSURE
,
327 signal_id
=signal_id
, detail
=detail
,
328 closure
=foo
, func
=dummy
, data
=dummy
)
329 self
.assertEqual(count
, 1)
330 obj
.emit('my-signal')
331 self
.assertEqual(obj
.status
, 2)
333 def test_signal_handler_find(self
):
338 handler_id
= GObject
.signal_connect_closure(obj
, 'my-signal', foo
, after
=False)
340 signal_id
, detail
= GObject
.signal_parse_name('my-signal', obj
, True)
341 found_id
= GObject
.signal_handler_find(obj
,
342 GObject
.SignalMatchType
.ID
,
343 signal_id
=signal_id
, detail
=detail
,
344 closure
=None, func
=0, data
=0)
345 self
.assertEqual(handler_id
, found_id
)
348 class TestClosures(unittest
.TestCase
):
351 self
.emission_stopped
= False
352 self
.emission_error
= False
353 self
.handler_pending
= False
355 def _callback_handler_pending(self
, e
):
356 signal_id
, detail
= GObject
.signal_parse_name('signal', e
, True)
357 self
.handler_pending
= GObject
.signal_has_handler_pending(e
, signal_id
, detail
,
358 may_be_blocked
=False)
360 def _callback(self
, e
):
363 def _callback_stop_emission(self
, obj
, prop
, stop_it
):
365 obj
.stop_emission_by_name('notify::prop')
366 self
.emission_stopped
= True
370 def _callback_invalid_stop_emission_name(self
, obj
, prop
):
371 with
capture_glib_warnings(allow_warnings
=True) as warn
:
372 obj
.stop_emission_by_name('notasignal::baddetail')
373 self
.emission_error
= True
374 self
.assertTrue(warn
)
376 def test_disconnect_by_func(self
):
378 e
.connect('signal', self
._callback
)
379 e
.disconnect_by_func(self
._callback
)
381 self
.assertEqual(self
.count
, 0)
383 def test_disconnect(self
):
385 handler_id
= e
.connect('signal', self
._callback
)
386 self
.assertTrue(e
.handler_is_connected(handler_id
))
387 e
.disconnect(handler_id
)
389 self
.assertEqual(self
.count
, 0)
390 self
.assertFalse(e
.handler_is_connected(handler_id
))
392 def test_stop_emission_by_name(self
):
395 # Sandwich a callback that stops emission in between a callback that increments
396 e
.connect('notify::prop', self
._callback
_stop
_emission
, False)
397 e
.connect('notify::prop', self
._callback
_stop
_emission
, True)
398 e
.connect('notify::prop', self
._callback
_stop
_emission
, False)
400 e
.set_property('prop', 1234)
401 self
.assertEqual(e
.get_property('prop'), 1234)
402 self
.assertEqual(self
.count
, 1)
403 self
.assertTrue(self
.emission_stopped
)
405 def test_stop_emission_by_name_error(self
):
408 e
.connect('notify::prop', self
._callback
_invalid
_stop
_emission
_name
)
409 with
capture_glib_warnings():
410 e
.set_property('prop', 1234)
411 self
.assertTrue(self
.emission_error
)
413 def test_handler_block(self
):
415 e
.connect('signal', self
._callback
)
416 e
.handler_block_by_func(self
._callback
)
418 self
.assertEqual(self
.count
, 0)
420 def test_handler_unblock(self
):
422 handler_id
= e
.connect('signal', self
._callback
)
423 e
.handler_block(handler_id
)
424 e
.handler_unblock_by_func(self
._callback
)
426 self
.assertEqual(self
.count
, 1)
428 def test_handler_block_method(self
):
434 def callback(self
, o
):
436 o
.handler_block_by_func(self
.callback
)
440 e
.connect("signal", inst
.callback
)
442 self
.assertEqual(inst
.a
, 1)
445 def test_gstring(self
):
446 class C(GObject
.GObject
):
447 __gsignals__
= {'my_signal': (GObject
.SignalFlags
.RUN_LAST
, GObject
.TYPE_GSTRING
,
448 (GObject
.TYPE_GSTRING
,))}
450 def __init__(self
, test
):
451 GObject
.GObject
.__init
__(self
)
454 def do_my_signal(self
, data
):
456 self
.test
.assertEqual(len(data
), 3)
457 return ''.join([data
[2], data
[1], data
[0]])
459 data
= c
.emit("my_signal", "\01\00\02")
460 self
.assertEqual(data
, "\02\00\01")
462 def test_handler_pending(self
):
464 obj
.connect('signal', self
._callback
_handler
_pending
)
465 obj
.connect('signal', self
._callback
)
467 self
.assertEqual(self
.count
, 0)
468 self
.assertEqual(self
.handler_pending
, False)
471 self
.assertEqual(self
.count
, 1)
472 self
.assertEqual(self
.handler_pending
, True)
474 def test_signal_handlers_destroy(self
):
476 obj
.connect('signal', self
._callback
)
477 obj
.connect('signal', self
._callback
)
478 obj
.connect('signal', self
._callback
)
481 self
.assertEqual(self
.count
, 3)
483 # count should remain at 3 after all handlers are destroyed
484 GObject
.signal_handlers_destroy(obj
)
486 self
.assertEqual(self
.count
, 3)
489 class SigPropClass(GObject
.GObject
):
490 __gsignals__
= {'my_signal': (GObject
.SignalFlags
.RUN_FIRST
, None,
491 (GObject
.TYPE_INT
,))}
494 'foo': (str, None, None, '',
495 GObject
.ParamFlags
.WRITABLE | GObject
.ParamFlags
.CONSTRUCT
),
498 signal_emission_failed
= False
500 def do_my_signal(self
, arg
):
503 def do_set_property(self
, pspec
, value
):
504 if pspec
.name
== 'foo':
507 raise AttributeError('unknown property %s' % pspec
.name
)
509 self
.emit("my-signal", 1)
511 self
.signal_emission_failed
= True
514 class TestSigProp(unittest
.TestCase
):
515 def test_emit_in_property_setter(self
):
517 self
.assertFalse(obj
.signal_emission_failed
)
520 class CM(GObject
.GObject
):
522 test1
=(GObject
.SignalFlags
.RUN_FIRST
, None, ()),
523 test2
=(GObject
.SignalFlags
.RUN_LAST
, None, (str,)),
524 test3
=(GObject
.SignalFlags
.RUN_LAST
, int, (GObject
.TYPE_DOUBLE
,)),
525 test4
=(GObject
.SignalFlags
.RUN_FIRST
, None,
526 (bool, long_
, GObject
.TYPE_FLOAT
, GObject
.TYPE_DOUBLE
, int,
527 GObject
.TYPE_UINT
, GObject
.TYPE_ULONG
)),
528 test_float
=(GObject
.SignalFlags
.RUN_LAST
, GObject
.TYPE_FLOAT
, (GObject
.TYPE_FLOAT
,)),
529 test_double
=(GObject
.SignalFlags
.RUN_LAST
, GObject
.TYPE_DOUBLE
, (GObject
.TYPE_DOUBLE
,)),
530 test_int64
=(GObject
.SignalFlags
.RUN_LAST
, GObject
.TYPE_INT64
, (GObject
.TYPE_INT64
,)),
531 test_string
=(GObject
.SignalFlags
.RUN_LAST
, str, (str,)),
532 test_object
=(GObject
.SignalFlags
.RUN_LAST
, object, (object,)),
533 test_paramspec
=(GObject
.SignalFlags
.RUN_LAST
, GObject
.ParamSpec
, ()),
534 test_paramspec_in
=(GObject
.SignalFlags
.RUN_LAST
, GObject
.ParamSpec
, (GObject
.ParamSpec
, )),
535 test_gvalue
=(GObject
.SignalFlags
.RUN_LAST
, GObject
.Value
, (GObject
.Value
,)),
536 test_gvalue_ret
=(GObject
.SignalFlags
.RUN_LAST
, GObject
.Value
, (GObject
.TYPE_GTYPE
,)),
539 testprop
= GObject
.Property(type=int)
542 class _TestCMarshaller
:
545 testhelper
.connectcallbacks(self
.obj
)
547 def test_test1(self
):
548 self
.obj
.emit("test1")
550 def test_test2(self
):
551 self
.obj
.emit("test2", "string")
553 def test_test3(self
):
554 rv
= self
.obj
.emit("test3", 42.0)
555 self
.assertEqual(rv
, 20)
557 def test_test4(self
):
558 self
.obj
.emit("test4", True, long_(10), 3.14, 1.78, 20, long_(30), long_(31))
560 def test_float(self
):
561 rv
= self
.obj
.emit("test-float", 1.234)
562 self
.assertTrue(rv
>= 1.233999 and rv
<= 1.2400001, rv
)
564 def test_double(self
):
565 rv
= self
.obj
.emit("test-double", 1.234)
566 self
.assertEqual(rv
, 1.234)
568 def test_int64(self
):
569 rv
= self
.obj
.emit("test-int64", 102030405)
570 self
.assertEqual(rv
, 102030405)
572 rv
= self
.obj
.emit("test-int64", GLib
.MAXINT64
)
573 self
.assertEqual(rv
, GLib
.MAXINT64
- 1)
575 rv
= self
.obj
.emit("test-int64", GLib
.MININT64
)
576 self
.assertEqual(rv
, GLib
.MININT64
)
578 def test_string(self
):
579 rv
= self
.obj
.emit("test-string", "str")
580 self
.assertEqual(rv
, "str")
582 def test_object(self
):
583 rv
= self
.obj
.emit("test-object", self
)
584 self
.assertEqual(rv
, self
)
586 def test_paramspec(self
):
587 rv
= self
.obj
.emit("test-paramspec")
588 self
.assertEqual(rv
.name
, "test-param")
589 self
.assertEqual(rv
.nick
, "test")
591 @unittest.skipUnless(hasattr(GObject
, 'param_spec_boolean'),
592 'too old gobject-introspection')
593 def test_paramspec_in(self
):
594 rv
= GObject
.param_spec_boolean('mybool', 'test-bool', 'do something',
595 True, GObject
.ParamFlags
.READABLE
)
597 rv2
= self
.obj
.emit("test-paramspec-in", rv
)
598 self
.assertEqual(type(rv
), type(rv2
))
599 self
.assertEqual(rv2
.name
, "mybool")
600 self
.assertEqual(rv2
.nick
, "test-bool")
602 def test_C_paramspec(self
):
603 self
.notify_called
= False
605 def cb_notify(obj
, prop
):
606 self
.notify_called
= True
607 self
.assertEqual(obj
, self
.obj
)
608 self
.assertEqual(prop
.name
, "testprop")
610 self
.obj
.connect("notify", cb_notify
)
611 self
.obj
.set_property("testprop", 42)
612 self
.assertTrue(self
.notify_called
)
614 def test_gvalue(self
):
616 rv
= self
.obj
.emit("test-gvalue", 42)
617 self
.assertEqual(rv
, 42)
620 v
= GObject
.Value(GObject
.TYPE_FLOAT
, 1.234)
621 rv
= self
.obj
.emit("test-gvalue", v
)
622 self
.assertAlmostEqual(rv
, 1.234, places
=4)
625 rv
= self
.obj
.emit("test-gvalue", 1.234)
626 self
.assertAlmostEqual(rv
, 1.234, places
=4)
629 v
= GObject
.Value(GObject
.TYPE_INT64
, GLib
.MAXINT64
)
630 rv
= self
.obj
.emit("test-gvalue", v
)
631 self
.assertEqual(rv
, GLib
.MAXINT64
)
634 v
= GObject
.Value(GObject
.TYPE_UINT64
, GLib
.MAXUINT64
)
635 rv
= self
.obj
.emit("test-gvalue", v
)
636 self
.assertEqual(rv
, GLib
.MAXUINT64
)
638 @unittest.expectedFailure
# https://bugzilla.gnome.org/show_bug.cgi?id=705291
639 def test_gvalue_implicit_int64(self
):
641 rv
= self
.obj
.emit("test-gvalue", GLib
.MAXINT64
)
642 self
.assertEqual(rv
, GLib
.MAXINT64
)
645 rv
= self
.obj
.emit("test-gvalue", GLib
.MAXUINT64
)
646 self
.assertEqual(rv
, GLib
.MAXUINT64
)
648 def test_gvalue_ret(self
):
649 self
.assertEqual(self
.obj
.emit("test-gvalue-ret", GObject
.TYPE_INT
),
651 self
.assertEqual(self
.obj
.emit("test-gvalue-ret", GObject
.TYPE_UINT
),
653 self
.assertEqual(self
.obj
.emit("test-gvalue-ret", GObject
.TYPE_INT64
),
655 self
.assertEqual(self
.obj
.emit("test-gvalue-ret", GObject
.TYPE_UINT64
),
657 self
.assertEqual(self
.obj
.emit("test-gvalue-ret", GObject
.TYPE_STRING
),
661 class TestCMarshaller(_TestCMarshaller
, unittest
.TestCase
):
668 class TestPyGValue(unittest
.TestCase
):
669 def test_none_null_boxed_conversion(self
):
670 class C(GObject
.GObject
):
671 __gsignals__
= dict(my_boxed_signal
=(
672 GObject
.SignalFlags
.RUN_LAST
,
673 GObject
.TYPE_STRV
, ()))
676 obj
.connect('my-boxed-signal', lambda obj
: None)
678 obj
.emit('my-boxed-signal')
679 assert not sys
.last_type
682 class TestSignalDecorator(unittest
.TestCase
):
683 class Decorated(GObject
.GObject
):
691 @GObject.Signal(flags
=GObject
.SignalFlags
.RUN_LAST
)
695 @GObject.Signal(flags
=GObject
.SignalFlags
.DETAILED
)
699 stomped
= GObject
.Signal('stomped', arg_types
=(int,), doc
='this will stomp')
700 unnamed
= GObject
.Signal()
702 class DecoratedOverride(GObject
.GObject
):
703 overridden_closure_called
= False
704 notify_called
= False
705 value
= GObject
.Property(type=int, default
=0)
707 @GObject.SignalOverride
708 def notify(self
, *args
, **kargs
):
709 self
.overridden_closure_called
= True
711 def on_notify(self
, obj
, prop
):
712 self
.notify_called
= True
715 self
.unnamedCalled
= False
717 def onUnnamed(self
, obj
):
718 self
.unnamedCalled
= True
720 def test_disconnect(self
):
721 decorated
= self
.Decorated()
722 id_
= decorated
.pushed
.connect(lambda *args
: None)
723 decorated
.pushed
.disconnect(id_
)
725 def test_signal_repr(self
):
726 decorated
= self
.Decorated()
727 assert repr(decorated
.pushed
) == 'BoundSignal("pushed")'
729 def test_signal_call(self
):
730 decorated
= self
.Decorated()
731 assert decorated
.value
== 0
733 assert decorated
.value
== 1
735 def test_connect_detailed(self
):
736 decorated
= self
.Decorated()
737 id_
= decorated
.detailed
.connect_detailed(lambda *args
: None, "foo")
738 decorated
.pushed
.disconnect(id_
)
740 def test_get_signal_args(self
):
741 self
.assertEqual(self
.Decorated
.pushed
.get_signal_args(),
742 (GObject
.SignalFlags
.RUN_FIRST
, None, tuple(), None, None))
743 self
.assertEqual(self
.Decorated
.pulled
.get_signal_args(),
744 (GObject
.SignalFlags
.RUN_LAST
, None, tuple(), None, None))
745 self
.assertEqual(self
.Decorated
.stomped
.get_signal_args(),
746 (GObject
.SignalFlags
.RUN_FIRST
, None, (int,), None, None))
748 def test_closures_called(self
):
749 decorated
= self
.Decorated()
750 self
.assertEqual(decorated
.value
, 0)
751 decorated
.pushed
.emit()
752 self
.assertEqual(decorated
.value
, 1)
753 decorated
.pulled
.emit()
754 self
.assertEqual(decorated
.value
, 0)
756 def test_signal_copy(self
):
757 blah
= self
.Decorated
.stomped
.copy('blah')
758 self
.assertEqual(str(blah
), blah
)
759 self
.assertEqual(blah
.func
, self
.Decorated
.stomped
.func
)
760 self
.assertEqual(blah
.flags
, self
.Decorated
.stomped
.flags
)
761 self
.assertEqual(blah
.return_type
, self
.Decorated
.stomped
.return_type
)
762 self
.assertEqual(blah
.arg_types
, self
.Decorated
.stomped
.arg_types
)
763 self
.assertEqual(blah
.__doc
__, self
.Decorated
.stomped
.__doc
__)
765 def test_doc_string(self
):
766 # Test the two techniques for setting doc strings on the signals
767 # class variables, through the "doc" keyword or as the getter doc string.
768 self
.assertEqual(self
.Decorated
.stomped
.__doc
__, 'this will stomp')
769 self
.assertEqual(self
.Decorated
.pushed
.__doc
__, 'this will push')
771 def test_unnamed_signal_gets_named(self
):
772 self
.assertEqual(str(self
.Decorated
.unnamed
), 'unnamed')
774 def test_unnamed_signal_gets_called(self
):
775 obj
= self
.Decorated()
776 obj
.connect('unnamed', self
.onUnnamed
)
777 self
.assertEqual(self
.unnamedCalled
, False)
779 self
.assertEqual(self
.unnamedCalled
, True)
781 def test_overridden_signal(self
):
782 # Test that the pushed signal is called in with super and the override
783 # which should both increment the "value" to 3
784 obj
= self
.DecoratedOverride()
785 obj
.connect("notify", obj
.on_notify
)
786 self
.assertEqual(obj
.value
, 0)
788 self
.assertEqual(obj
.value
, 1)
789 self
.assertTrue(obj
.overridden_closure_called
)
790 self
.assertTrue(obj
.notify_called
)
793 class TestSignalConnectors(unittest
.TestCase
):
794 class CustomButton(GObject
.GObject
):
795 on_notify_called
= False
796 value
= GObject
.Property(type=int)
798 @GObject.Signal(arg_types
=(int,))
799 def clicked(self
, value
):
806 def on_clicked(self
, obj
, value
):
810 def test_signal_notify(self
):
811 def on_notify(obj
, param
):
812 obj
.on_notify_called
= True
814 obj
= self
.CustomButton()
815 obj
.connect('notify', on_notify
)
816 self
.assertFalse(obj
.on_notify_called
)
818 self
.assertTrue(obj
.on_notify_called
)
820 def test_signal_emit(self
):
821 # standard callback connection with different forms of emit.
822 obj
= self
.CustomButton()
823 obj
.connect('clicked', self
.on_clicked
)
826 obj
.emit('clicked', 1)
827 self
.assertEqual(obj
.value
, 1)
828 self
.assertEqual(obj
, self
.obj
)
829 self
.assertEqual(self
.value
, 1)
831 # using class signal as param
834 obj
.emit(self
.CustomButton
.clicked
, 1)
835 self
.assertEqual(obj
, self
.obj
)
836 self
.assertEqual(self
.value
, 1)
838 # using bound signal as param
841 obj
.emit(obj
.clicked
, 1)
842 self
.assertEqual(obj
, self
.obj
)
843 self
.assertEqual(self
.value
, 1)
845 # using bound signal with emit
849 self
.assertEqual(obj
, self
.obj
)
850 self
.assertEqual(self
.value
, 1)
852 def test_signal_class_connect(self
):
853 obj
= self
.CustomButton()
854 obj
.connect(self
.CustomButton
.clicked
, self
.on_clicked
)
855 obj
.emit('clicked', 2)
856 self
.assertEqual(obj
, self
.obj
)
857 self
.assertEqual(self
.value
, 2)
859 def test_signal_bound_connect(self
):
860 obj
= self
.CustomButton()
861 obj
.clicked
.connect(self
.on_clicked
)
862 obj
.emit('clicked', 3)
863 self
.assertEqual(obj
, self
.obj
)
864 self
.assertEqual(self
.value
, 3)
867 class _ConnectDataTestBase(object):
869 # - self.Object is overridden in sub-classes.
870 # - Numeric suffixes indicate the number of user data args passed in.
873 def run_connect_test(self
, emit_args
, user_data
, flags
=0):
878 callback_args
.append(args
)
881 obj
.connect_data('sig-with-int64-prop', callback
, connect_flags
=flags
, *user_data
)
882 obj
.emit('sig-with-int64-prop', *emit_args
)
883 self
.assertEqual(len(callback_args
), 1)
884 return callback_args
[0]
887 obj
, value
= self
.run_connect_test([GLib
.MAXINT64
], user_data
=[])
888 self
.assertIsInstance(obj
, self
.Object
)
889 self
.assertEqual(value
, GLib
.MAXINT64
)
892 obj
, value
, data
= self
.run_connect_test([GLib
.MAXINT64
],
893 user_data
=['mydata'])
894 self
.assertIsInstance(obj
, self
.Object
)
895 self
.assertEqual(value
, GLib
.MAXINT64
)
896 self
.assertEqual(data
, 'mydata')
898 def test_after_0(self
):
899 obj
, value
= self
.run_connect_test([GLib
.MAXINT64
],
901 flags
=GObject
.ConnectFlags
.AFTER
)
902 self
.assertIsInstance(obj
, self
.Object
)
903 self
.assertEqual(value
, GLib
.MAXINT64
)
905 def test_after_1(self
):
906 obj
, value
, data
= self
.run_connect_test([GLib
.MAXINT64
],
907 user_data
=['mydata'],
908 flags
=GObject
.ConnectFlags
.AFTER
)
909 self
.assertIsInstance(obj
, self
.Object
)
910 self
.assertEqual(value
, GLib
.MAXINT64
)
911 self
.assertEqual(data
, 'mydata')
913 def test_swaped_0(self
):
914 # Swapped only works with a single user data argument.
915 with self
.assertRaises(ValueError):
916 self
.run_connect_test([GLib
.MAXINT64
],
918 flags
=GObject
.ConnectFlags
.SWAPPED
)
920 def test_swaped_1(self
):
921 # Notice obj and data are reversed in the return.
922 data
, value
, obj
= self
.run_connect_test([GLib
.MAXINT64
],
923 user_data
=['mydata'],
924 flags
=GObject
.ConnectFlags
.SWAPPED
)
925 self
.assertIsInstance(obj
, self
.Object
)
926 self
.assertEqual(value
, GLib
.MAXINT64
)
927 self
.assertEqual(data
, 'mydata')
929 def test_swaped_2(self
):
930 # Swapped only works with a single user data argument.
931 with self
.assertRaises(ValueError):
932 self
.run_connect_test([GLib
.MAXINT64
],
934 flags
=GObject
.ConnectFlags
.SWAPPED
)
936 def test_after_and_swapped_0(self
):
937 # Swapped only works with a single user data argument.
938 with self
.assertRaises(ValueError):
939 self
.run_connect_test([GLib
.MAXINT64
],
941 flags
=GObject
.ConnectFlags
.AFTER | GObject
.ConnectFlags
.SWAPPED
)
943 def test_after_and_swapped_1(self
):
944 # Notice obj and data are reversed in the return.
945 data
, value
, obj
= self
.run_connect_test([GLib
.MAXINT64
],
946 user_data
=['mydata'],
947 flags
=GObject
.ConnectFlags
.AFTER | GObject
.ConnectFlags
.SWAPPED
)
948 self
.assertIsInstance(obj
, self
.Object
)
949 self
.assertEqual(value
, GLib
.MAXINT64
)
950 self
.assertEqual(data
, 'mydata')
952 def test_after_and_swapped_2(self
):
953 # Swapped only works with a single user data argument.
954 with self
.assertRaises(ValueError):
955 self
.run_connect_test([GLib
.MAXINT64
],
957 flags
=GObject
.ConnectFlags
.AFTER | GObject
.ConnectFlags
.SWAPPED
)
960 class TestConnectDataNonIntrospected(unittest
.TestCase
, _ConnectDataTestBase
):
961 # This tests connect_data with non-introspected signals
962 # (created in Python in this case).
963 class Object(GObject
.Object
):
964 test
= GObject
.Signal()
965 sig_with_int64_prop
= GObject
.Signal(return_type
=GObject
.TYPE_INT64
,
966 arg_types
=[GObject
.TYPE_INT64
],
967 flags
=GObject
.SignalFlags
.RUN_LAST
)
970 class TestConnectDataIntrospected(unittest
.TestCase
, _ConnectDataTestBase
):
971 # This tests connect_data with introspected signals brought in from Regress.
972 Object
= Regress
.TestObj
975 class TestInstallSignals(unittest
.TestCase
):
976 # These tests only test how signalhelper.install_signals works
977 # with the __gsignals__ dict and therefore does not need to use
978 # GObject as a base class because that would automatically call
979 # install_signals within the meta-class.
981 __gsignals__
= {'test': (0, None, tuple())}
992 self
.assertEqual(len(self
.Base
.__gsignals
__), 1)
993 signalhelper
.install_signals(self
.Base
)
994 self
.assertEqual(len(self
.Base
.__gsignals
__), 1)
996 def test_subclass_gets_empty_gsignals_dict(self
):
997 # Installing signals will add the __gsignals__ dict to a class
998 # if it doesn't already exists.
999 self
.assertFalse('__gsignals__' in self
.Sub1
.__dict__
)
1000 signalhelper
.install_signals(self
.Sub1
)
1001 self
.assertTrue('__gsignals__' in self
.Sub1
.__dict__
)
1002 # Sub1 should only contain an empty signals dict, this tests:
1003 # https://bugzilla.gnome.org/show_bug.cgi?id=686496
1004 self
.assertEqual(self
.Sub1
.__dict__
['__gsignals__'], {})
1006 def test_subclass_with_decorator_gets_gsignals_dict(self
):
1007 self
.assertFalse('__gsignals__' in self
.Sub2
.__dict__
)
1008 signalhelper
.install_signals(self
.Sub2
)
1009 self
.assertTrue('__gsignals__' in self
.Sub2
.__dict__
)
1010 self
.assertEqual(len(self
.Base
.__gsignals
__), 1)
1011 self
.assertEqual(len(self
.Sub2
.__gsignals__
), 1)
1012 self
.assertTrue('sub2test' in self
.Sub2
.__gsignals__
)
1014 # Make sure the vfunc was added
1015 self
.assertTrue(hasattr(self
.Sub2
, 'do_sub2test'))
1018 # For this test to work with both python2 and 3 we need to dynamically
1019 # exec the given code due to the new syntax causing an error in python 2.
1020 annotated_class_code
= """
1021 class AnnotatedSignalClass(GObject.GObject):
1023 def sig1(self, a:int, b:float):
1026 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
1027 def sig2_with_return(self, a:int, b:float) -> str:
1032 @unittest.skipUnless(PY3
, 'Argument annotations require Python 3')
1033 class TestPython3Signals(unittest
.TestCase
):
1034 AnnotatedClass
= None
1037 exec(annotated_class_code
, globals(), globals())
1038 self
.assertTrue('AnnotatedSignalClass' in globals())
1039 self
.AnnotatedClass
= globals()['AnnotatedSignalClass']
1041 def test_annotations(self
):
1042 self
.assertEqual(signalhelper
.get_signal_annotations(self
.AnnotatedClass
.sig1
.func
),
1043 (None, (int, float)))
1044 self
.assertEqual(signalhelper
.get_signal_annotations(self
.AnnotatedClass
.sig2_with_return
.func
),
1045 (str, (int, float)))
1047 self
.assertEqual(self
.AnnotatedClass
.sig2_with_return
.get_signal_args(),
1048 (GObject
.SignalFlags
.RUN_LAST
, str, (int, float), None, None))
1049 self
.assertEqual(self
.AnnotatedClass
.sig2_with_return
.arg_types
,
1051 self
.assertEqual(self
.AnnotatedClass
.sig2_with_return
.return_type
,
1054 def test_emit_return(self
):
1055 obj
= self
.AnnotatedClass()
1056 self
.assertEqual(obj
.sig2_with_return
.emit(1, 2.0),
1060 class TestSignalModuleLevelFunctions(unittest
.TestCase
):
1061 def test_signal_list_ids_with_invalid_type(self
):
1062 with self
.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1063 GObject
.signal_list_ids(GObject
.TYPE_INVALID
)
1065 def test_signal_list_ids(self
):
1066 with self
.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1067 GObject
.signal_list_ids(GObject
.TYPE_INT
)
1069 ids
= GObject
.signal_list_ids(C
)
1070 self
.assertEqual(len(ids
), 1)
1071 # Note canonicalized names
1072 self
.assertEqual(GObject
.signal_name(ids
[0]), 'my-signal')
1073 # There is no signal 0 in gobject
1074 self
.assertEqual(GObject
.signal_name(0), None)
1076 def test_signal_lookup_with_invalid_type(self
):
1077 with self
.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1078 GObject
.signal_lookup('NOT_A_SIGNAL_NAME', GObject
.TYPE_INVALID
)
1080 def test_signal_lookup(self
):
1081 ids
= GObject
.signal_list_ids(C
)
1082 self
.assertEqual(ids
[0], GObject
.signal_lookup('my_signal', C
))
1083 self
.assertEqual(ids
[0], GObject
.signal_lookup('my-signal', C
))
1085 with self
.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1086 GObject
.signal_lookup('NOT_A_SIGNAL_NAME', GObject
.TYPE_INT
)
1088 # Invalid signal names return 0 instead of raising
1089 self
.assertEqual(GObject
.signal_lookup('NOT_A_SIGNAL_NAME', C
),
1092 def test_signal_query(self
):
1093 my_signal_id
, = GObject
.signal_list_ids(C
)
1095 # Form is: (id, name, gtype, arg_count, return_type, (arg_type1, ...))
1096 my_signal_expected_query_result
= [my_signal_id
, 'my-signal', C
.__gtype
__,
1097 1, GObject
.TYPE_NONE
, (GObject
.TYPE_INT
,)]
1098 # signal_query(name, type)
1099 self
.assertEqual(list(GObject
.signal_query('my-signal', C
)), my_signal_expected_query_result
)
1100 # signal_query(signal_id)
1101 self
.assertEqual(list(GObject
.signal_query(my_signal_id
)), my_signal_expected_query_result
)
1102 # invalid query returns None instead of raising
1103 self
.assertEqual(GObject
.signal_query(0), None)
1104 self
.assertEqual(GObject
.signal_query('NOT_A_SIGNAL', C
),
1108 class TestIntrospectedSignals(unittest
.TestCase
):
1109 def test_object_param_signal(self
):
1110 obj
= Regress
.TestObj()
1112 def callback(obj
, obj_param
):
1113 self
.assertEqual(obj_param
.props
.int, 3)
1114 self
.assertGreater(obj_param
.__grefcount
__, 1)
1118 obj
.connect('sig-with-obj', callback
)
1119 obj
.emit_sig_with_obj()
1120 self
.assertTrue(obj
.called
)
1122 def test_connect_after(self
):
1123 obj
= Regress
.TestObj()
1125 def callback(obj
, obj_param
):
1129 obj
.connect_after('sig-with-obj', callback
)
1130 obj
.emit_sig_with_obj()
1131 self
.assertTrue(obj
.called
)
1133 def test_int64_param_from_py(self
):
1134 obj
= Regress
.TestObj()
1136 def callback(obj
, i
):
1140 obj
.callback_i
= None
1141 obj
.connect('sig-with-int64-prop', callback
)
1142 rv
= obj
.emit('sig-with-int64-prop', GLib
.MAXINT64
)
1143 self
.assertEqual(rv
, GLib
.MAXINT64
)
1144 self
.assertEqual(obj
.callback_i
, GLib
.MAXINT64
)
1146 def test_uint64_param_from_py(self
):
1147 obj
= Regress
.TestObj()
1149 def callback(obj
, i
):
1153 obj
.callback_i
= None
1154 obj
.connect('sig-with-uint64-prop', callback
)
1155 rv
= obj
.emit('sig-with-uint64-prop', GLib
.MAXUINT64
)
1156 self
.assertEqual(rv
, GLib
.MAXUINT64
)
1157 self
.assertEqual(obj
.callback_i
, GLib
.MAXUINT64
)
1159 def test_int64_param_from_c(self
):
1160 obj
= Regress
.TestObj()
1162 def callback(obj
, i
):
1166 obj
.callback_i
= None
1168 obj
.connect('sig-with-int64-prop', callback
)
1169 obj
.emit_sig_with_int64()
1170 self
.assertEqual(obj
.callback_i
, GLib
.MAXINT64
)
1172 def test_uint64_param_from_c(self
):
1173 obj
= Regress
.TestObj()
1175 def callback(obj
, i
):
1179 obj
.callback_i
= None
1181 obj
.connect('sig-with-uint64-prop', callback
)
1182 obj
.emit_sig_with_uint64()
1183 self
.assertEqual(obj
.callback_i
, GLib
.MAXUINT64
)
1185 def test_intarray_ret(self
):
1186 obj
= Regress
.TestObj()
1188 def callback(obj
, i
):
1192 obj
.callback_i
= None
1195 obj
.connect('sig-with-intarray-ret', callback
)
1196 except TypeError as e
:
1197 # compat with g-i 1.34.x
1198 if 'unknown signal' in str(e
):
1202 rv
= obj
.emit('sig-with-intarray-ret', 42)
1203 self
.assertEqual(obj
.callback_i
, 42)
1204 self
.assertEqual(type(rv
), GLib
.Array
)
1205 self
.assertEqual(rv
.len, 2)
1207 @unittest.skip('https://bugzilla.gnome.org/show_bug.cgi?id=669496')
1208 def test_array_parm(self
):
1209 obj
= Regress
.TestObj()
1211 def callback(obj
, arr
):
1212 obj
.callback_arr
= arr
1214 obj
.connect('sig-with-array-prop', callback
)
1215 obj
.callback_arr
= None
1216 self
.assertEqual(obj
.emit('sig-with-array-prop', [1, 2, GLib
.MAXUINT
]), None)
1217 self
.assertEqual(obj
.callback_arr
, [1, 2, GLib
.MAXUINT
])
1219 def test_held_struct_ref(self
):
1222 def callback(obj
, struct
):
1223 # The struct held by Python will become a copy after this callback exits.
1224 struct
.some_int
= 42
1225 struct
.some_int8
= 42
1226 held_structs
.append(struct
)
1228 struct
= Regress
.TestSimpleBoxedA()
1229 obj
= Regress
.TestObj()
1231 self
.assertEqual(struct
.some_int
, 0)
1232 self
.assertEqual(struct
.some_int8
, 0)
1234 obj
.connect('test-with-static-scope-arg', callback
)
1235 obj
.emit('test-with-static-scope-arg', struct
)
1237 # The held struct will be a copy of the modified struct.
1238 self
.assertEqual(len(held_structs
), 1)
1239 held_struct
= held_structs
[0]
1240 self
.assertEqual(held_struct
.some_int
, 42)
1241 self
.assertEqual(held_struct
.some_int8
, 42)
1243 # Boxed equality checks pointers by default.
1244 self
.assertNotEqual(struct
, held_struct
)
1247 class TestIntrospectedSignalsIssue158(unittest
.TestCase
):
1249 The test for https://gitlab.gnome.org/GNOME/pygobject/issues/158
1251 _obj_sig_names
= [sig
.get_name() for sig
in repo
.find_by_name('Regress', 'TestObj').get_signals()]
1253 def __init__(self
, *args
):
1254 unittest
.TestCase
.__init
__(self
, *args
)
1255 self
._gc
_thread
_stop
= False
1257 def _gc_thread(self
):
1258 while not self
._gc
_thread
_stop
:
1262 def _callback(self
, *args
):
1267 Manually trigger GC from a different thread periodicaly
1268 while the main thread keeps connecting/disconnecting to/from signals.
1270 It takes a lot of time to reproduce the issue. It is possible to make it
1271 fail reliably by changing the code of pygobject_unwatch_closure slightly from:
1272 PyGObjectData *inst_data = data;
1273 inst_data->closures = g_slist_remove (inst_data->closures, closure);
1275 PyGObjectData *inst_data = data;
1276 GSList *tmp = g_slist_remove (inst_data->closures, closure);
1277 g_usleep(G_USEC_PER_SEC/10);
1278 inst_data->closures = tmp;
1280 obj
= Regress
.TestObj()
1281 gc_thread
= threading
.Thread(target
=self
._gc
_thread
)
1285 handlers
= [obj
.connect(sig
, self
._callback
) for sig
in self
._obj
_sig
_names
]
1287 while len(handlers
) > 0:
1288 obj
.disconnect(handlers
.pop())
1290 self
._gc
_thread
_stop
= True
1294 class _ConnectObjectTestBase(object):
1296 # - self.Object is overridden in sub-classes.
1297 # - Numeric suffixes indicate the number of user data args passed in.
1301 def run_connect_test(self
, emit_args
, user_data
, flags
=0):
1304 swap_obj
= self
.SwapObject()
1306 def callback(*args
):
1307 callback_args
.append(args
)
1310 if flags
& GObject
.ConnectFlags
.AFTER
:
1311 connect_func
= obj
.connect_object_after
1313 connect_func
= obj
.connect_object
1315 with
capture_gi_deprecation_warnings():
1316 connect_func('sig-with-int64-prop', callback
, swap_obj
, *user_data
)
1317 obj
.emit('sig-with-int64-prop', *emit_args
)
1318 self
.assertEqual(len(callback_args
), 1)
1319 return callback_args
[0]
1322 obj
, value
= self
.run_connect_test([GLib
.MAXINT64
], user_data
=[])
1323 self
.assertIsInstance(obj
, self
.SwapObject
)
1324 self
.assertEqual(value
, GLib
.MAXINT64
)
1327 obj
, value
, data
= self
.run_connect_test([GLib
.MAXINT64
],
1328 user_data
=['mydata'])
1329 self
.assertIsInstance(obj
, self
.SwapObject
)
1330 self
.assertEqual(value
, GLib
.MAXINT64
)
1331 self
.assertEqual(data
, 'mydata')
1334 obj
, value
, data1
, data2
= self
.run_connect_test([GLib
.MAXINT64
],
1335 user_data
=['mydata1', 'mydata2'])
1336 self
.assertIsInstance(obj
, self
.SwapObject
)
1337 self
.assertEqual(value
, GLib
.MAXINT64
)
1338 self
.assertEqual(data1
, 'mydata1')
1339 self
.assertEqual(data2
, 'mydata2')
1341 def test_after_0(self
):
1342 obj
, value
= self
.run_connect_test([GLib
.MAXINT64
],
1344 flags
=GObject
.ConnectFlags
.AFTER
)
1345 self
.assertIsInstance(obj
, self
.SwapObject
)
1346 self
.assertEqual(value
, GLib
.MAXINT64
)
1348 def test_after_1(self
):
1349 obj
, value
, data
= self
.run_connect_test([GLib
.MAXINT64
],
1350 user_data
=['mydata'],
1351 flags
=GObject
.ConnectFlags
.AFTER
)
1352 self
.assertIsInstance(obj
, self
.SwapObject
)
1353 self
.assertEqual(value
, GLib
.MAXINT64
)
1354 self
.assertEqual(data
, 'mydata')
1356 def test_after_2(self
):
1357 obj
, value
, data1
, data2
= self
.run_connect_test([GLib
.MAXINT64
],
1358 user_data
=['mydata1', 'mydata2'],
1359 flags
=GObject
.ConnectFlags
.AFTER
)
1360 self
.assertIsInstance(obj
, self
.SwapObject
)
1361 self
.assertEqual(value
, GLib
.MAXINT64
)
1362 self
.assertEqual(data1
, 'mydata1')
1363 self
.assertEqual(data2
, 'mydata2')
1366 class TestConnectGObjectNonIntrospected(unittest
.TestCase
, _ConnectObjectTestBase
):
1367 # This tests connect_object with non-introspected signals
1368 # (created in Python in this case).
1369 class Object(GObject
.Object
):
1370 test
= GObject
.Signal()
1371 sig_with_int64_prop
= GObject
.Signal(return_type
=GObject
.TYPE_INT64
,
1372 arg_types
=[GObject
.TYPE_INT64
],
1373 flags
=GObject
.SignalFlags
.RUN_LAST
)
1375 # Object passed for swapping is GObject based.
1376 class SwapObject(GObject
.Object
):
1380 class TestConnectGObjectIntrospected(unittest
.TestCase
, _ConnectObjectTestBase
):
1381 # This tests connect_object with introspected signals brought in from Regress.
1382 Object
= Regress
.TestObj
1384 # Object passed for swapping is GObject based.
1385 class SwapObject(GObject
.Object
):
1389 class TestConnectPyObjectNonIntrospected(unittest
.TestCase
, _ConnectObjectTestBase
):
1390 # This tests connect_object with non-introspected signals
1391 # (created in Python in this case).
1392 class Object(GObject
.Object
):
1393 test
= GObject
.Signal()
1394 sig_with_int64_prop
= GObject
.Signal(return_type
=GObject
.TYPE_INT64
,
1395 arg_types
=[GObject
.TYPE_INT64
],
1396 flags
=GObject
.SignalFlags
.RUN_LAST
)
1398 # Object passed for swapping is pure Python
1402 class TestConnectPyObjectIntrospected(unittest
.TestCase
, _ConnectObjectTestBase
):
1403 # This tests connect_object with introspected signals brought in from Regress.
1404 Object
= Regress
.TestObj
1406 # Object passed for swapping is pure Python
1410 class _RefCountTestBase(object):
1411 # NOTE: ref counts are always one more than expected because the getrefcount()
1412 # function adds a ref for the input argument.
1414 # Sub-classes set this
1417 class PyData(object):
1420 @unittest.skipUnless(hasattr(sys
, "getrefcount"), "no sys.getrefcount")
1421 def test_callback_ref_count_del(self
):
1422 def callback(obj
, value
):
1425 callback_ref
= weakref
.ref(callback
)
1426 self
.assertEqual(sys
.getrefcount(callback
), 2)
1429 obj
.connect('sig-with-int64-prop', callback
)
1430 self
.assertEqual(sys
.getrefcount(callback
), 3)
1433 self
.assertEqual(sys
.getrefcount(callback_ref()), 2)
1435 res
= obj
.emit('sig-with-int64-prop', 42)
1436 self
.assertEqual(res
, 21)
1437 self
.assertEqual(sys
.getrefcount(callback_ref
), 2)
1440 self
.assertIsNone(callback_ref())
1442 @unittest.skipUnless(hasattr(sys
, "getrefcount"), "no sys.getrefcount")
1443 def test_callback_ref_count_disconnect(self
):
1444 def callback(obj
, value
):
1447 callback_ref
= weakref
.ref(callback
)
1448 self
.assertEqual(sys
.getrefcount(callback
), 2)
1451 handler_id
= obj
.connect('sig-with-int64-prop', callback
)
1452 self
.assertEqual(sys
.getrefcount(callback
), 3)
1455 self
.assertEqual(sys
.getrefcount(callback_ref()), 2)
1457 res
= obj
.emit('sig-with-int64-prop', 42)
1458 self
.assertEqual(res
, 21)
1459 self
.assertEqual(sys
.getrefcount(callback_ref
), 2)
1461 obj
.disconnect(handler_id
)
1462 self
.assertIsNone(callback_ref())
1464 @unittest.skipUnless(hasattr(sys
, "getrefcount"), "no sys.getrefcount")
1465 def test_callback_ref_count_disconnect_by_func(self
):
1466 def callback(obj
, value
):
1469 callback_ref
= weakref
.ref(callback
)
1470 self
.assertEqual(sys
.getrefcount(callback
), 2)
1473 obj
.connect('sig-with-int64-prop', callback
)
1474 self
.assertEqual(sys
.getrefcount(callback
), 3)
1477 self
.assertEqual(sys
.getrefcount(callback_ref()), 2)
1479 res
= obj
.emit('sig-with-int64-prop', 42)
1480 self
.assertEqual(res
, 21)
1481 self
.assertEqual(sys
.getrefcount(callback_ref
), 2)
1483 obj
.disconnect_by_func(callback_ref())
1484 self
.assertIsNone(callback_ref())
1486 @unittest.skipUnless(hasattr(sys
, "getrefcount"), "no sys.getrefcount")
1487 def test_user_data_ref_count(self
):
1488 def callback(obj
, value
, data
):
1491 data
= self
.PyData()
1492 data_ref
= weakref
.ref(data
)
1493 self
.assertEqual(sys
.getrefcount(data
), 2)
1496 obj
.connect('sig-with-int64-prop', callback
, data
)
1497 self
.assertEqual(sys
.getrefcount(data
), 3)
1500 self
.assertEqual(sys
.getrefcount(data_ref()), 2)
1502 res
= obj
.emit('sig-with-int64-prop', 42)
1503 self
.assertEqual(res
, 21)
1504 self
.assertEqual(sys
.getrefcount(data_ref()), 2)
1507 self
.assertIsNone(data_ref())
1509 @unittest.skipUnless(hasattr(sys
, "getrefcount"), "no sys.getrefcount")
1510 @unittest.expectedFailure
# https://bugzilla.gnome.org/show_bug.cgi?id=688064
1511 def test_object_ref_count(self
):
1512 # connect_object() should only weakly reference the object passed in
1513 # and auto-disconnect the signal when the object is destroyed.
1514 def callback(data
, value
):
1517 data
= GObject
.Object()
1518 data_ref
= weakref
.ref(data
)
1519 self
.assertEqual(sys
.getrefcount(data
), 2)
1522 handler_id
= obj
.connect_object('sig-with-int64-prop', callback
, data
)
1523 self
.assertEqual(sys
.getrefcount(data
), 2)
1525 res
= obj
.emit('sig-with-int64-prop', 42)
1526 self
.assertEqual(res
, 21)
1527 self
.assertEqual(sys
.getrefcount(data
), 2)
1531 self
.assertIsNone(data_ref())
1532 self
.assertFalse(obj
.handler_is_connected(handler_id
))
1535 class TestRefCountsNonIntrospected(unittest
.TestCase
, _RefCountTestBase
):
1536 class Object(GObject
.Object
):
1537 sig_with_int64_prop
= GObject
.Signal(return_type
=GObject
.TYPE_INT64
,
1538 arg_types
=[GObject
.TYPE_INT64
],
1539 flags
=GObject
.SignalFlags
.RUN_LAST
)
1542 class TestRefCountsIntrospected(unittest
.TestCase
, _RefCountTestBase
):
1543 Object
= Regress
.TestObj
1546 class TestClosureRefCycle(unittest
.TestCase
):
1548 def test_closure_ref_cycle_unreachable(self
):
1549 # https://bugzilla.gnome.org/show_bug.cgi?id=731501
1553 def on_add(store
, *args
):
1554 called
.append(store
)
1556 store
= Gio
.ListStore()
1557 store
.connect_object('items-changed', on_add
, store
)
1559 # Remove all Python references to the object and keep it alive
1562 x
.set_attribute_object('store', store
)
1566 # get it back and trigger the signal
1567 x
.get_attribute_object('store').append(Gio
.FileInfo())
1569 self
.assertEqual(len(called
), 1)
1570 self
.assertTrue(called
[0].__grefcount
__ > 0)