8 from gi.repository import GObject, GLib
9 from gi import _signalhelper as signalhelper
11 from compathelper import _long
12 from helper import capture_glib_warnings, capture_gi_deprecation_warnings
17 from gi.repository import Regress
23 class C(GObject.GObject):
24 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None,
27 def do_my_signal(self, arg):
32 def do_my_signal(self, arg2):
34 C.do_my_signal(self, arg2)
37 class TestSignalCreation(unittest.TestCase):
39 def test_illegals(self):
40 self.assertRaises(TypeError, lambda: GObject.signal_new('test',
44 (GObject.TYPE_LONG,)))
47 class TestChaining(unittest.TestCase):
50 self.inst.connect("my_signal", self.my_signal_handler_cb, 1, 2, 3)
52 def my_signal_handler_cb(self, *args):
54 assert isinstance(args[0], C)
55 assert args[0] == self.inst
57 assert isinstance(args[1], int)
60 assert args[2:] == (1, 2, 3)
62 def test_chaining(self):
63 self.inst.emit("my_signal", 42)
64 assert self.inst.arg == 42
66 def test_chaining2(self):
68 inst2.emit("my_signal", 44)
69 assert inst2.arg == 44
70 assert inst2.arg2 == 44
72 # This is for bug 153718
75 class TestGSignalsError(unittest.TestCase):
76 def test_invalid_type(self, *args):
78 class Foo(GObject.GObject):
80 self.assertRaises(TypeError, foo)
83 def test_invalid_name(self, *args):
85 class Foo(GObject.GObject):
86 __gsignals__ = {'not-exists': 'override'}
88 with capture_glib_warnings(allow_warnings=True):
89 self.assertRaises(TypeError, foo)
93 class TestGPropertyError(unittest.TestCase):
94 def test_invalid_type(self, *args):
96 class Foo(GObject.GObject):
97 __gproperties__ = None
98 self.assertRaises(TypeError, foo)
101 def test_invalid_name(self, *args):
103 class Foo(GObject.GObject):
104 __gproperties__ = {None: None}
106 self.assertRaises(TypeError, foo)
110 class TestList(unittest.TestCase):
111 def test_list_names(self):
112 self.assertEqual(GObject.signal_list_names(C), ('my-signal',))
115 def my_accumulator(ihint, return_accu, handler_return, user_data):
116 """An accumulator that stops emission when the sum of handler
117 returned values reaches 3"""
118 assert user_data == "accum data"
120 return False, return_accu
121 return True, return_accu + handler_return
124 class Foo(GObject.GObject):
125 my_acc_signal = GObject.Signal(return_type=GObject.TYPE_INT,
126 flags=GObject.SignalFlags.RUN_LAST,
127 accumulator=my_accumulator,
128 accu_data="accum data")
130 my_other_acc_signal = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
131 flags=GObject.SignalFlags.RUN_LAST,
132 accumulator=GObject.signal_accumulator_true_handled)
134 my_acc_first_wins = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
135 flags=GObject.SignalFlags.RUN_LAST,
136 accumulator=GObject.signal_accumulator_first_wins)
139 class TestAccumulator(unittest.TestCase):
141 def test_accumulator(self):
143 inst.my_acc_signal.connect(lambda obj: 1)
144 inst.my_acc_signal.connect(lambda obj: 2)
145 # the value returned in the following handler will not be
146 # considered, because at this point the accumulator already
148 inst.my_acc_signal.connect(lambda obj: 3)
149 retval = inst.my_acc_signal.emit()
150 self.assertEqual(retval, 3)
152 def test_accumulator_true_handled(self):
154 inst.my_other_acc_signal.connect(self._true_handler1)
155 inst.my_other_acc_signal.connect(self._true_handler2)
156 # the following handler will not be called because handler2
157 # returns True, so it should stop the emission.
158 inst.my_other_acc_signal.connect(self._true_handler3)
159 self.__true_val = None
160 inst.my_other_acc_signal.emit()
161 self.assertEqual(self.__true_val, 2)
163 def test_accumulator_first_wins(self):
164 # First signal hit will always win
166 inst.my_acc_first_wins.connect(self._true_handler3)
167 inst.my_acc_first_wins.connect(self._true_handler1)
168 inst.my_acc_first_wins.connect(self._true_handler2)
169 self.__true_val = None
170 inst.my_acc_first_wins.emit()
171 self.assertEqual(self.__true_val, 3)
173 def _true_handler1(self, obj):
177 def _true_handler2(self, obj):
181 def _true_handler3(self, obj):
186 class E(GObject.GObject):
187 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
190 # Property used to test detailed signal
191 prop = GObject.Property(type=int, default=0)
194 GObject.GObject.__init__(self)
198 assert self.status == 0
202 class F(GObject.GObject):
203 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
207 GObject.GObject.__init__(self)
214 class TestEmissionHook(unittest.TestCase):
218 e.connect('signal', self._callback)
219 GObject.add_emission_hook(E, "signal", self._emission_hook)
221 self.assertEqual(e.status, 3)
223 def test_remove(self):
226 e.connect('signal', self._callback)
227 hook_id = GObject.add_emission_hook(E, "signal", self._emission_hook)
228 GObject.remove_emission_hook(E, "signal", hook_id)
230 self.assertEqual(e.status, 3)
232 def _emission_hook(self, e):
233 self.assertEqual(e.status, 1)
236 def _callback(self, e):
238 self.assertEqual(e.status, 2)
240 self.assertEqual(e.status, 1)
243 def test_callback_return_false(self):
247 def _emission_hook(obj):
250 GObject.add_emission_hook(obj, "signal", _emission_hook)
253 self.assertEqual(obj.status, 3)
255 def test_callback_return_true(self):
259 def _emission_hook(obj):
262 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
265 GObject.remove_emission_hook(obj, "signal", hook_id)
266 self.assertEqual(obj.status, 4)
268 def test_callback_return_true_but_remove(self):
272 def _emission_hook(obj):
275 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
277 GObject.remove_emission_hook(obj, "signal", hook_id)
279 self.assertEqual(obj.status, 3)
282 class TestMatching(unittest.TestCase):
283 class Object(GObject.Object):
285 prop = GObject.Property(type=int, default=0)
291 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=692918
292 def test_signal_handler_block_matching(self):
294 "Hack to work around: "
300 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
303 self.assertEqual(obj.status, 0)
304 obj.emit('my-signal')
305 self.assertEqual(obj.status, 1)
307 # Blocking by match criteria disables the foo callback
308 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
309 count = GObject.signal_handlers_block_matched(obj,
310 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
311 signal_id=signal_id, detail=detail,
312 closure=foo, func=dummy, data=dummy)
313 self.assertEqual(count, 1)
314 obj.emit('my-signal')
315 self.assertEqual(obj.status, 1)
317 # Unblocking by the same match criteria allows callback to work again
318 count = GObject.signal_handlers_unblock_matched(obj,
319 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
320 signal_id=signal_id, detail=detail,
321 closure=foo, func=dummy, data=dummy)
322 self.assertEqual(count, 1)
323 obj.emit('my-signal')
324 self.assertEqual(obj.status, 2)
326 # Disconnecting by match criteria completely removes the handler
327 count = GObject.signal_handlers_disconnect_matched(obj,
328 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
329 signal_id=signal_id, detail=detail,
330 closure=foo, func=dummy, data=dummy)
331 self.assertEqual(count, 1)
332 obj.emit('my-signal')
333 self.assertEqual(obj.status, 2)
335 def test_signal_handler_find(self):
340 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
342 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
343 found_id = GObject.signal_handler_find(obj,
344 GObject.SignalMatchType.ID,
345 signal_id=signal_id, detail=detail,
346 closure=None, func=0, data=0)
347 self.assertEqual(handler_id, found_id)
350 class TestClosures(unittest.TestCase):
353 self.emission_stopped = False
354 self.emission_error = False
355 self.handler_pending = False
357 def _callback_handler_pending(self, e):
358 signal_id, detail = GObject.signal_parse_name('signal', e, True)
359 self.handler_pending = GObject.signal_has_handler_pending(e, signal_id, detail,
360 may_be_blocked=False)
362 def _callback(self, e):
365 def _callback_stop_emission(self, obj, prop, stop_it):
367 obj.stop_emission_by_name('notify::prop')
368 self.emission_stopped = True
372 def _callback_invalid_stop_emission_name(self, obj, prop):
373 with capture_glib_warnings(allow_warnings=True) as warn:
374 obj.stop_emission_by_name('notasignal::baddetail')
375 self.emission_error = True
376 self.assertTrue(warn)
378 def test_disconnect_by_func(self):
380 e.connect('signal', self._callback)
381 e.disconnect_by_func(self._callback)
383 self.assertEqual(self.count, 0)
385 def test_disconnect(self):
387 handler_id = e.connect('signal', self._callback)
388 self.assertTrue(e.handler_is_connected(handler_id))
389 e.disconnect(handler_id)
391 self.assertEqual(self.count, 0)
392 self.assertFalse(e.handler_is_connected(handler_id))
394 def test_stop_emission_by_name(self):
397 # Sandwich a callback that stops emission in between a callback that increments
398 e.connect('notify::prop', self._callback_stop_emission, False)
399 e.connect('notify::prop', self._callback_stop_emission, True)
400 e.connect('notify::prop', self._callback_stop_emission, False)
402 e.set_property('prop', 1234)
403 self.assertEqual(e.get_property('prop'), 1234)
404 self.assertEqual(self.count, 1)
405 self.assertTrue(self.emission_stopped)
407 def test_stop_emission_by_name_error(self):
410 e.connect('notify::prop', self._callback_invalid_stop_emission_name)
411 with capture_glib_warnings():
412 e.set_property('prop', 1234)
413 self.assertTrue(self.emission_error)
415 def test_handler_block(self):
417 e.connect('signal', self._callback)
418 e.handler_block_by_func(self._callback)
420 self.assertEqual(self.count, 0)
422 def test_handler_unblock(self):
424 handler_id = e.connect('signal', self._callback)
425 e.handler_block(handler_id)
426 e.handler_unblock_by_func(self._callback)
428 self.assertEqual(self.count, 1)
430 def test_handler_block_method(self):
436 def callback(self, o):
438 o.handler_block_by_func(self.callback)
442 e.connect("signal", inst.callback)
444 self.assertEqual(inst.a, 1)
447 def test_gstring(self):
448 class C(GObject.GObject):
449 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_GSTRING,
450 (GObject.TYPE_GSTRING,))}
452 def __init__(self, test):
453 GObject.GObject.__init__(self)
456 def do_my_signal(self, data):
458 self.test.assertEqual(len(data), 3)
459 return ''.join([data[2], data[1], data[0]])
461 data = c.emit("my_signal", "\01\00\02")
462 self.assertEqual(data, "\02\00\01")
464 def test_handler_pending(self):
466 obj.connect('signal', self._callback_handler_pending)
467 obj.connect('signal', self._callback)
469 self.assertEqual(self.count, 0)
470 self.assertEqual(self.handler_pending, False)
473 self.assertEqual(self.count, 1)
474 self.assertEqual(self.handler_pending, True)
476 def test_signal_handlers_destroy(self):
478 obj.connect('signal', self._callback)
479 obj.connect('signal', self._callback)
480 obj.connect('signal', self._callback)
483 self.assertEqual(self.count, 3)
485 # count should remain at 3 after all handlers are destroyed
486 GObject.signal_handlers_destroy(obj)
488 self.assertEqual(self.count, 3)
491 class SigPropClass(GObject.GObject):
492 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None,
493 (GObject.TYPE_INT,))}
496 'foo': (str, None, None, '',
497 GObject.ParamFlags.WRITABLE | GObject.ParamFlags.CONSTRUCT),
500 signal_emission_failed = False
502 def do_my_signal(self, arg):
505 def do_set_property(self, pspec, value):
506 if pspec.name == 'foo':
509 raise AttributeError('unknown property %s' % pspec.name)
511 self.emit("my-signal", 1)
513 self.signal_emission_failed = True
516 class TestSigProp(unittest.TestCase):
517 def test_emit_in_property_setter(self):
519 self.assertFalse(obj.signal_emission_failed)
522 class CM(GObject.GObject):
524 test1=(GObject.SignalFlags.RUN_FIRST, None, ()),
525 test2=(GObject.SignalFlags.RUN_LAST, None, (str,)),
526 test3=(GObject.SignalFlags.RUN_LAST, int, (GObject.TYPE_DOUBLE,)),
527 test4=(GObject.SignalFlags.RUN_FIRST, None,
528 (bool, _long, GObject.TYPE_FLOAT, GObject.TYPE_DOUBLE, int,
529 GObject.TYPE_UINT, GObject.TYPE_ULONG)),
530 test_float=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_FLOAT, (GObject.TYPE_FLOAT,)),
531 test_double=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_DOUBLE, (GObject.TYPE_DOUBLE,)),
532 test_int64=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_INT64, (GObject.TYPE_INT64,)),
533 test_string=(GObject.SignalFlags.RUN_LAST, str, (str,)),
534 test_object=(GObject.SignalFlags.RUN_LAST, object, (object,)),
535 test_paramspec=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, ()),
536 test_paramspec_in=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, (GObject.ParamSpec, )),
537 test_gvalue=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.Value,)),
538 test_gvalue_ret=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.TYPE_GTYPE,)),
541 testprop = GObject.Property(type=int)
544 class _TestCMarshaller:
547 testhelper.connectcallbacks(self.obj)
549 def test_test1(self):
550 self.obj.emit("test1")
552 def test_test2(self):
553 self.obj.emit("test2", "string")
555 def test_test3(self):
556 rv = self.obj.emit("test3", 42.0)
557 self.assertEqual(rv, 20)
559 def test_test4(self):
560 self.obj.emit("test4", True, _long(10), 3.14, 1.78, 20, _long(30), _long(31))
562 def test_float(self):
563 rv = self.obj.emit("test-float", 1.234)
564 self.assertTrue(rv >= 1.233999 and rv <= 1.2400001, rv)
566 def test_double(self):
567 rv = self.obj.emit("test-double", 1.234)
568 self.assertEqual(rv, 1.234)
570 def test_int64(self):
571 rv = self.obj.emit("test-int64", 102030405)
572 self.assertEqual(rv, 102030405)
574 rv = self.obj.emit("test-int64", GLib.MAXINT64)
575 self.assertEqual(rv, GLib.MAXINT64 - 1)
577 rv = self.obj.emit("test-int64", GLib.MININT64)
578 self.assertEqual(rv, GLib.MININT64)
580 def test_string(self):
581 rv = self.obj.emit("test-string", "str")
582 self.assertEqual(rv, "str")
584 def test_object(self):
585 rv = self.obj.emit("test-object", self)
586 self.assertEqual(rv, self)
588 def test_paramspec(self):
589 rv = self.obj.emit("test-paramspec")
590 self.assertEqual(rv.name, "test-param")
591 self.assertEqual(rv.nick, "test")
593 @unittest.skipUnless(hasattr(GObject, 'param_spec_boolean'),
594 'too old gobject-introspection')
595 def test_paramspec_in(self):
596 rv = GObject.param_spec_boolean('mybool', 'test-bool', 'do something',
597 True, GObject.ParamFlags.READABLE)
599 rv2 = self.obj.emit("test-paramspec-in", rv)
600 self.assertEqual(type(rv), type(rv2))
601 self.assertEqual(rv2.name, "mybool")
602 self.assertEqual(rv2.nick, "test-bool")
604 def test_C_paramspec(self):
605 self.notify_called = False
607 def cb_notify(obj, prop):
608 self.notify_called = True
609 self.assertEqual(obj, self.obj)
610 self.assertEqual(prop.name, "testprop")
612 self.obj.connect("notify", cb_notify)
613 self.obj.set_property("testprop", 42)
614 self.assertTrue(self.notify_called)
616 def test_gvalue(self):
618 rv = self.obj.emit("test-gvalue", 42)
619 self.assertEqual(rv, 42)
622 v = GObject.Value(GObject.TYPE_FLOAT, 1.234)
623 rv = self.obj.emit("test-gvalue", v)
624 self.assertAlmostEqual(rv, 1.234, places=4)
627 rv = self.obj.emit("test-gvalue", 1.234)
628 self.assertAlmostEqual(rv, 1.234, places=4)
631 v = GObject.Value(GObject.TYPE_INT64, GLib.MAXINT64)
632 rv = self.obj.emit("test-gvalue", v)
633 self.assertEqual(rv, GLib.MAXINT64)
636 v = GObject.Value(GObject.TYPE_UINT64, GLib.MAXUINT64)
637 rv = self.obj.emit("test-gvalue", v)
638 self.assertEqual(rv, GLib.MAXUINT64)
640 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=705291
641 def test_gvalue_implicit_int64(self):
643 rv = self.obj.emit("test-gvalue", GLib.MAXINT64)
644 self.assertEqual(rv, GLib.MAXINT64)
647 rv = self.obj.emit("test-gvalue", GLib.MAXUINT64)
648 self.assertEqual(rv, GLib.MAXUINT64)
650 def test_gvalue_ret(self):
651 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT),
653 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT),
655 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT64),
657 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT64),
659 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_STRING),
663 if 'generic-c-marshaller' in GObject.features:
664 class TestCMarshaller(_TestCMarshaller, unittest.TestCase):
668 print('** WARNING: LIBFFI disabled, not testing')
674 class TestPyGValue(unittest.TestCase):
675 def test_none_null_boxed_conversion(self):
676 class C(GObject.GObject):
677 __gsignals__ = dict(my_boxed_signal=(
678 GObject.SignalFlags.RUN_LAST,
679 GObject.TYPE_STRV, ()))
682 obj.connect('my-boxed-signal', lambda obj: None)
684 obj.emit('my-boxed-signal')
685 assert not sys.last_type
688 class TestSignalDecorator(unittest.TestCase):
689 class Decorated(GObject.GObject):
697 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
701 stomped = GObject.Signal('stomped', arg_types=(int,), doc='this will stomp')
702 unnamed = GObject.Signal()
704 class DecoratedOverride(GObject.GObject):
705 overridden_closure_called = False
706 notify_called = False
707 value = GObject.Property(type=int, default=0)
709 @GObject.SignalOverride
710 def notify(self, *args, **kargs):
711 self.overridden_closure_called = True
713 def on_notify(self, obj, prop):
714 self.notify_called = True
717 self.unnamedCalled = False
719 def onUnnamed(self, obj):
720 self.unnamedCalled = True
722 def test_get_signal_args(self):
723 self.assertEqual(self.Decorated.pushed.get_signal_args(),
724 (GObject.SignalFlags.RUN_FIRST, None, tuple(), None, None))
725 self.assertEqual(self.Decorated.pulled.get_signal_args(),
726 (GObject.SignalFlags.RUN_LAST, None, tuple(), None, None))
727 self.assertEqual(self.Decorated.stomped.get_signal_args(),
728 (GObject.SignalFlags.RUN_FIRST, None, (int,), None, None))
730 def test_closures_called(self):
731 decorated = self.Decorated()
732 self.assertEqual(decorated.value, 0)
733 decorated.pushed.emit()
734 self.assertEqual(decorated.value, 1)
735 decorated.pulled.emit()
736 self.assertEqual(decorated.value, 0)
738 def test_signal_copy(self):
739 blah = self.Decorated.stomped.copy('blah')
740 self.assertEqual(str(blah), blah)
741 self.assertEqual(blah.func, self.Decorated.stomped.func)
742 self.assertEqual(blah.flags, self.Decorated.stomped.flags)
743 self.assertEqual(blah.return_type, self.Decorated.stomped.return_type)
744 self.assertEqual(blah.arg_types, self.Decorated.stomped.arg_types)
745 self.assertEqual(blah.__doc__, self.Decorated.stomped.__doc__)
747 def test_doc_string(self):
748 # Test the two techniques for setting doc strings on the signals
749 # class variables, through the "doc" keyword or as the getter doc string.
750 self.assertEqual(self.Decorated.stomped.__doc__, 'this will stomp')
751 self.assertEqual(self.Decorated.pushed.__doc__, 'this will push')
753 def test_unnamed_signal_gets_named(self):
754 self.assertEqual(str(self.Decorated.unnamed), 'unnamed')
756 def test_unnamed_signal_gets_called(self):
757 obj = self.Decorated()
758 obj.connect('unnamed', self.onUnnamed)
759 self.assertEqual(self.unnamedCalled, False)
761 self.assertEqual(self.unnamedCalled, True)
763 def test_overridden_signal(self):
764 # Test that the pushed signal is called in with super and the override
765 # which should both increment the "value" to 3
766 obj = self.DecoratedOverride()
767 obj.connect("notify", obj.on_notify)
768 self.assertEqual(obj.value, 0)
770 self.assertEqual(obj.value, 1)
771 self.assertTrue(obj.overridden_closure_called)
772 self.assertTrue(obj.notify_called)
775 class TestSignalConnectors(unittest.TestCase):
776 class CustomButton(GObject.GObject):
777 on_notify_called = False
778 value = GObject.Property(type=int)
780 @GObject.Signal(arg_types=(int,))
781 def clicked(self, value):
788 def on_clicked(self, obj, value):
792 def test_signal_notify(self):
793 def on_notify(obj, param):
794 obj.on_notify_called = True
796 obj = self.CustomButton()
797 obj.connect('notify', on_notify)
798 self.assertFalse(obj.on_notify_called)
800 self.assertTrue(obj.on_notify_called)
802 def test_signal_emit(self):
803 # standard callback connection with different forms of emit.
804 obj = self.CustomButton()
805 obj.connect('clicked', self.on_clicked)
808 obj.emit('clicked', 1)
809 self.assertEqual(obj.value, 1)
810 self.assertEqual(obj, self.obj)
811 self.assertEqual(self.value, 1)
813 # using class signal as param
816 obj.emit(self.CustomButton.clicked, 1)
817 self.assertEqual(obj, self.obj)
818 self.assertEqual(self.value, 1)
820 # using bound signal as param
823 obj.emit(obj.clicked, 1)
824 self.assertEqual(obj, self.obj)
825 self.assertEqual(self.value, 1)
827 # using bound signal with emit
831 self.assertEqual(obj, self.obj)
832 self.assertEqual(self.value, 1)
834 def test_signal_class_connect(self):
835 obj = self.CustomButton()
836 obj.connect(self.CustomButton.clicked, self.on_clicked)
837 obj.emit('clicked', 2)
838 self.assertEqual(obj, self.obj)
839 self.assertEqual(self.value, 2)
841 def test_signal_bound_connect(self):
842 obj = self.CustomButton()
843 obj.clicked.connect(self.on_clicked)
844 obj.emit('clicked', 3)
845 self.assertEqual(obj, self.obj)
846 self.assertEqual(self.value, 3)
849 class _ConnectDataTestBase(object):
851 # - self.Object is overridden in sub-classes.
852 # - Numeric suffixes indicate the number of user data args passed in.
855 def run_connect_test(self, emit_args, user_data, flags=0):
860 callback_args.append(args)
863 obj.connect_data('sig-with-int64-prop', callback, connect_flags=flags, *user_data)
864 obj.emit('sig-with-int64-prop', *emit_args)
865 self.assertEqual(len(callback_args), 1)
866 return callback_args[0]
869 obj, value = self.run_connect_test([GLib.MAXINT64], user_data=[])
870 self.assertIsInstance(obj, self.Object)
871 self.assertEqual(value, GLib.MAXINT64)
874 obj, value, data = self.run_connect_test([GLib.MAXINT64],
875 user_data=['mydata'])
876 self.assertIsInstance(obj, self.Object)
877 self.assertEqual(value, GLib.MAXINT64)
878 self.assertEqual(data, 'mydata')
880 def test_after_0(self):
881 obj, value = self.run_connect_test([GLib.MAXINT64],
883 flags=GObject.ConnectFlags.AFTER)
884 self.assertIsInstance(obj, self.Object)
885 self.assertEqual(value, GLib.MAXINT64)
887 def test_after_1(self):
888 obj, value, data = self.run_connect_test([GLib.MAXINT64],
889 user_data=['mydata'],
890 flags=GObject.ConnectFlags.AFTER)
891 self.assertIsInstance(obj, self.Object)
892 self.assertEqual(value, GLib.MAXINT64)
893 self.assertEqual(data, 'mydata')
895 def test_swaped_0(self):
896 # Swapped only works with a single user data argument.
897 with self.assertRaises(ValueError):
898 self.run_connect_test([GLib.MAXINT64],
900 flags=GObject.ConnectFlags.SWAPPED)
902 def test_swaped_1(self):
903 # Notice obj and data are reversed in the return.
904 data, value, obj = self.run_connect_test([GLib.MAXINT64],
905 user_data=['mydata'],
906 flags=GObject.ConnectFlags.SWAPPED)
907 self.assertIsInstance(obj, self.Object)
908 self.assertEqual(value, GLib.MAXINT64)
909 self.assertEqual(data, 'mydata')
911 def test_swaped_2(self):
912 # Swapped only works with a single user data argument.
913 with self.assertRaises(ValueError):
914 self.run_connect_test([GLib.MAXINT64],
916 flags=GObject.ConnectFlags.SWAPPED)
918 def test_after_and_swapped_0(self):
919 # Swapped only works with a single user data argument.
920 with self.assertRaises(ValueError):
921 self.run_connect_test([GLib.MAXINT64],
923 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
925 def test_after_and_swapped_1(self):
926 # Notice obj and data are reversed in the return.
927 data, value, obj = self.run_connect_test([GLib.MAXINT64],
928 user_data=['mydata'],
929 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
930 self.assertIsInstance(obj, self.Object)
931 self.assertEqual(value, GLib.MAXINT64)
932 self.assertEqual(data, 'mydata')
934 def test_after_and_swapped_2(self):
935 # Swapped only works with a single user data argument.
936 with self.assertRaises(ValueError):
937 self.run_connect_test([GLib.MAXINT64],
939 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
942 class TestConnectDataNonIntrospected(unittest.TestCase, _ConnectDataTestBase):
943 # This tests connect_data with non-introspected signals
944 # (created in Python in this case).
945 class Object(GObject.Object):
946 test = GObject.Signal()
947 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
948 arg_types=[GObject.TYPE_INT64],
949 flags=GObject.SignalFlags.RUN_LAST)
952 @unittest.skipUnless(has_cairo, 'built without cairo support')
953 class TestConnectDataIntrospected(unittest.TestCase, _ConnectDataTestBase):
954 # This tests connect_data with introspected signals brought in from Regress.
955 Object = Regress.TestObj
958 class TestInstallSignals(unittest.TestCase):
959 # These tests only test how signalhelper.install_signals works
960 # with the __gsignals__ dict and therefore does not need to use
961 # GObject as a base class because that would automatically call
962 # install_signals within the meta-class.
964 __gsignals__ = {'test': (0, None, tuple())}
975 self.assertEqual(len(self.Base.__gsignals__), 1)
976 signalhelper.install_signals(self.Base)
977 self.assertEqual(len(self.Base.__gsignals__), 1)
979 def test_subclass_gets_empty_gsignals_dict(self):
980 # Installing signals will add the __gsignals__ dict to a class
981 # if it doesn't already exists.
982 self.assertFalse('__gsignals__' in self.Sub1.__dict__)
983 signalhelper.install_signals(self.Sub1)
984 self.assertTrue('__gsignals__' in self.Sub1.__dict__)
985 # Sub1 should only contain an empty signals dict, this tests:
986 # https://bugzilla.gnome.org/show_bug.cgi?id=686496
987 self.assertEqual(self.Sub1.__dict__['__gsignals__'], {})
989 def test_subclass_with_decorator_gets_gsignals_dict(self):
990 self.assertFalse('__gsignals__' in self.Sub2.__dict__)
991 signalhelper.install_signals(self.Sub2)
992 self.assertTrue('__gsignals__' in self.Sub2.__dict__)
993 self.assertEqual(len(self.Base.__gsignals__), 1)
994 self.assertEqual(len(self.Sub2.__gsignals__), 1)
995 self.assertTrue('sub2test' in self.Sub2.__gsignals__)
997 # Make sure the vfunc was added
998 self.assertTrue(hasattr(self.Sub2, 'do_sub2test'))
1001 # For this test to work with both python2 and 3 we need to dynamically
1002 # exec the given code due to the new syntax causing an error in python 2.
1003 annotated_class_code = """
1004 class AnnotatedSignalClass(GObject.GObject):
1006 def sig1(self, a:int, b:float):
1009 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
1010 def sig2_with_return(self, a:int, b:float) -> str:
1015 @unittest.skipUnless(sys.version_info >= (3, 0),
1016 'Argument annotations require Python 3')
1017 class TestPython3Signals(unittest.TestCase):
1018 AnnotatedClass = None
1021 exec(annotated_class_code, globals(), globals())
1022 self.assertTrue('AnnotatedSignalClass' in globals())
1023 self.AnnotatedClass = globals()['AnnotatedSignalClass']
1025 def test_annotations(self):
1026 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig1.func),
1027 (None, (int, float)))
1028 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig2_with_return.func),
1029 (str, (int, float)))
1031 self.assertEqual(self.AnnotatedClass.sig2_with_return.get_signal_args(),
1032 (GObject.SignalFlags.RUN_LAST, str, (int, float), None, None))
1033 self.assertEqual(self.AnnotatedClass.sig2_with_return.arg_types,
1035 self.assertEqual(self.AnnotatedClass.sig2_with_return.return_type,
1038 def test_emit_return(self):
1039 obj = self.AnnotatedClass()
1040 self.assertEqual(obj.sig2_with_return.emit(1, 2.0),
1044 class TestSignalModuleLevelFunctions(unittest.TestCase):
1045 def test_signal_list_ids_with_invalid_type(self):
1046 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1047 GObject.signal_list_ids(GObject.TYPE_INVALID)
1049 def test_signal_list_ids(self):
1050 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1051 GObject.signal_list_ids(GObject.TYPE_INT)
1053 ids = GObject.signal_list_ids(C)
1054 self.assertEqual(len(ids), 1)
1055 # Note canonicalized names
1056 self.assertEqual(GObject.signal_name(ids[0]), 'my-signal')
1057 # There is no signal 0 in gobject
1058 self.assertEqual(GObject.signal_name(0), None)
1060 def test_signal_lookup_with_invalid_type(self):
1061 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1062 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INVALID)
1064 def test_signal_lookup(self):
1065 ids = GObject.signal_list_ids(C)
1066 self.assertEqual(ids[0], GObject.signal_lookup('my_signal', C))
1067 self.assertEqual(ids[0], GObject.signal_lookup('my-signal', C))
1069 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1070 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INT)
1072 # Invalid signal names return 0 instead of raising
1073 self.assertEqual(GObject.signal_lookup('NOT_A_SIGNAL_NAME', C),
1076 def test_signal_query(self):
1077 my_signal_id, = GObject.signal_list_ids(C)
1079 # Form is: (id, name, gtype, arg_count, return_type, (arg_type1, ...))
1080 my_signal_expected_query_result = [my_signal_id, 'my-signal', C.__gtype__,
1081 1, GObject.TYPE_NONE, (GObject.TYPE_INT,)]
1082 # signal_query(name, type)
1083 self.assertEqual(list(GObject.signal_query('my-signal', C)), my_signal_expected_query_result)
1084 # signal_query(signal_id)
1085 self.assertEqual(list(GObject.signal_query(my_signal_id)), my_signal_expected_query_result)
1086 # invalid query returns None instead of raising
1087 self.assertEqual(GObject.signal_query(0), None)
1088 self.assertEqual(GObject.signal_query('NOT_A_SIGNAL', C),
1092 @unittest.skipUnless(has_cairo, 'built without cairo support')
1093 class TestIntrospectedSignals(unittest.TestCase):
1094 def test_object_param_signal(self):
1095 obj = Regress.TestObj()
1097 def callback(obj, obj_param):
1098 self.assertEqual(obj_param.props.int, 3)
1099 self.assertGreater(obj_param.__grefcount__, 1)
1103 obj.connect('sig-with-obj', callback)
1104 obj.emit_sig_with_obj()
1105 self.assertTrue(obj.called)
1107 def test_connect_after(self):
1108 obj = Regress.TestObj()
1110 def callback(obj, obj_param):
1114 obj.connect_after('sig-with-obj', callback)
1115 obj.emit_sig_with_obj()
1116 self.assertTrue(obj.called)
1118 def test_int64_param_from_py(self):
1119 obj = Regress.TestObj()
1121 def callback(obj, i):
1125 obj.callback_i = None
1126 obj.connect('sig-with-int64-prop', callback)
1127 rv = obj.emit('sig-with-int64-prop', GLib.MAXINT64)
1128 self.assertEqual(rv, GLib.MAXINT64)
1129 self.assertEqual(obj.callback_i, GLib.MAXINT64)
1131 def test_uint64_param_from_py(self):
1132 obj = Regress.TestObj()
1134 def callback(obj, i):
1138 obj.callback_i = None
1139 obj.connect('sig-with-uint64-prop', callback)
1140 rv = obj.emit('sig-with-uint64-prop', GLib.MAXUINT64)
1141 self.assertEqual(rv, GLib.MAXUINT64)
1142 self.assertEqual(obj.callback_i, GLib.MAXUINT64)
1144 def test_int64_param_from_c(self):
1145 obj = Regress.TestObj()
1147 def callback(obj, i):
1151 obj.callback_i = None
1153 obj.connect('sig-with-int64-prop', callback)
1154 obj.emit_sig_with_int64()
1155 self.assertEqual(obj.callback_i, GLib.MAXINT64)
1157 def test_uint64_param_from_c(self):
1158 obj = Regress.TestObj()
1160 def callback(obj, i):
1164 obj.callback_i = None
1166 obj.connect('sig-with-uint64-prop', callback)
1167 obj.emit_sig_with_uint64()
1168 self.assertEqual(obj.callback_i, GLib.MAXUINT64)
1170 def test_intarray_ret(self):
1171 obj = Regress.TestObj()
1173 def callback(obj, i):
1177 obj.callback_i = None
1180 obj.connect('sig-with-intarray-ret', callback)
1181 except TypeError as e:
1182 # compat with g-i 1.34.x
1183 if 'unknown signal' in str(e):
1187 rv = obj.emit('sig-with-intarray-ret', 42)
1188 self.assertEqual(obj.callback_i, 42)
1189 self.assertEqual(type(rv), GLib.Array)
1190 self.assertEqual(rv.len, 2)
1192 @unittest.skip('https://bugzilla.gnome.org/show_bug.cgi?id=669496')
1193 def test_array_parm(self):
1194 obj = Regress.TestObj()
1196 def callback(obj, arr):
1197 obj.callback_arr = arr
1199 obj.connect('sig-with-array-prop', callback)
1200 obj.callback_arr = None
1201 self.assertEqual(obj.emit('sig-with-array-prop', [1, 2, GLib.MAXUINT]), None)
1202 self.assertEqual(obj.callback_arr, [1, 2, GLib.MAXUINT])
1204 def test_held_struct_ref(self):
1207 def callback(obj, struct):
1208 # The struct held by Python will become a copy after this callback exits.
1209 struct.some_int = 42
1210 struct.some_int8 = 42
1211 held_structs.append(struct)
1213 struct = Regress.TestSimpleBoxedA()
1214 obj = Regress.TestObj()
1216 self.assertEqual(struct.some_int, 0)
1217 self.assertEqual(struct.some_int8, 0)
1219 obj.connect('test-with-static-scope-arg', callback)
1220 obj.emit('test-with-static-scope-arg', struct)
1222 # The held struct will be a copy of the modified struct.
1223 self.assertEqual(len(held_structs), 1)
1224 held_struct = held_structs[0]
1225 self.assertEqual(held_struct.some_int, 42)
1226 self.assertEqual(held_struct.some_int8, 42)
1228 # Boxed equality checks pointers by default.
1229 self.assertNotEqual(struct, held_struct)
1232 class _ConnectObjectTestBase(object):
1234 # - self.Object is overridden in sub-classes.
1235 # - Numeric suffixes indicate the number of user data args passed in.
1239 def run_connect_test(self, emit_args, user_data, flags=0):
1242 swap_obj = self.SwapObject()
1244 def callback(*args):
1245 callback_args.append(args)
1248 if flags & GObject.ConnectFlags.AFTER:
1249 connect_func = obj.connect_object_after
1251 connect_func = obj.connect_object
1253 with capture_gi_deprecation_warnings():
1254 connect_func('sig-with-int64-prop', callback, swap_obj, *user_data)
1255 obj.emit('sig-with-int64-prop', *emit_args)
1256 self.assertEqual(len(callback_args), 1)
1257 return callback_args[0]
1260 obj, value = self.run_connect_test([GLib.MAXINT64], user_data=[])
1261 self.assertIsInstance(obj, self.SwapObject)
1262 self.assertEqual(value, GLib.MAXINT64)
1265 obj, value, data = self.run_connect_test([GLib.MAXINT64],
1266 user_data=['mydata'])
1267 self.assertIsInstance(obj, self.SwapObject)
1268 self.assertEqual(value, GLib.MAXINT64)
1269 self.assertEqual(data, 'mydata')
1272 obj, value, data1, data2 = self.run_connect_test([GLib.MAXINT64],
1273 user_data=['mydata1', 'mydata2'])
1274 self.assertIsInstance(obj, self.SwapObject)
1275 self.assertEqual(value, GLib.MAXINT64)
1276 self.assertEqual(data1, 'mydata1')
1277 self.assertEqual(data2, 'mydata2')
1279 def test_after_0(self):
1280 obj, value = self.run_connect_test([GLib.MAXINT64],
1282 flags=GObject.ConnectFlags.AFTER)
1283 self.assertIsInstance(obj, self.SwapObject)
1284 self.assertEqual(value, GLib.MAXINT64)
1286 def test_after_1(self):
1287 obj, value, data = self.run_connect_test([GLib.MAXINT64],
1288 user_data=['mydata'],
1289 flags=GObject.ConnectFlags.AFTER)
1290 self.assertIsInstance(obj, self.SwapObject)
1291 self.assertEqual(value, GLib.MAXINT64)
1292 self.assertEqual(data, 'mydata')
1294 def test_after_2(self):
1295 obj, value, data1, data2 = self.run_connect_test([GLib.MAXINT64],
1296 user_data=['mydata1', 'mydata2'],
1297 flags=GObject.ConnectFlags.AFTER)
1298 self.assertIsInstance(obj, self.SwapObject)
1299 self.assertEqual(value, GLib.MAXINT64)
1300 self.assertEqual(data1, 'mydata1')
1301 self.assertEqual(data2, 'mydata2')
1304 class TestConnectGObjectNonIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1305 # This tests connect_object with non-introspected signals
1306 # (created in Python in this case).
1307 class Object(GObject.Object):
1308 test = GObject.Signal()
1309 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1310 arg_types=[GObject.TYPE_INT64],
1311 flags=GObject.SignalFlags.RUN_LAST)
1313 # Object passed for swapping is GObject based.
1314 class SwapObject(GObject.Object):
1318 @unittest.skipUnless(has_cairo, 'built without cairo support')
1319 class TestConnectGObjectIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1320 # This tests connect_object with introspected signals brought in from Regress.
1321 Object = Regress.TestObj
1323 # Object passed for swapping is GObject based.
1324 class SwapObject(GObject.Object):
1328 class TestConnectPyObjectNonIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1329 # This tests connect_object with non-introspected signals
1330 # (created in Python in this case).
1331 class Object(GObject.Object):
1332 test = GObject.Signal()
1333 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1334 arg_types=[GObject.TYPE_INT64],
1335 flags=GObject.SignalFlags.RUN_LAST)
1337 # Object passed for swapping is pure Python
1341 @unittest.skipUnless(has_cairo, 'built without cairo support')
1342 class TestConnectPyObjectIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1343 # This tests connect_object with introspected signals brought in from Regress.
1344 Object = Regress.TestObj
1346 # Object passed for swapping is pure Python
1350 class _RefCountTestBase(object):
1351 # NOTE: ref counts are always one more than expected because the getrefcount()
1352 # function adds a ref for the input argument.
1354 # Sub-classes set this
1357 class PyData(object):
1360 def test_callback_ref_count_del(self):
1361 def callback(obj, value):
1364 callback_ref = weakref.ref(callback)
1365 self.assertEqual(sys.getrefcount(callback), 2)
1368 obj.connect('sig-with-int64-prop', callback)
1369 self.assertEqual(sys.getrefcount(callback), 3)
1372 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1374 res = obj.emit('sig-with-int64-prop', 42)
1375 self.assertEqual(res, 21)
1376 self.assertEqual(sys.getrefcount(callback_ref), 2)
1379 self.assertIsNone(callback_ref())
1381 def test_callback_ref_count_disconnect(self):
1382 def callback(obj, value):
1385 callback_ref = weakref.ref(callback)
1386 self.assertEqual(sys.getrefcount(callback), 2)
1389 handler_id = obj.connect('sig-with-int64-prop', callback)
1390 self.assertEqual(sys.getrefcount(callback), 3)
1393 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1395 res = obj.emit('sig-with-int64-prop', 42)
1396 self.assertEqual(res, 21)
1397 self.assertEqual(sys.getrefcount(callback_ref), 2)
1399 obj.disconnect(handler_id)
1400 self.assertIsNone(callback_ref())
1402 def test_callback_ref_count_disconnect_by_func(self):
1403 def callback(obj, value):
1406 callback_ref = weakref.ref(callback)
1407 self.assertEqual(sys.getrefcount(callback), 2)
1410 obj.connect('sig-with-int64-prop', callback)
1411 self.assertEqual(sys.getrefcount(callback), 3)
1414 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1416 res = obj.emit('sig-with-int64-prop', 42)
1417 self.assertEqual(res, 21)
1418 self.assertEqual(sys.getrefcount(callback_ref), 2)
1420 obj.disconnect_by_func(callback_ref())
1421 self.assertIsNone(callback_ref())
1423 def test_user_data_ref_count(self):
1424 def callback(obj, value, data):
1427 data = self.PyData()
1428 data_ref = weakref.ref(data)
1429 self.assertEqual(sys.getrefcount(data), 2)
1432 obj.connect('sig-with-int64-prop', callback, data)
1433 self.assertEqual(sys.getrefcount(data), 3)
1436 self.assertEqual(sys.getrefcount(data_ref()), 2)
1438 res = obj.emit('sig-with-int64-prop', 42)
1439 self.assertEqual(res, 21)
1440 self.assertEqual(sys.getrefcount(data_ref()), 2)
1443 self.assertIsNone(data_ref())
1445 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=688064
1446 def test_object_ref_count(self):
1447 # connect_object() should only weakly reference the object passed in
1448 # and auto-disconnect the signal when the object is destroyed.
1449 def callback(data, value):
1452 data = GObject.Object()
1453 data_ref = weakref.ref(data)
1454 self.assertEqual(sys.getrefcount(data), 2)
1457 handler_id = obj.connect_object('sig-with-int64-prop', callback, data)
1458 self.assertEqual(sys.getrefcount(data), 2)
1460 res = obj.emit('sig-with-int64-prop', 42)
1461 self.assertEqual(res, 21)
1462 self.assertEqual(sys.getrefcount(data), 2)
1466 self.assertIsNone(data_ref())
1467 self.assertFalse(obj.handler_is_connected(handler_id))
1470 class TestRefCountsNonIntrospected(unittest.TestCase, _RefCountTestBase):
1471 class Object(GObject.Object):
1472 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1473 arg_types=[GObject.TYPE_INT64],
1474 flags=GObject.SignalFlags.RUN_LAST)
1477 @unittest.skipUnless(has_cairo, 'built without cairo support')
1478 class TestRefCountsIntrospected(unittest.TestCase, _RefCountTestBase):
1479 Object = Regress.TestObj
1482 if __name__ == '__main__':