7 from gi.repository import GObject, GLib
8 from gi import _signalhelper as signalhelper
10 from compathelper import _long
13 class C(GObject.GObject):
14 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None,
17 def do_my_signal(self, arg):
22 def do_my_signal(self, arg2):
24 C.do_my_signal(self, arg2)
27 class TestSignalCreation(unittest.TestCase):
29 def test_illegals(self):
30 self.assertRaises(TypeError, lambda: GObject.signal_new('test',
34 (GObject.TYPE_LONG,)))
37 class TestChaining(unittest.TestCase):
40 self.inst.connect("my_signal", self.my_signal_handler_cb, 1, 2, 3)
42 def my_signal_handler_cb(self, *args):
44 assert isinstance(args[0], C)
45 assert args[0] == self.inst
47 assert isinstance(args[1], int)
50 assert args[2:] == (1, 2, 3)
52 def test_chaining(self):
53 self.inst.emit("my_signal", 42)
54 assert self.inst.arg == 42
56 def test_chaining2(self):
58 inst2.emit("my_signal", 44)
59 assert inst2.arg == 44
60 assert inst2.arg2 == 44
62 # This is for bug 153718
65 class TestGSignalsError(unittest.TestCase):
66 def test_invalid_type(self, *args):
68 class Foo(GObject.GObject):
70 self.assertRaises(TypeError, foo)
73 def test_invalid_name(self, *args):
75 class Foo(GObject.GObject):
76 __gsignals__ = {'not-exists': 'override'}
77 # do not stumble over the warning thrown by GLib
78 old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL |
79 GLib.LogLevelFlags.LEVEL_ERROR)
81 self.assertRaises(TypeError, foo)
83 GLib.log_set_always_fatal(old_mask)
87 class TestGPropertyError(unittest.TestCase):
88 def test_invalid_type(self, *args):
90 class Foo(GObject.GObject):
91 __gproperties__ = None
92 self.assertRaises(TypeError, foo)
95 def test_invalid_name(self, *args):
97 class Foo(GObject.GObject):
98 __gproperties__ = {None: None}
100 self.assertRaises(TypeError, foo)
104 class TestList(unittest.TestCase):
105 def test_list_names(self):
106 self.assertEqual(GObject.signal_list_names(C), ('my-signal',))
109 def my_accumulator(ihint, return_accu, handler_return, user_data):
110 """An accumulator that stops emission when the sum of handler
111 returned values reaches 3"""
112 assert user_data == "accum data"
114 return False, return_accu
115 return True, return_accu + handler_return
118 class Foo(GObject.GObject):
119 my_acc_signal = GObject.Signal(return_type=GObject.TYPE_INT,
120 flags=GObject.SignalFlags.RUN_LAST,
121 accumulator=my_accumulator,
122 accu_data="accum data")
124 my_other_acc_signal = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
125 flags=GObject.SignalFlags.RUN_LAST,
126 accumulator=GObject.signal_accumulator_true_handled)
128 my_acc_first_wins = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
129 flags=GObject.SignalFlags.RUN_LAST,
130 accumulator=GObject.signal_accumulator_first_wins)
133 class TestAccumulator(unittest.TestCase):
135 def test_accumulator(self):
137 inst.my_acc_signal.connect(lambda obj: 1)
138 inst.my_acc_signal.connect(lambda obj: 2)
139 # the value returned in the following handler will not be
140 # considered, because at this point the accumulator already
142 inst.my_acc_signal.connect(lambda obj: 3)
143 retval = inst.my_acc_signal.emit()
144 self.assertEqual(retval, 3)
146 def test_accumulator_true_handled(self):
148 inst.my_other_acc_signal.connect(self._true_handler1)
149 inst.my_other_acc_signal.connect(self._true_handler2)
150 # the following handler will not be called because handler2
151 # returns True, so it should stop the emission.
152 inst.my_other_acc_signal.connect(self._true_handler3)
153 self.__true_val = None
154 inst.my_other_acc_signal.emit()
155 self.assertEqual(self.__true_val, 2)
157 def test_accumulator_first_wins(self):
158 # First signal hit will always win
160 inst.my_acc_first_wins.connect(self._true_handler3)
161 inst.my_acc_first_wins.connect(self._true_handler1)
162 inst.my_acc_first_wins.connect(self._true_handler2)
163 self.__true_val = None
164 inst.my_acc_first_wins.emit()
165 self.assertEqual(self.__true_val, 3)
167 def _true_handler1(self, obj):
171 def _true_handler2(self, obj):
175 def _true_handler3(self, obj):
180 class E(GObject.GObject):
181 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
184 # Property used to test detailed signal
185 prop = GObject.Property(type=int, default=0)
188 GObject.GObject.__init__(self)
192 assert self.status == 0
196 class F(GObject.GObject):
197 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
201 GObject.GObject.__init__(self)
208 class TestEmissionHook(unittest.TestCase):
212 e.connect('signal', self._callback)
213 GObject.add_emission_hook(E, "signal", self._emission_hook)
215 self.assertEqual(e.status, 3)
217 def test_remove(self):
220 e.connect('signal', self._callback)
221 hook_id = GObject.add_emission_hook(E, "signal", self._emission_hook)
222 GObject.remove_emission_hook(E, "signal", hook_id)
224 self.assertEqual(e.status, 3)
226 def _emission_hook(self, e):
227 self.assertEqual(e.status, 1)
230 def _callback(self, e):
232 self.assertEqual(e.status, 2)
234 self.assertEqual(e.status, 1)
237 def test_callback_return_false(self):
241 def _emission_hook(obj):
244 GObject.add_emission_hook(obj, "signal", _emission_hook)
247 self.assertEqual(obj.status, 3)
249 def test_callback_return_true(self):
253 def _emission_hook(obj):
256 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
259 GObject.remove_emission_hook(obj, "signal", hook_id)
260 self.assertEqual(obj.status, 4)
262 def test_callback_return_true_but_remove(self):
266 def _emission_hook(obj):
269 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
271 GObject.remove_emission_hook(obj, "signal", hook_id)
273 self.assertEqual(obj.status, 3)
276 class TestMatching(unittest.TestCase):
277 class Object(GObject.Object):
279 prop = GObject.Property(type=int, default=0)
285 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=692918
286 def test_signal_handler_block_matching(self):
288 "Hack to work around: "
294 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
297 self.assertEqual(obj.status, 0)
298 obj.emit('my-signal')
299 self.assertEqual(obj.status, 1)
301 # Blocking by match criteria disables the foo callback
302 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
303 count = GObject.signal_handlers_block_matched(obj,
304 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
305 signal_id=signal_id, detail=detail,
306 closure=foo, func=dummy, data=dummy)
307 self.assertEqual(count, 1)
308 obj.emit('my-signal')
309 self.assertEqual(obj.status, 1)
311 # Unblocking by the same match criteria allows callback to work again
312 count = GObject.signal_handlers_unblock_matched(obj,
313 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
314 signal_id=signal_id, detail=detail,
315 closure=foo, func=dummy, data=dummy)
316 self.assertEqual(count, 1)
317 obj.emit('my-signal')
318 self.assertEqual(obj.status, 2)
320 # Disconnecting by match criteria completely removes the handler
321 count = GObject.signal_handlers_disconnect_matched(obj,
322 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
323 signal_id=signal_id, detail=detail,
324 closure=foo, func=dummy, data=dummy)
325 self.assertEqual(count, 1)
326 obj.emit('my-signal')
327 self.assertEqual(obj.status, 2)
329 def test_signal_handler_find(self):
334 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
336 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
337 found_id = GObject.signal_handler_find(obj,
338 GObject.SignalMatchType.ID,
339 signal_id=signal_id, detail=detail,
340 closure=None, func=0, data=0)
341 self.assertEqual(handler_id, found_id)
344 class TestClosures(unittest.TestCase):
347 self.emission_stopped = False
348 self.emission_error = False
349 self.handler_pending = False
351 def _callback_handler_pending(self, e):
352 signal_id, detail = GObject.signal_parse_name('signal', e, True)
353 self.handler_pending = GObject.signal_has_handler_pending(e, signal_id, detail,
354 may_be_blocked=False)
356 def _callback(self, e):
359 def _callback_stop_emission(self, obj, prop, stop_it):
361 obj.stop_emission_by_name('notify::prop')
362 self.emission_stopped = True
366 def _callback_invalid_stop_emission_name(self, obj, prop):
367 # We expect a GLib warning but there currently is no way to test that
368 # This can at least make sure we don't crash
369 old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL |
370 GLib.LogLevelFlags.LEVEL_ERROR)
372 obj.stop_emission_by_name('notasignal::baddetail')
374 GLib.log_set_always_fatal(old_mask)
375 self.emission_error = True
377 def test_disconnect_by_func(self):
379 e.connect('signal', self._callback)
380 e.disconnect_by_func(self._callback)
382 self.assertEqual(self.count, 0)
384 def test_disconnect(self):
386 handler_id = e.connect('signal', self._callback)
387 self.assertTrue(e.handler_is_connected(handler_id))
388 e.disconnect(handler_id)
390 self.assertEqual(self.count, 0)
391 self.assertFalse(e.handler_is_connected(handler_id))
393 def test_stop_emission_by_name(self):
396 # Sandwich a callback that stops emission in between a callback that increments
397 e.connect('notify::prop', self._callback_stop_emission, False)
398 e.connect('notify::prop', self._callback_stop_emission, True)
399 e.connect('notify::prop', self._callback_stop_emission, False)
401 e.set_property('prop', 1234)
402 self.assertEqual(e.get_property('prop'), 1234)
403 self.assertEqual(self.count, 1)
404 self.assertTrue(self.emission_stopped)
406 def test_stop_emission_by_name_error(self):
409 e.connect('notify::prop', self._callback_invalid_stop_emission_name)
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, '', GObject.PARAM_WRITABLE | GObject.PARAM_CONSTRUCT),
497 signal_emission_failed = False
499 def do_my_signal(self, arg):
502 def do_set_property(self, pspec, value):
503 if pspec.name == 'foo':
506 raise AttributeError('unknown property %s' % pspec.name)
508 self.emit("my-signal", 1)
510 self.signal_emission_failed = True
513 class TestSigProp(unittest.TestCase):
514 def test_emit_in_property_setter(self):
516 self.assertFalse(obj.signal_emission_failed)
519 class CM(GObject.GObject):
521 test1=(GObject.SignalFlags.RUN_FIRST, None, ()),
522 test2=(GObject.SignalFlags.RUN_LAST, None, (str,)),
523 test3=(GObject.SignalFlags.RUN_LAST, int, (GObject.TYPE_DOUBLE,)),
524 test4=(GObject.SignalFlags.RUN_FIRST, None,
525 (bool, _long, GObject.TYPE_FLOAT, GObject.TYPE_DOUBLE, int,
526 GObject.TYPE_UINT, GObject.TYPE_ULONG)),
527 test_float=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_FLOAT, (GObject.TYPE_FLOAT,)),
528 test_double=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_DOUBLE, (GObject.TYPE_DOUBLE,)),
529 test_int64=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_INT64, (GObject.TYPE_INT64,)),
530 test_string=(GObject.SignalFlags.RUN_LAST, str, (str,)),
531 test_object=(GObject.SignalFlags.RUN_LAST, object, (object,)),
532 test_paramspec=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, ()),
533 test_paramspec_in=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, (GObject.ParamSpec, )),
534 test_gvalue=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.Value,)),
535 test_gvalue_ret=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.TYPE_GTYPE,)),
538 testprop = GObject.Property(type=int)
541 class _TestCMarshaller:
544 testhelper.connectcallbacks(self.obj)
546 def test_test1(self):
547 self.obj.emit("test1")
549 def test_test2(self):
550 self.obj.emit("test2", "string")
552 def test_test3(self):
553 rv = self.obj.emit("test3", 42.0)
554 self.assertEqual(rv, 20)
556 def test_test4(self):
557 self.obj.emit("test4", True, _long(10), 3.14, 1.78, 20, _long(30), _long(31))
559 def test_float(self):
560 rv = self.obj.emit("test-float", 1.234)
561 self.assertTrue(rv >= 1.233999 and rv <= 1.2400001, rv)
563 def test_double(self):
564 rv = self.obj.emit("test-double", 1.234)
565 self.assertEqual(rv, 1.234)
567 def test_int64(self):
568 rv = self.obj.emit("test-int64", 102030405)
569 self.assertEqual(rv, 102030405)
571 rv = self.obj.emit("test-int64", GObject.G_MAXINT64)
572 self.assertEqual(rv, GObject.G_MAXINT64 - 1)
574 rv = self.obj.emit("test-int64", GObject.G_MININT64)
575 self.assertEqual(rv, GObject.G_MININT64)
577 def test_string(self):
578 rv = self.obj.emit("test-string", "str")
579 self.assertEqual(rv, "str")
581 def test_object(self):
582 rv = self.obj.emit("test-object", self)
583 self.assertEqual(rv, self)
585 def test_paramspec(self):
586 rv = self.obj.emit("test-paramspec")
587 self.assertEqual(rv.name, "test-param")
588 self.assertEqual(rv.nick, "test")
590 @unittest.skipUnless(hasattr(GObject, 'param_spec_boolean'),
591 'too old gobject-introspection')
592 def test_paramspec_in(self):
593 rv = GObject.param_spec_boolean('mybool', 'test-bool', 'do something',
594 True, GObject.ParamFlags.READABLE)
596 rv2 = self.obj.emit("test-paramspec-in", rv)
597 self.assertEqual(type(rv), type(rv2))
598 self.assertEqual(rv2.name, "mybool")
599 self.assertEqual(rv2.nick, "test-bool")
601 def test_C_paramspec(self):
602 self.notify_called = False
604 def cb_notify(obj, prop):
605 self.notify_called = True
606 self.assertEqual(obj, self.obj)
607 self.assertEqual(prop.name, "testprop")
609 self.obj.connect("notify", cb_notify)
610 self.obj.set_property("testprop", 42)
611 self.assertTrue(self.notify_called)
613 def test_gvalue(self):
615 rv = self.obj.emit("test-gvalue", 42)
616 self.assertEqual(rv, 42)
619 v = GObject.Value(GObject.TYPE_FLOAT, 1.234)
620 rv = self.obj.emit("test-gvalue", v)
621 self.assertAlmostEqual(rv, 1.234, 4)
624 rv = self.obj.emit("test-gvalue", 1.234)
625 self.assertAlmostEqual(rv, 1.234, 4)
628 v = GObject.Value(GObject.TYPE_INT64, GObject.G_MAXINT64)
629 rv = self.obj.emit("test-gvalue", v)
630 self.assertEqual(rv, GObject.G_MAXINT64)
633 v = GObject.Value(GObject.TYPE_UINT64, GObject.G_MAXUINT64)
634 rv = self.obj.emit("test-gvalue", v)
635 self.assertEqual(rv, GObject.G_MAXUINT64)
637 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=705291
638 def test_gvalue_implicit_int64(self):
640 rv = self.obj.emit("test-gvalue", GObject.G_MAXINT64)
641 self.assertEqual(rv, GObject.G_MAXINT64)
644 rv = self.obj.emit("test-gvalue", GObject.G_MAXUINT64)
645 self.assertEqual(rv, GObject.G_MAXUINT64)
647 def test_gvalue_ret(self):
648 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT),
650 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT),
652 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT64),
654 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT64),
656 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_STRING),
659 if 'generic-c-marshaller' in GObject.features:
660 class TestCMarshaller(_TestCMarshaller, unittest.TestCase):
664 print('** WARNING: LIBFFI disabled, not testing')
670 class TestPyGValue(unittest.TestCase):
671 def test_none_null_boxed_conversion(self):
672 class C(GObject.GObject):
673 __gsignals__ = dict(my_boxed_signal=(
674 GObject.SignalFlags.RUN_LAST,
675 GObject.TYPE_STRV, ()))
678 obj.connect('my-boxed-signal', lambda obj: None)
680 obj.emit('my-boxed-signal')
681 assert not sys.last_type
684 class TestSignalDecorator(unittest.TestCase):
685 class Decorated(GObject.GObject):
693 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
697 stomped = GObject.Signal('stomped', arg_types=(int,), doc='this will stomp')
698 unnamed = GObject.Signal()
700 class DecoratedOverride(GObject.GObject):
701 overridden_closure_called = False
702 notify_called = False
703 value = GObject.Property(type=int, default=0)
705 @GObject.SignalOverride
706 def notify(self, *args, **kargs):
707 self.overridden_closure_called = True
709 def on_notify(self, obj, prop):
710 self.notify_called = True
713 self.unnamedCalled = False
715 def onUnnamed(self, obj):
716 self.unnamedCalled = True
718 def test_get_signal_args(self):
719 self.assertEqual(self.Decorated.pushed.get_signal_args(),
720 (GObject.SignalFlags.RUN_FIRST, None, tuple(), None, None))
721 self.assertEqual(self.Decorated.pulled.get_signal_args(),
722 (GObject.SignalFlags.RUN_LAST, None, tuple(), None, None))
723 self.assertEqual(self.Decorated.stomped.get_signal_args(),
724 (GObject.SignalFlags.RUN_FIRST, None, (int,), None, None))
726 def test_closures_called(self):
727 decorated = self.Decorated()
728 self.assertEqual(decorated.value, 0)
729 decorated.pushed.emit()
730 self.assertEqual(decorated.value, 1)
731 decorated.pulled.emit()
732 self.assertEqual(decorated.value, 0)
734 def test_signal_copy(self):
735 blah = self.Decorated.stomped.copy('blah')
736 self.assertEqual(str(blah), blah)
737 self.assertEqual(blah.func, self.Decorated.stomped.func)
738 self.assertEqual(blah.flags, self.Decorated.stomped.flags)
739 self.assertEqual(blah.return_type, self.Decorated.stomped.return_type)
740 self.assertEqual(blah.arg_types, self.Decorated.stomped.arg_types)
741 self.assertEqual(blah.__doc__, self.Decorated.stomped.__doc__)
743 def test_doc_string(self):
744 # Test the two techniques for setting doc strings on the signals
745 # class variables, through the "doc" keyword or as the getter doc string.
746 self.assertEqual(self.Decorated.stomped.__doc__, 'this will stomp')
747 self.assertEqual(self.Decorated.pushed.__doc__, 'this will push')
749 def test_unnamed_signal_gets_named(self):
750 self.assertEqual(str(self.Decorated.unnamed), 'unnamed')
752 def test_unnamed_signal_gets_called(self):
753 obj = self.Decorated()
754 obj.connect('unnamed', self.onUnnamed)
755 self.assertEqual(self.unnamedCalled, False)
757 self.assertEqual(self.unnamedCalled, True)
759 def NOtest_overridden_signal(self):
760 # Test that the pushed signal is called in with super and the override
761 # which should both increment the "value" to 3
762 obj = self.DecoratedOverride()
763 obj.connect("notify", obj.on_notify)
764 self.assertEqual(obj.value, 0)
766 self.assertEqual(obj.value, 1)
767 self.assertTrue(obj.overridden_closure_called)
768 self.assertTrue(obj.notify_called)
771 class TestSignalConnectors(unittest.TestCase):
772 class CustomButton(GObject.GObject):
773 on_notify_called = False
774 value = GObject.Property(type=int)
776 @GObject.Signal(arg_types=(int,))
777 def clicked(self, value):
784 def on_clicked(self, obj, value):
788 def test_signal_notify(self):
789 def on_notify(obj, param):
790 obj.on_notify_called = True
792 obj = self.CustomButton()
793 obj.connect('notify', on_notify)
794 self.assertFalse(obj.on_notify_called)
796 self.assertTrue(obj.on_notify_called)
798 def test_signal_emit(self):
799 # standard callback connection with different forms of emit.
800 obj = self.CustomButton()
801 obj.connect('clicked', self.on_clicked)
804 obj.emit('clicked', 1)
805 self.assertEqual(obj.value, 1)
806 self.assertEqual(obj, self.obj)
807 self.assertEqual(self.value, 1)
809 # using class signal as param
812 obj.emit(self.CustomButton.clicked, 1)
813 self.assertEqual(obj, self.obj)
814 self.assertEqual(self.value, 1)
816 # using bound signal as param
819 obj.emit(obj.clicked, 1)
820 self.assertEqual(obj, self.obj)
821 self.assertEqual(self.value, 1)
823 # using bound signal with emit
827 self.assertEqual(obj, self.obj)
828 self.assertEqual(self.value, 1)
830 def test_signal_class_connect(self):
831 obj = self.CustomButton()
832 obj.connect(self.CustomButton.clicked, self.on_clicked)
833 obj.emit('clicked', 2)
834 self.assertEqual(obj, self.obj)
835 self.assertEqual(self.value, 2)
837 def test_signal_bound_connect(self):
838 obj = self.CustomButton()
839 obj.clicked.connect(self.on_clicked)
840 obj.emit('clicked', 3)
841 self.assertEqual(obj, self.obj)
842 self.assertEqual(self.value, 3)
845 class TestInstallSignals(unittest.TestCase):
846 # These tests only test how signalhelper.install_signals works
847 # with the __gsignals__ dict and therefore does not need to use
848 # GObject as a base class because that would automatically call
849 # install_signals within the meta-class.
851 __gsignals__ = {'test': (0, None, tuple())}
862 self.assertEqual(len(self.Base.__gsignals__), 1)
863 signalhelper.install_signals(self.Base)
864 self.assertEqual(len(self.Base.__gsignals__), 1)
866 def test_subclass_gets_empty_gsignals_dict(self):
867 # Installing signals will add the __gsignals__ dict to a class
868 # if it doesn't already exists.
869 self.assertFalse('__gsignals__' in self.Sub1.__dict__)
870 signalhelper.install_signals(self.Sub1)
871 self.assertTrue('__gsignals__' in self.Sub1.__dict__)
872 # Sub1 should only contain an empty signals dict, this tests:
873 # https://bugzilla.gnome.org/show_bug.cgi?id=686496
874 self.assertEqual(self.Sub1.__dict__['__gsignals__'], {})
876 def test_subclass_with_decorator_gets_gsignals_dict(self):
877 self.assertFalse('__gsignals__' in self.Sub2.__dict__)
878 signalhelper.install_signals(self.Sub2)
879 self.assertTrue('__gsignals__' in self.Sub2.__dict__)
880 self.assertEqual(len(self.Base.__gsignals__), 1)
881 self.assertEqual(len(self.Sub2.__gsignals__), 1)
882 self.assertTrue('sub2test' in self.Sub2.__gsignals__)
884 # Make sure the vfunc was added
885 self.assertTrue(hasattr(self.Sub2, 'do_sub2test'))
888 # For this test to work with both python2 and 3 we need to dynamically
889 # exec the given code due to the new syntax causing an error in python 2.
890 annotated_class_code = """
891 class AnnotatedSignalClass(GObject.GObject):
893 def sig1(self, a:int, b:float):
896 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
897 def sig2_with_return(self, a:int, b:float) -> str:
902 @unittest.skipUnless(sys.version_info >= (3, 0),
903 'Argument annotations require Python 3')
904 class TestPython3Signals(unittest.TestCase):
905 AnnotatedClass = None
908 exec(annotated_class_code, globals(), globals())
909 self.assertTrue('AnnotatedSignalClass' in globals())
910 self.AnnotatedClass = globals()['AnnotatedSignalClass']
912 def test_annotations(self):
913 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig1.func),
914 (None, (int, float)))
915 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig2_with_return.func),
918 self.assertEqual(self.AnnotatedClass.sig2_with_return.get_signal_args(),
919 (GObject.SignalFlags.RUN_LAST, str, (int, float), None, None))
920 self.assertEqual(self.AnnotatedClass.sig2_with_return.arg_types,
922 self.assertEqual(self.AnnotatedClass.sig2_with_return.return_type,
925 def test_emit_return(self):
926 obj = self.AnnotatedClass()
927 self.assertEqual(obj.sig2_with_return.emit(1, 2.0),
931 class TestSignalModuleLevelFunctions(unittest.TestCase):
932 def test_signal_list_ids_with_invalid_type(self):
933 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
934 GObject.signal_list_ids(GObject.TYPE_INVALID)
936 def test_signal_list_ids(self):
937 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
938 GObject.signal_list_ids(GObject.TYPE_INT)
940 ids = GObject.signal_list_ids(C)
941 self.assertEqual(len(ids), 1)
942 # Note canonicalized names
943 self.assertEqual(GObject.signal_name(ids[0]), 'my-signal')
944 # There is no signal 0 in gobject
945 self.assertEqual(GObject.signal_name(0), None)
947 def test_signal_lookup_with_invalid_type(self):
948 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
949 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INVALID)
951 def test_signal_lookup(self):
952 ids = GObject.signal_list_ids(C)
953 self.assertEqual(ids[0], GObject.signal_lookup('my_signal', C))
954 self.assertEqual(ids[0], GObject.signal_lookup('my-signal', C))
956 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
957 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INT)
959 # Invalid signal names return 0 instead of raising
960 self.assertEqual(GObject.signal_lookup('NOT_A_SIGNAL_NAME', C),
963 def test_signal_query(self):
964 my_signal_id, = GObject.signal_list_ids(C)
966 # Form is: (id, name, gtype, arg_count, return_type, (arg_type1, ...))
967 my_signal_expected_query_result = [my_signal_id, 'my-signal', C.__gtype__,
968 1, GObject.TYPE_NONE, (GObject.TYPE_INT,)]
969 # signal_query(name, type)
970 self.assertEqual(list(GObject.signal_query('my-signal', C)), my_signal_expected_query_result)
971 # signal_query(signal_id)
972 self.assertEqual(list(GObject.signal_query(my_signal_id)), my_signal_expected_query_result)
973 # invalid query returns None instead of raising
974 self.assertEqual(GObject.signal_query(0), None)
975 self.assertEqual(GObject.signal_query('NOT_A_SIGNAL', C),
979 if __name__ == '__main__':