7 from gi.repository import GObject, GLib
8 from gi import _signalhelper as signalhelper
10 from compathelper import _long
15 from gi.repository import Regress
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'}
85 # do not stumble over the warning thrown by GLib
86 old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL |
87 GLib.LogLevelFlags.LEVEL_ERROR)
89 self.assertRaises(TypeError, foo)
91 GLib.log_set_always_fatal(old_mask)
95 class TestGPropertyError(unittest.TestCase):
96 def test_invalid_type(self, *args):
98 class Foo(GObject.GObject):
99 __gproperties__ = None
100 self.assertRaises(TypeError, foo)
103 def test_invalid_name(self, *args):
105 class Foo(GObject.GObject):
106 __gproperties__ = {None: None}
108 self.assertRaises(TypeError, foo)
112 class TestList(unittest.TestCase):
113 def test_list_names(self):
114 self.assertEqual(GObject.signal_list_names(C), ('my-signal',))
117 def my_accumulator(ihint, return_accu, handler_return, user_data):
118 """An accumulator that stops emission when the sum of handler
119 returned values reaches 3"""
120 assert user_data == "accum data"
122 return False, return_accu
123 return True, return_accu + handler_return
126 class Foo(GObject.GObject):
127 my_acc_signal = GObject.Signal(return_type=GObject.TYPE_INT,
128 flags=GObject.SignalFlags.RUN_LAST,
129 accumulator=my_accumulator,
130 accu_data="accum data")
132 my_other_acc_signal = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
133 flags=GObject.SignalFlags.RUN_LAST,
134 accumulator=GObject.signal_accumulator_true_handled)
136 my_acc_first_wins = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
137 flags=GObject.SignalFlags.RUN_LAST,
138 accumulator=GObject.signal_accumulator_first_wins)
141 class TestAccumulator(unittest.TestCase):
143 def test_accumulator(self):
145 inst.my_acc_signal.connect(lambda obj: 1)
146 inst.my_acc_signal.connect(lambda obj: 2)
147 # the value returned in the following handler will not be
148 # considered, because at this point the accumulator already
150 inst.my_acc_signal.connect(lambda obj: 3)
151 retval = inst.my_acc_signal.emit()
152 self.assertEqual(retval, 3)
154 def test_accumulator_true_handled(self):
156 inst.my_other_acc_signal.connect(self._true_handler1)
157 inst.my_other_acc_signal.connect(self._true_handler2)
158 # the following handler will not be called because handler2
159 # returns True, so it should stop the emission.
160 inst.my_other_acc_signal.connect(self._true_handler3)
161 self.__true_val = None
162 inst.my_other_acc_signal.emit()
163 self.assertEqual(self.__true_val, 2)
165 def test_accumulator_first_wins(self):
166 # First signal hit will always win
168 inst.my_acc_first_wins.connect(self._true_handler3)
169 inst.my_acc_first_wins.connect(self._true_handler1)
170 inst.my_acc_first_wins.connect(self._true_handler2)
171 self.__true_val = None
172 inst.my_acc_first_wins.emit()
173 self.assertEqual(self.__true_val, 3)
175 def _true_handler1(self, obj):
179 def _true_handler2(self, obj):
183 def _true_handler3(self, obj):
188 class E(GObject.GObject):
189 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
192 # Property used to test detailed signal
193 prop = GObject.Property(type=int, default=0)
196 GObject.GObject.__init__(self)
200 assert self.status == 0
204 class F(GObject.GObject):
205 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
209 GObject.GObject.__init__(self)
216 class TestEmissionHook(unittest.TestCase):
220 e.connect('signal', self._callback)
221 GObject.add_emission_hook(E, "signal", self._emission_hook)
223 self.assertEqual(e.status, 3)
225 def test_remove(self):
228 e.connect('signal', self._callback)
229 hook_id = GObject.add_emission_hook(E, "signal", self._emission_hook)
230 GObject.remove_emission_hook(E, "signal", hook_id)
232 self.assertEqual(e.status, 3)
234 def _emission_hook(self, e):
235 self.assertEqual(e.status, 1)
238 def _callback(self, e):
240 self.assertEqual(e.status, 2)
242 self.assertEqual(e.status, 1)
245 def test_callback_return_false(self):
249 def _emission_hook(obj):
252 GObject.add_emission_hook(obj, "signal", _emission_hook)
255 self.assertEqual(obj.status, 3)
257 def test_callback_return_true(self):
261 def _emission_hook(obj):
264 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
267 GObject.remove_emission_hook(obj, "signal", hook_id)
268 self.assertEqual(obj.status, 4)
270 def test_callback_return_true_but_remove(self):
274 def _emission_hook(obj):
277 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
279 GObject.remove_emission_hook(obj, "signal", hook_id)
281 self.assertEqual(obj.status, 3)
284 class TestMatching(unittest.TestCase):
285 class Object(GObject.Object):
287 prop = GObject.Property(type=int, default=0)
293 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=692918
294 def test_signal_handler_block_matching(self):
296 "Hack to work around: "
302 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
305 self.assertEqual(obj.status, 0)
306 obj.emit('my-signal')
307 self.assertEqual(obj.status, 1)
309 # Blocking by match criteria disables the foo callback
310 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
311 count = GObject.signal_handlers_block_matched(obj,
312 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
313 signal_id=signal_id, detail=detail,
314 closure=foo, func=dummy, data=dummy)
315 self.assertEqual(count, 1)
316 obj.emit('my-signal')
317 self.assertEqual(obj.status, 1)
319 # Unblocking by the same match criteria allows callback to work again
320 count = GObject.signal_handlers_unblock_matched(obj,
321 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
322 signal_id=signal_id, detail=detail,
323 closure=foo, func=dummy, data=dummy)
324 self.assertEqual(count, 1)
325 obj.emit('my-signal')
326 self.assertEqual(obj.status, 2)
328 # Disconnecting by match criteria completely removes the handler
329 count = GObject.signal_handlers_disconnect_matched(obj,
330 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
331 signal_id=signal_id, detail=detail,
332 closure=foo, func=dummy, data=dummy)
333 self.assertEqual(count, 1)
334 obj.emit('my-signal')
335 self.assertEqual(obj.status, 2)
337 def test_signal_handler_find(self):
342 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
344 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
345 found_id = GObject.signal_handler_find(obj,
346 GObject.SignalMatchType.ID,
347 signal_id=signal_id, detail=detail,
348 closure=None, func=0, data=0)
349 self.assertEqual(handler_id, found_id)
352 class TestClosures(unittest.TestCase):
355 self.emission_stopped = False
356 self.emission_error = False
357 self.handler_pending = False
359 def _callback_handler_pending(self, e):
360 signal_id, detail = GObject.signal_parse_name('signal', e, True)
361 self.handler_pending = GObject.signal_has_handler_pending(e, signal_id, detail,
362 may_be_blocked=False)
364 def _callback(self, e):
367 def _callback_stop_emission(self, obj, prop, stop_it):
369 obj.stop_emission_by_name('notify::prop')
370 self.emission_stopped = True
374 def _callback_invalid_stop_emission_name(self, obj, prop):
375 # We expect a GLib warning but there currently is no way to test that
376 # This can at least make sure we don't crash
377 old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL |
378 GLib.LogLevelFlags.LEVEL_ERROR)
380 obj.stop_emission_by_name('notasignal::baddetail')
382 GLib.log_set_always_fatal(old_mask)
383 self.emission_error = True
385 def test_disconnect_by_func(self):
387 e.connect('signal', self._callback)
388 e.disconnect_by_func(self._callback)
390 self.assertEqual(self.count, 0)
392 def test_disconnect(self):
394 handler_id = e.connect('signal', self._callback)
395 self.assertTrue(e.handler_is_connected(handler_id))
396 e.disconnect(handler_id)
398 self.assertEqual(self.count, 0)
399 self.assertFalse(e.handler_is_connected(handler_id))
401 def test_stop_emission_by_name(self):
404 # Sandwich a callback that stops emission in between a callback that increments
405 e.connect('notify::prop', self._callback_stop_emission, False)
406 e.connect('notify::prop', self._callback_stop_emission, True)
407 e.connect('notify::prop', self._callback_stop_emission, False)
409 e.set_property('prop', 1234)
410 self.assertEqual(e.get_property('prop'), 1234)
411 self.assertEqual(self.count, 1)
412 self.assertTrue(self.emission_stopped)
414 def test_stop_emission_by_name_error(self):
417 e.connect('notify::prop', self._callback_invalid_stop_emission_name)
418 e.set_property('prop', 1234)
419 self.assertTrue(self.emission_error)
421 def test_handler_block(self):
423 e.connect('signal', self._callback)
424 e.handler_block_by_func(self._callback)
426 self.assertEqual(self.count, 0)
428 def test_handler_unblock(self):
430 handler_id = e.connect('signal', self._callback)
431 e.handler_block(handler_id)
432 e.handler_unblock_by_func(self._callback)
434 self.assertEqual(self.count, 1)
436 def test_handler_block_method(self):
442 def callback(self, o):
444 o.handler_block_by_func(self.callback)
448 e.connect("signal", inst.callback)
450 self.assertEqual(inst.a, 1)
453 def test_gstring(self):
454 class C(GObject.GObject):
455 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_GSTRING,
456 (GObject.TYPE_GSTRING,))}
458 def __init__(self, test):
459 GObject.GObject.__init__(self)
462 def do_my_signal(self, data):
464 self.test.assertEqual(len(data), 3)
465 return ''.join([data[2], data[1], data[0]])
467 data = c.emit("my_signal", "\01\00\02")
468 self.assertEqual(data, "\02\00\01")
470 def test_handler_pending(self):
472 obj.connect('signal', self._callback_handler_pending)
473 obj.connect('signal', self._callback)
475 self.assertEqual(self.count, 0)
476 self.assertEqual(self.handler_pending, False)
479 self.assertEqual(self.count, 1)
480 self.assertEqual(self.handler_pending, True)
482 def test_signal_handlers_destroy(self):
484 obj.connect('signal', self._callback)
485 obj.connect('signal', self._callback)
486 obj.connect('signal', self._callback)
489 self.assertEqual(self.count, 3)
491 # count should remain at 3 after all handlers are destroyed
492 GObject.signal_handlers_destroy(obj)
494 self.assertEqual(self.count, 3)
497 class SigPropClass(GObject.GObject):
498 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None,
499 (GObject.TYPE_INT,))}
502 'foo': (str, None, None, '', GObject.PARAM_WRITABLE | GObject.PARAM_CONSTRUCT),
505 signal_emission_failed = False
507 def do_my_signal(self, arg):
510 def do_set_property(self, pspec, value):
511 if pspec.name == 'foo':
514 raise AttributeError('unknown property %s' % pspec.name)
516 self.emit("my-signal", 1)
518 self.signal_emission_failed = True
521 class TestSigProp(unittest.TestCase):
522 def test_emit_in_property_setter(self):
524 self.assertFalse(obj.signal_emission_failed)
527 class CM(GObject.GObject):
529 test1=(GObject.SignalFlags.RUN_FIRST, None, ()),
530 test2=(GObject.SignalFlags.RUN_LAST, None, (str,)),
531 test3=(GObject.SignalFlags.RUN_LAST, int, (GObject.TYPE_DOUBLE,)),
532 test4=(GObject.SignalFlags.RUN_FIRST, None,
533 (bool, _long, GObject.TYPE_FLOAT, GObject.TYPE_DOUBLE, int,
534 GObject.TYPE_UINT, GObject.TYPE_ULONG)),
535 test_float=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_FLOAT, (GObject.TYPE_FLOAT,)),
536 test_double=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_DOUBLE, (GObject.TYPE_DOUBLE,)),
537 test_int64=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_INT64, (GObject.TYPE_INT64,)),
538 test_string=(GObject.SignalFlags.RUN_LAST, str, (str,)),
539 test_object=(GObject.SignalFlags.RUN_LAST, object, (object,)),
540 test_paramspec=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, ()),
541 test_paramspec_in=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, (GObject.ParamSpec, )),
542 test_gvalue=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.Value,)),
543 test_gvalue_ret=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.TYPE_GTYPE,)),
546 testprop = GObject.Property(type=int)
549 class _TestCMarshaller:
552 testhelper.connectcallbacks(self.obj)
554 def test_test1(self):
555 self.obj.emit("test1")
557 def test_test2(self):
558 self.obj.emit("test2", "string")
560 def test_test3(self):
561 rv = self.obj.emit("test3", 42.0)
562 self.assertEqual(rv, 20)
564 def test_test4(self):
565 self.obj.emit("test4", True, _long(10), 3.14, 1.78, 20, _long(30), _long(31))
567 def test_float(self):
568 rv = self.obj.emit("test-float", 1.234)
569 self.assertTrue(rv >= 1.233999 and rv <= 1.2400001, rv)
571 def test_double(self):
572 rv = self.obj.emit("test-double", 1.234)
573 self.assertEqual(rv, 1.234)
575 def test_int64(self):
576 rv = self.obj.emit("test-int64", 102030405)
577 self.assertEqual(rv, 102030405)
579 rv = self.obj.emit("test-int64", GObject.G_MAXINT64)
580 self.assertEqual(rv, GObject.G_MAXINT64 - 1)
582 rv = self.obj.emit("test-int64", GObject.G_MININT64)
583 self.assertEqual(rv, GObject.G_MININT64)
585 def test_string(self):
586 rv = self.obj.emit("test-string", "str")
587 self.assertEqual(rv, "str")
589 def test_object(self):
590 rv = self.obj.emit("test-object", self)
591 self.assertEqual(rv, self)
593 def test_paramspec(self):
594 rv = self.obj.emit("test-paramspec")
595 self.assertEqual(rv.name, "test-param")
596 self.assertEqual(rv.nick, "test")
598 @unittest.skipUnless(hasattr(GObject, 'param_spec_boolean'),
599 'too old gobject-introspection')
600 def test_paramspec_in(self):
601 rv = GObject.param_spec_boolean('mybool', 'test-bool', 'do something',
602 True, GObject.ParamFlags.READABLE)
604 rv2 = self.obj.emit("test-paramspec-in", rv)
605 self.assertEqual(type(rv), type(rv2))
606 self.assertEqual(rv2.name, "mybool")
607 self.assertEqual(rv2.nick, "test-bool")
609 def test_C_paramspec(self):
610 self.notify_called = False
612 def cb_notify(obj, prop):
613 self.notify_called = True
614 self.assertEqual(obj, self.obj)
615 self.assertEqual(prop.name, "testprop")
617 self.obj.connect("notify", cb_notify)
618 self.obj.set_property("testprop", 42)
619 self.assertTrue(self.notify_called)
621 def test_gvalue(self):
623 rv = self.obj.emit("test-gvalue", 42)
624 self.assertEqual(rv, 42)
627 v = GObject.Value(GObject.TYPE_FLOAT, 1.234)
628 rv = self.obj.emit("test-gvalue", v)
629 self.assertAlmostEqual(rv, 1.234, 4)
632 rv = self.obj.emit("test-gvalue", 1.234)
633 self.assertAlmostEqual(rv, 1.234, 4)
636 v = GObject.Value(GObject.TYPE_INT64, GObject.G_MAXINT64)
637 rv = self.obj.emit("test-gvalue", v)
638 self.assertEqual(rv, GObject.G_MAXINT64)
641 v = GObject.Value(GObject.TYPE_UINT64, GObject.G_MAXUINT64)
642 rv = self.obj.emit("test-gvalue", v)
643 self.assertEqual(rv, GObject.G_MAXUINT64)
645 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=705291
646 def test_gvalue_implicit_int64(self):
648 rv = self.obj.emit("test-gvalue", GObject.G_MAXINT64)
649 self.assertEqual(rv, GObject.G_MAXINT64)
652 rv = self.obj.emit("test-gvalue", GObject.G_MAXUINT64)
653 self.assertEqual(rv, GObject.G_MAXUINT64)
655 def test_gvalue_ret(self):
656 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT),
658 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT),
660 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT64),
662 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT64),
664 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_STRING),
667 if 'generic-c-marshaller' in GObject.features:
668 class TestCMarshaller(_TestCMarshaller, unittest.TestCase):
672 print('** WARNING: LIBFFI disabled, not testing')
678 class TestPyGValue(unittest.TestCase):
679 def test_none_null_boxed_conversion(self):
680 class C(GObject.GObject):
681 __gsignals__ = dict(my_boxed_signal=(
682 GObject.SignalFlags.RUN_LAST,
683 GObject.TYPE_STRV, ()))
686 obj.connect('my-boxed-signal', lambda obj: None)
688 obj.emit('my-boxed-signal')
689 assert not sys.last_type
692 class TestSignalDecorator(unittest.TestCase):
693 class Decorated(GObject.GObject):
701 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
705 stomped = GObject.Signal('stomped', arg_types=(int,), doc='this will stomp')
706 unnamed = GObject.Signal()
708 class DecoratedOverride(GObject.GObject):
709 overridden_closure_called = False
710 notify_called = False
711 value = GObject.Property(type=int, default=0)
713 @GObject.SignalOverride
714 def notify(self, *args, **kargs):
715 self.overridden_closure_called = True
717 def on_notify(self, obj, prop):
718 self.notify_called = True
721 self.unnamedCalled = False
723 def onUnnamed(self, obj):
724 self.unnamedCalled = True
726 def test_get_signal_args(self):
727 self.assertEqual(self.Decorated.pushed.get_signal_args(),
728 (GObject.SignalFlags.RUN_FIRST, None, tuple(), None, None))
729 self.assertEqual(self.Decorated.pulled.get_signal_args(),
730 (GObject.SignalFlags.RUN_LAST, None, tuple(), None, None))
731 self.assertEqual(self.Decorated.stomped.get_signal_args(),
732 (GObject.SignalFlags.RUN_FIRST, None, (int,), None, None))
734 def test_closures_called(self):
735 decorated = self.Decorated()
736 self.assertEqual(decorated.value, 0)
737 decorated.pushed.emit()
738 self.assertEqual(decorated.value, 1)
739 decorated.pulled.emit()
740 self.assertEqual(decorated.value, 0)
742 def test_signal_copy(self):
743 blah = self.Decorated.stomped.copy('blah')
744 self.assertEqual(str(blah), blah)
745 self.assertEqual(blah.func, self.Decorated.stomped.func)
746 self.assertEqual(blah.flags, self.Decorated.stomped.flags)
747 self.assertEqual(blah.return_type, self.Decorated.stomped.return_type)
748 self.assertEqual(blah.arg_types, self.Decorated.stomped.arg_types)
749 self.assertEqual(blah.__doc__, self.Decorated.stomped.__doc__)
751 def test_doc_string(self):
752 # Test the two techniques for setting doc strings on the signals
753 # class variables, through the "doc" keyword or as the getter doc string.
754 self.assertEqual(self.Decorated.stomped.__doc__, 'this will stomp')
755 self.assertEqual(self.Decorated.pushed.__doc__, 'this will push')
757 def test_unnamed_signal_gets_named(self):
758 self.assertEqual(str(self.Decorated.unnamed), 'unnamed')
760 def test_unnamed_signal_gets_called(self):
761 obj = self.Decorated()
762 obj.connect('unnamed', self.onUnnamed)
763 self.assertEqual(self.unnamedCalled, False)
765 self.assertEqual(self.unnamedCalled, True)
767 def NOtest_overridden_signal(self):
768 # Test that the pushed signal is called in with super and the override
769 # which should both increment the "value" to 3
770 obj = self.DecoratedOverride()
771 obj.connect("notify", obj.on_notify)
772 self.assertEqual(obj.value, 0)
774 self.assertEqual(obj.value, 1)
775 self.assertTrue(obj.overridden_closure_called)
776 self.assertTrue(obj.notify_called)
779 class TestSignalConnectors(unittest.TestCase):
780 class CustomButton(GObject.GObject):
781 on_notify_called = False
782 value = GObject.Property(type=int)
784 @GObject.Signal(arg_types=(int,))
785 def clicked(self, value):
792 def on_clicked(self, obj, value):
796 def test_signal_notify(self):
797 def on_notify(obj, param):
798 obj.on_notify_called = True
800 obj = self.CustomButton()
801 obj.connect('notify', on_notify)
802 self.assertFalse(obj.on_notify_called)
804 self.assertTrue(obj.on_notify_called)
806 def test_signal_emit(self):
807 # standard callback connection with different forms of emit.
808 obj = self.CustomButton()
809 obj.connect('clicked', self.on_clicked)
812 obj.emit('clicked', 1)
813 self.assertEqual(obj.value, 1)
814 self.assertEqual(obj, self.obj)
815 self.assertEqual(self.value, 1)
817 # using class signal as param
820 obj.emit(self.CustomButton.clicked, 1)
821 self.assertEqual(obj, self.obj)
822 self.assertEqual(self.value, 1)
824 # using bound signal as param
827 obj.emit(obj.clicked, 1)
828 self.assertEqual(obj, self.obj)
829 self.assertEqual(self.value, 1)
831 # using bound signal with emit
835 self.assertEqual(obj, self.obj)
836 self.assertEqual(self.value, 1)
838 def test_signal_class_connect(self):
839 obj = self.CustomButton()
840 obj.connect(self.CustomButton.clicked, self.on_clicked)
841 obj.emit('clicked', 2)
842 self.assertEqual(obj, self.obj)
843 self.assertEqual(self.value, 2)
845 def test_signal_bound_connect(self):
846 obj = self.CustomButton()
847 obj.clicked.connect(self.on_clicked)
848 obj.emit('clicked', 3)
849 self.assertEqual(obj, self.obj)
850 self.assertEqual(self.value, 3)
853 class TestInstallSignals(unittest.TestCase):
854 # These tests only test how signalhelper.install_signals works
855 # with the __gsignals__ dict and therefore does not need to use
856 # GObject as a base class because that would automatically call
857 # install_signals within the meta-class.
859 __gsignals__ = {'test': (0, None, tuple())}
870 self.assertEqual(len(self.Base.__gsignals__), 1)
871 signalhelper.install_signals(self.Base)
872 self.assertEqual(len(self.Base.__gsignals__), 1)
874 def test_subclass_gets_empty_gsignals_dict(self):
875 # Installing signals will add the __gsignals__ dict to a class
876 # if it doesn't already exists.
877 self.assertFalse('__gsignals__' in self.Sub1.__dict__)
878 signalhelper.install_signals(self.Sub1)
879 self.assertTrue('__gsignals__' in self.Sub1.__dict__)
880 # Sub1 should only contain an empty signals dict, this tests:
881 # https://bugzilla.gnome.org/show_bug.cgi?id=686496
882 self.assertEqual(self.Sub1.__dict__['__gsignals__'], {})
884 def test_subclass_with_decorator_gets_gsignals_dict(self):
885 self.assertFalse('__gsignals__' in self.Sub2.__dict__)
886 signalhelper.install_signals(self.Sub2)
887 self.assertTrue('__gsignals__' in self.Sub2.__dict__)
888 self.assertEqual(len(self.Base.__gsignals__), 1)
889 self.assertEqual(len(self.Sub2.__gsignals__), 1)
890 self.assertTrue('sub2test' in self.Sub2.__gsignals__)
892 # Make sure the vfunc was added
893 self.assertTrue(hasattr(self.Sub2, 'do_sub2test'))
896 # For this test to work with both python2 and 3 we need to dynamically
897 # exec the given code due to the new syntax causing an error in python 2.
898 annotated_class_code = """
899 class AnnotatedSignalClass(GObject.GObject):
901 def sig1(self, a:int, b:float):
904 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
905 def sig2_with_return(self, a:int, b:float) -> str:
910 @unittest.skipUnless(sys.version_info >= (3, 0),
911 'Argument annotations require Python 3')
912 class TestPython3Signals(unittest.TestCase):
913 AnnotatedClass = None
916 exec(annotated_class_code, globals(), globals())
917 self.assertTrue('AnnotatedSignalClass' in globals())
918 self.AnnotatedClass = globals()['AnnotatedSignalClass']
920 def test_annotations(self):
921 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig1.func),
922 (None, (int, float)))
923 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig2_with_return.func),
926 self.assertEqual(self.AnnotatedClass.sig2_with_return.get_signal_args(),
927 (GObject.SignalFlags.RUN_LAST, str, (int, float), None, None))
928 self.assertEqual(self.AnnotatedClass.sig2_with_return.arg_types,
930 self.assertEqual(self.AnnotatedClass.sig2_with_return.return_type,
933 def test_emit_return(self):
934 obj = self.AnnotatedClass()
935 self.assertEqual(obj.sig2_with_return.emit(1, 2.0),
939 class TestSignalModuleLevelFunctions(unittest.TestCase):
940 def test_signal_list_ids_with_invalid_type(self):
941 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
942 GObject.signal_list_ids(GObject.TYPE_INVALID)
944 def test_signal_list_ids(self):
945 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
946 GObject.signal_list_ids(GObject.TYPE_INT)
948 ids = GObject.signal_list_ids(C)
949 self.assertEqual(len(ids), 1)
950 # Note canonicalized names
951 self.assertEqual(GObject.signal_name(ids[0]), 'my-signal')
952 # There is no signal 0 in gobject
953 self.assertEqual(GObject.signal_name(0), None)
955 def test_signal_lookup_with_invalid_type(self):
956 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
957 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INVALID)
959 def test_signal_lookup(self):
960 ids = GObject.signal_list_ids(C)
961 self.assertEqual(ids[0], GObject.signal_lookup('my_signal', C))
962 self.assertEqual(ids[0], GObject.signal_lookup('my-signal', C))
964 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
965 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INT)
967 # Invalid signal names return 0 instead of raising
968 self.assertEqual(GObject.signal_lookup('NOT_A_SIGNAL_NAME', C),
971 def test_signal_query(self):
972 my_signal_id, = GObject.signal_list_ids(C)
974 # Form is: (id, name, gtype, arg_count, return_type, (arg_type1, ...))
975 my_signal_expected_query_result = [my_signal_id, 'my-signal', C.__gtype__,
976 1, GObject.TYPE_NONE, (GObject.TYPE_INT,)]
977 # signal_query(name, type)
978 self.assertEqual(list(GObject.signal_query('my-signal', C)), my_signal_expected_query_result)
979 # signal_query(signal_id)
980 self.assertEqual(list(GObject.signal_query(my_signal_id)), my_signal_expected_query_result)
981 # invalid query returns None instead of raising
982 self.assertEqual(GObject.signal_query(0), None)
983 self.assertEqual(GObject.signal_query('NOT_A_SIGNAL', C),
987 @unittest.skipUnless(has_cairo, 'built without cairo support')
988 class TestIntrospectedSignals(unittest.TestCase):
989 def test_object_param_signal(self):
990 obj = Regress.TestObj()
992 def callback(obj, obj_param):
993 self.assertEqual(obj_param.props.int, 3)
994 self.assertGreater(obj_param.__grefcount__, 1)
998 obj.connect('sig-with-obj', callback)
999 obj.emit_sig_with_obj()
1000 self.assertTrue(obj.called)
1002 def test_connect_after(self):
1003 obj = Regress.TestObj()
1005 def callback(obj, obj_param):
1009 obj.connect_after('sig-with-obj', callback)
1010 obj.emit_sig_with_obj()
1011 self.assertTrue(obj.called)
1013 def test_connect_object(self):
1014 obj = Regress.TestObj()
1016 def callback(obj, obj_param):
1020 obj.connect_object('sig-with-obj', callback, obj)
1021 obj.emit_sig_with_obj()
1022 self.assertTrue(obj.called)
1024 def test_connect_object_after(self):
1025 obj = Regress.TestObj()
1027 def callback(obj, obj_param):
1031 obj.connect_object_after('sig-with-obj', callback, obj)
1032 obj.emit_sig_with_obj()
1033 self.assertTrue(obj.called)
1035 def test_int64_param_from_py(self):
1036 obj = Regress.TestObj()
1038 def callback(obj, i):
1042 obj.callback_i = None
1043 obj.connect('sig-with-int64-prop', callback)
1044 rv = obj.emit('sig-with-int64-prop', GObject.G_MAXINT64)
1045 self.assertEqual(rv, GObject.G_MAXINT64)
1046 self.assertEqual(obj.callback_i, GObject.G_MAXINT64)
1048 def test_uint64_param_from_py(self):
1049 obj = Regress.TestObj()
1051 def callback(obj, i):
1055 obj.callback_i = None
1056 obj.connect('sig-with-uint64-prop', callback)
1057 rv = obj.emit('sig-with-uint64-prop', GObject.G_MAXUINT64)
1058 self.assertEqual(rv, GObject.G_MAXUINT64)
1059 self.assertEqual(obj.callback_i, GObject.G_MAXUINT64)
1061 def test_int64_param_from_c(self):
1062 obj = Regress.TestObj()
1064 def callback(obj, i):
1068 obj.callback_i = None
1070 obj.connect('sig-with-int64-prop', callback)
1071 obj.emit_sig_with_int64()
1072 self.assertEqual(obj.callback_i, GObject.G_MAXINT64)
1074 def test_uint64_param_from_c(self):
1075 obj = Regress.TestObj()
1077 def callback(obj, i):
1081 obj.callback_i = None
1083 obj.connect('sig-with-uint64-prop', callback)
1084 obj.emit_sig_with_uint64()
1085 self.assertEqual(obj.callback_i, GObject.G_MAXUINT64)
1087 def test_intarray_ret(self):
1088 obj = Regress.TestObj()
1090 def callback(obj, i):
1094 obj.callback_i = None
1097 obj.connect('sig-with-intarray-ret', callback)
1098 except TypeError as e:
1099 # compat with g-i 1.34.x
1100 if 'unknown signal' in str(e):
1104 rv = obj.emit('sig-with-intarray-ret', 42)
1105 self.assertEqual(obj.callback_i, 42)
1106 self.assertEqual(type(rv), GLib.Array)
1107 self.assertEqual(rv.len, 2)
1110 if __name__ == '__main__':