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 _ConnectDataTestBase(object):
855 # - self.Object is overridden in sub-classes.
856 # - Numeric suffixes indicate the number of user data args passed in.
859 def run_connect_test(self, emit_args, user_data, flags=0):
864 callback_args.append(args)
867 obj.connect_data('sig-with-int64-prop', callback, connect_flags=flags, *user_data)
868 obj.emit('sig-with-int64-prop', *emit_args)
869 self.assertEqual(len(callback_args), 1)
870 return callback_args[0]
873 obj, value = self.run_connect_test([GObject.G_MAXINT64], user_data=[])
874 self.assertIsInstance(obj, self.Object)
875 self.assertEqual(value, GObject.G_MAXINT64)
878 obj, value, data = self.run_connect_test([GObject.G_MAXINT64],
879 user_data=['mydata'])
880 self.assertIsInstance(obj, self.Object)
881 self.assertEqual(value, GObject.G_MAXINT64)
882 self.assertEqual(data, 'mydata')
884 def test_after_0(self):
885 obj, value = self.run_connect_test([GObject.G_MAXINT64],
887 flags=GObject.ConnectFlags.AFTER)
888 self.assertIsInstance(obj, self.Object)
889 self.assertEqual(value, GObject.G_MAXINT64)
891 def test_after_1(self):
892 obj, value, data = self.run_connect_test([GObject.G_MAXINT64],
893 user_data=['mydata'],
894 flags=GObject.ConnectFlags.AFTER)
895 self.assertIsInstance(obj, self.Object)
896 self.assertEqual(value, GObject.G_MAXINT64)
897 self.assertEqual(data, 'mydata')
899 def test_swaped_0(self):
900 # Swapped only works with a single user data argument.
901 with self.assertRaises(ValueError):
902 self.run_connect_test([GObject.G_MAXINT64],
904 flags=GObject.ConnectFlags.SWAPPED)
906 def test_swaped_1(self):
907 # Notice obj and data are reversed in the return.
908 data, value, obj = self.run_connect_test([GObject.G_MAXINT64],
909 user_data=['mydata'],
910 flags=GObject.ConnectFlags.SWAPPED)
911 self.assertIsInstance(obj, self.Object)
912 self.assertEqual(value, GObject.G_MAXINT64)
913 self.assertEqual(data, 'mydata')
915 def test_swaped_2(self):
916 # Swapped only works with a single user data argument.
917 with self.assertRaises(ValueError):
918 self.run_connect_test([GObject.G_MAXINT64],
920 flags=GObject.ConnectFlags.SWAPPED)
922 def test_after_and_swapped_0(self):
923 # Swapped only works with a single user data argument.
924 with self.assertRaises(ValueError):
925 self.run_connect_test([GObject.G_MAXINT64],
927 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
929 def test_after_and_swapped_1(self):
930 # Notice obj and data are reversed in the return.
931 data, value, obj = self.run_connect_test([GObject.G_MAXINT64],
932 user_data=['mydata'],
933 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
934 self.assertIsInstance(obj, self.Object)
935 self.assertEqual(value, GObject.G_MAXINT64)
936 self.assertEqual(data, 'mydata')
938 def test_after_and_swapped_2(self):
939 # Swapped only works with a single user data argument.
940 with self.assertRaises(ValueError):
941 self.run_connect_test([GObject.G_MAXINT64],
943 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
946 class TestConnectDataNonIntrospected(unittest.TestCase, _ConnectDataTestBase):
947 # This tests connect_data with non-introspected signals
948 # (created in Python in this case).
949 class Object(GObject.Object):
950 test = GObject.Signal()
951 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
952 arg_types=[GObject.TYPE_INT64],
953 flags=GObject.SignalFlags.RUN_LAST)
956 @unittest.skipUnless(has_cairo, 'built without cairo support')
957 class TestConnectDataIntrospected(unittest.TestCase, _ConnectDataTestBase):
958 # This tests connect_data with introspected signals brought in from Regress.
959 Object = Regress.TestObj
962 class TestInstallSignals(unittest.TestCase):
963 # These tests only test how signalhelper.install_signals works
964 # with the __gsignals__ dict and therefore does not need to use
965 # GObject as a base class because that would automatically call
966 # install_signals within the meta-class.
968 __gsignals__ = {'test': (0, None, tuple())}
979 self.assertEqual(len(self.Base.__gsignals__), 1)
980 signalhelper.install_signals(self.Base)
981 self.assertEqual(len(self.Base.__gsignals__), 1)
983 def test_subclass_gets_empty_gsignals_dict(self):
984 # Installing signals will add the __gsignals__ dict to a class
985 # if it doesn't already exists.
986 self.assertFalse('__gsignals__' in self.Sub1.__dict__)
987 signalhelper.install_signals(self.Sub1)
988 self.assertTrue('__gsignals__' in self.Sub1.__dict__)
989 # Sub1 should only contain an empty signals dict, this tests:
990 # https://bugzilla.gnome.org/show_bug.cgi?id=686496
991 self.assertEqual(self.Sub1.__dict__['__gsignals__'], {})
993 def test_subclass_with_decorator_gets_gsignals_dict(self):
994 self.assertFalse('__gsignals__' in self.Sub2.__dict__)
995 signalhelper.install_signals(self.Sub2)
996 self.assertTrue('__gsignals__' in self.Sub2.__dict__)
997 self.assertEqual(len(self.Base.__gsignals__), 1)
998 self.assertEqual(len(self.Sub2.__gsignals__), 1)
999 self.assertTrue('sub2test' in self.Sub2.__gsignals__)
1001 # Make sure the vfunc was added
1002 self.assertTrue(hasattr(self.Sub2, 'do_sub2test'))
1005 # For this test to work with both python2 and 3 we need to dynamically
1006 # exec the given code due to the new syntax causing an error in python 2.
1007 annotated_class_code = """
1008 class AnnotatedSignalClass(GObject.GObject):
1010 def sig1(self, a:int, b:float):
1013 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
1014 def sig2_with_return(self, a:int, b:float) -> str:
1019 @unittest.skipUnless(sys.version_info >= (3, 0),
1020 'Argument annotations require Python 3')
1021 class TestPython3Signals(unittest.TestCase):
1022 AnnotatedClass = None
1025 exec(annotated_class_code, globals(), globals())
1026 self.assertTrue('AnnotatedSignalClass' in globals())
1027 self.AnnotatedClass = globals()['AnnotatedSignalClass']
1029 def test_annotations(self):
1030 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig1.func),
1031 (None, (int, float)))
1032 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig2_with_return.func),
1033 (str, (int, float)))
1035 self.assertEqual(self.AnnotatedClass.sig2_with_return.get_signal_args(),
1036 (GObject.SignalFlags.RUN_LAST, str, (int, float), None, None))
1037 self.assertEqual(self.AnnotatedClass.sig2_with_return.arg_types,
1039 self.assertEqual(self.AnnotatedClass.sig2_with_return.return_type,
1042 def test_emit_return(self):
1043 obj = self.AnnotatedClass()
1044 self.assertEqual(obj.sig2_with_return.emit(1, 2.0),
1048 class TestSignalModuleLevelFunctions(unittest.TestCase):
1049 def test_signal_list_ids_with_invalid_type(self):
1050 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1051 GObject.signal_list_ids(GObject.TYPE_INVALID)
1053 def test_signal_list_ids(self):
1054 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1055 GObject.signal_list_ids(GObject.TYPE_INT)
1057 ids = GObject.signal_list_ids(C)
1058 self.assertEqual(len(ids), 1)
1059 # Note canonicalized names
1060 self.assertEqual(GObject.signal_name(ids[0]), 'my-signal')
1061 # There is no signal 0 in gobject
1062 self.assertEqual(GObject.signal_name(0), None)
1064 def test_signal_lookup_with_invalid_type(self):
1065 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1066 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INVALID)
1068 def test_signal_lookup(self):
1069 ids = GObject.signal_list_ids(C)
1070 self.assertEqual(ids[0], GObject.signal_lookup('my_signal', C))
1071 self.assertEqual(ids[0], GObject.signal_lookup('my-signal', C))
1073 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1074 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INT)
1076 # Invalid signal names return 0 instead of raising
1077 self.assertEqual(GObject.signal_lookup('NOT_A_SIGNAL_NAME', C),
1080 def test_signal_query(self):
1081 my_signal_id, = GObject.signal_list_ids(C)
1083 # Form is: (id, name, gtype, arg_count, return_type, (arg_type1, ...))
1084 my_signal_expected_query_result = [my_signal_id, 'my-signal', C.__gtype__,
1085 1, GObject.TYPE_NONE, (GObject.TYPE_INT,)]
1086 # signal_query(name, type)
1087 self.assertEqual(list(GObject.signal_query('my-signal', C)), my_signal_expected_query_result)
1088 # signal_query(signal_id)
1089 self.assertEqual(list(GObject.signal_query(my_signal_id)), my_signal_expected_query_result)
1090 # invalid query returns None instead of raising
1091 self.assertEqual(GObject.signal_query(0), None)
1092 self.assertEqual(GObject.signal_query('NOT_A_SIGNAL', C),
1096 @unittest.skipUnless(has_cairo, 'built without cairo support')
1097 class TestIntrospectedSignals(unittest.TestCase):
1098 def test_object_param_signal(self):
1099 obj = Regress.TestObj()
1101 def callback(obj, obj_param):
1102 self.assertEqual(obj_param.props.int, 3)
1103 self.assertGreater(obj_param.__grefcount__, 1)
1107 obj.connect('sig-with-obj', callback)
1108 obj.emit_sig_with_obj()
1109 self.assertTrue(obj.called)
1111 def test_connect_after(self):
1112 obj = Regress.TestObj()
1114 def callback(obj, obj_param):
1118 obj.connect_after('sig-with-obj', callback)
1119 obj.emit_sig_with_obj()
1120 self.assertTrue(obj.called)
1122 def test_int64_param_from_py(self):
1123 obj = Regress.TestObj()
1125 def callback(obj, i):
1129 obj.callback_i = None
1130 obj.connect('sig-with-int64-prop', callback)
1131 rv = obj.emit('sig-with-int64-prop', GObject.G_MAXINT64)
1132 self.assertEqual(rv, GObject.G_MAXINT64)
1133 self.assertEqual(obj.callback_i, GObject.G_MAXINT64)
1135 def test_uint64_param_from_py(self):
1136 obj = Regress.TestObj()
1138 def callback(obj, i):
1142 obj.callback_i = None
1143 obj.connect('sig-with-uint64-prop', callback)
1144 rv = obj.emit('sig-with-uint64-prop', GObject.G_MAXUINT64)
1145 self.assertEqual(rv, GObject.G_MAXUINT64)
1146 self.assertEqual(obj.callback_i, GObject.G_MAXUINT64)
1148 def test_int64_param_from_c(self):
1149 obj = Regress.TestObj()
1151 def callback(obj, i):
1155 obj.callback_i = None
1157 obj.connect('sig-with-int64-prop', callback)
1158 obj.emit_sig_with_int64()
1159 self.assertEqual(obj.callback_i, GObject.G_MAXINT64)
1161 def test_uint64_param_from_c(self):
1162 obj = Regress.TestObj()
1164 def callback(obj, i):
1168 obj.callback_i = None
1170 obj.connect('sig-with-uint64-prop', callback)
1171 obj.emit_sig_with_uint64()
1172 self.assertEqual(obj.callback_i, GObject.G_MAXUINT64)
1174 def test_intarray_ret(self):
1175 obj = Regress.TestObj()
1177 def callback(obj, i):
1181 obj.callback_i = None
1184 obj.connect('sig-with-intarray-ret', callback)
1185 except TypeError as e:
1186 # compat with g-i 1.34.x
1187 if 'unknown signal' in str(e):
1191 rv = obj.emit('sig-with-intarray-ret', 42)
1192 self.assertEqual(obj.callback_i, 42)
1193 self.assertEqual(type(rv), GLib.Array)
1194 self.assertEqual(rv.len, 2)
1196 @unittest.skip('https://bugzilla.gnome.org/show_bug.cgi?id=669496')
1197 def test_array_parm(self):
1198 obj = Regress.TestObj()
1200 def callback(obj, arr):
1201 obj.callback_arr = arr
1203 obj.connect('sig-with-array-prop', callback)
1204 obj.callback_arr = None
1205 self.assertEqual(obj.emit('sig-with-array-prop', [1, 2, GObject.G_MAXUINT]), None)
1206 self.assertEqual(obj.callback_arr, [1, 2, GObject.G_MAXUINT])
1209 class _ConnectObjectTestBase(object):
1211 # - self.Object is overridden in sub-classes.
1212 # - Numeric suffixes indicate the number of user data args passed in.
1216 def run_connect_test(self, emit_args, user_data, flags=0):
1219 swap_obj = self.SwapObject()
1221 def callback(*args):
1222 callback_args.append(args)
1225 if flags & GObject.ConnectFlags.AFTER:
1226 connect_func = obj.connect_object_after
1228 connect_func = obj.connect_object
1230 connect_func('sig-with-int64-prop', callback, swap_obj, *user_data)
1231 obj.emit('sig-with-int64-prop', *emit_args)
1232 self.assertEqual(len(callback_args), 1)
1233 return callback_args[0]
1236 obj, value = self.run_connect_test([GObject.G_MAXINT64], user_data=[])
1237 self.assertIsInstance(obj, self.SwapObject)
1238 self.assertEqual(value, GObject.G_MAXINT64)
1241 obj, value, data = self.run_connect_test([GObject.G_MAXINT64],
1242 user_data=['mydata'])
1243 self.assertIsInstance(obj, self.SwapObject)
1244 self.assertEqual(value, GObject.G_MAXINT64)
1245 self.assertEqual(data, 'mydata')
1248 obj, value, data1, data2 = self.run_connect_test([GObject.G_MAXINT64],
1249 user_data=['mydata1', 'mydata2'])
1250 self.assertIsInstance(obj, self.SwapObject)
1251 self.assertEqual(value, GObject.G_MAXINT64)
1252 self.assertEqual(data1, 'mydata1')
1253 self.assertEqual(data2, 'mydata2')
1255 def test_after_0(self):
1256 obj, value = self.run_connect_test([GObject.G_MAXINT64],
1258 flags=GObject.ConnectFlags.AFTER)
1259 self.assertIsInstance(obj, self.SwapObject)
1260 self.assertEqual(value, GObject.G_MAXINT64)
1262 def test_after_1(self):
1263 obj, value, data = self.run_connect_test([GObject.G_MAXINT64],
1264 user_data=['mydata'],
1265 flags=GObject.ConnectFlags.AFTER)
1266 self.assertIsInstance(obj, self.SwapObject)
1267 self.assertEqual(value, GObject.G_MAXINT64)
1268 self.assertEqual(data, 'mydata')
1270 def test_after_2(self):
1271 obj, value, data1, data2 = self.run_connect_test([GObject.G_MAXINT64],
1272 user_data=['mydata1', 'mydata2'],
1273 flags=GObject.ConnectFlags.AFTER)
1274 self.assertIsInstance(obj, self.SwapObject)
1275 self.assertEqual(value, GObject.G_MAXINT64)
1276 self.assertEqual(data1, 'mydata1')
1277 self.assertEqual(data2, 'mydata2')
1280 class TestConnectGObjectNonIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1281 # This tests connect_object with non-introspected signals
1282 # (created in Python in this case).
1283 class Object(GObject.Object):
1284 test = GObject.Signal()
1285 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1286 arg_types=[GObject.TYPE_INT64],
1287 flags=GObject.SignalFlags.RUN_LAST)
1289 # Object passed for swapping is GObject based.
1290 class SwapObject(GObject.Object):
1294 @unittest.skipUnless(has_cairo, 'built without cairo support')
1295 class TestConnectGObjectIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1296 # This tests connect_object with introspected signals brought in from Regress.
1297 Object = Regress.TestObj
1299 # Object passed for swapping is GObject based.
1300 class SwapObject(GObject.Object):
1304 class TestConnectPyObjectNonIntrospected(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 pure Python
1317 @unittest.skipUnless(has_cairo, 'built without cairo support')
1318 class TestConnectPyObjectIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1319 # This tests connect_object with introspected signals brought in from Regress.
1320 Object = Regress.TestObj
1322 # Object passed for swapping is pure Python
1326 if __name__ == '__main__':