8 from gi.repository import GObject, GLib
9 from gi import _signalhelper as signalhelper
11 from compathelper import _long
12 from helper import capture_glib_warnings, capture_gi_deprecation_warnings
17 from gi.repository import Regress
23 class C(GObject.GObject):
24 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None,
27 def do_my_signal(self, arg):
32 def do_my_signal(self, arg2):
34 C.do_my_signal(self, arg2)
37 class TestSignalCreation(unittest.TestCase):
39 def test_illegals(self):
40 self.assertRaises(TypeError, lambda: GObject.signal_new('test',
44 (GObject.TYPE_LONG,)))
47 class TestChaining(unittest.TestCase):
50 self.inst.connect("my_signal", self.my_signal_handler_cb, 1, 2, 3)
52 def my_signal_handler_cb(self, *args):
54 assert isinstance(args[0], C)
55 assert args[0] == self.inst
57 assert isinstance(args[1], int)
60 assert args[2:] == (1, 2, 3)
62 def test_chaining(self):
63 self.inst.emit("my_signal", 42)
64 assert self.inst.arg == 42
66 def test_chaining2(self):
68 inst2.emit("my_signal", 44)
69 assert inst2.arg == 44
70 assert inst2.arg2 == 44
72 # This is for bug 153718
75 class TestGSignalsError(unittest.TestCase):
76 def test_invalid_type(self, *args):
78 class Foo(GObject.GObject):
80 self.assertRaises(TypeError, foo)
83 def test_invalid_name(self, *args):
85 class Foo(GObject.GObject):
86 __gsignals__ = {'not-exists': 'override'}
88 with capture_glib_warnings(allow_warnings=True):
89 self.assertRaises(TypeError, foo)
93 class TestGPropertyError(unittest.TestCase):
94 def test_invalid_type(self, *args):
96 class Foo(GObject.GObject):
97 __gproperties__ = None
98 self.assertRaises(TypeError, foo)
101 def test_invalid_name(self, *args):
103 class Foo(GObject.GObject):
104 __gproperties__ = {None: None}
106 self.assertRaises(TypeError, foo)
110 class TestList(unittest.TestCase):
111 def test_list_names(self):
112 self.assertEqual(GObject.signal_list_names(C), ('my-signal',))
115 def my_accumulator(ihint, return_accu, handler_return, user_data):
116 """An accumulator that stops emission when the sum of handler
117 returned values reaches 3"""
118 assert user_data == "accum data"
120 return False, return_accu
121 return True, return_accu + handler_return
124 class Foo(GObject.GObject):
125 my_acc_signal = GObject.Signal(return_type=GObject.TYPE_INT,
126 flags=GObject.SignalFlags.RUN_LAST,
127 accumulator=my_accumulator,
128 accu_data="accum data")
130 my_other_acc_signal = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
131 flags=GObject.SignalFlags.RUN_LAST,
132 accumulator=GObject.signal_accumulator_true_handled)
134 my_acc_first_wins = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
135 flags=GObject.SignalFlags.RUN_LAST,
136 accumulator=GObject.signal_accumulator_first_wins)
139 class TestAccumulator(unittest.TestCase):
141 def test_accumulator(self):
143 inst.my_acc_signal.connect(lambda obj: 1)
144 inst.my_acc_signal.connect(lambda obj: 2)
145 # the value returned in the following handler will not be
146 # considered, because at this point the accumulator already
148 inst.my_acc_signal.connect(lambda obj: 3)
149 retval = inst.my_acc_signal.emit()
150 self.assertEqual(retval, 3)
152 def test_accumulator_true_handled(self):
154 inst.my_other_acc_signal.connect(self._true_handler1)
155 inst.my_other_acc_signal.connect(self._true_handler2)
156 # the following handler will not be called because handler2
157 # returns True, so it should stop the emission.
158 inst.my_other_acc_signal.connect(self._true_handler3)
159 self.__true_val = None
160 inst.my_other_acc_signal.emit()
161 self.assertEqual(self.__true_val, 2)
163 def test_accumulator_first_wins(self):
164 # First signal hit will always win
166 inst.my_acc_first_wins.connect(self._true_handler3)
167 inst.my_acc_first_wins.connect(self._true_handler1)
168 inst.my_acc_first_wins.connect(self._true_handler2)
169 self.__true_val = None
170 inst.my_acc_first_wins.emit()
171 self.assertEqual(self.__true_val, 3)
173 def _true_handler1(self, obj):
177 def _true_handler2(self, obj):
181 def _true_handler3(self, obj):
186 class E(GObject.GObject):
187 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
190 # Property used to test detailed signal
191 prop = GObject.Property(type=int, default=0)
194 GObject.GObject.__init__(self)
198 assert self.status == 0
202 class F(GObject.GObject):
203 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
207 GObject.GObject.__init__(self)
214 class TestEmissionHook(unittest.TestCase):
218 e.connect('signal', self._callback)
219 GObject.add_emission_hook(E, "signal", self._emission_hook)
221 self.assertEqual(e.status, 3)
223 def test_remove(self):
226 e.connect('signal', self._callback)
227 hook_id = GObject.add_emission_hook(E, "signal", self._emission_hook)
228 GObject.remove_emission_hook(E, "signal", hook_id)
230 self.assertEqual(e.status, 3)
232 def _emission_hook(self, e):
233 self.assertEqual(e.status, 1)
236 def _callback(self, e):
238 self.assertEqual(e.status, 2)
240 self.assertEqual(e.status, 1)
243 def test_callback_return_false(self):
247 def _emission_hook(obj):
250 GObject.add_emission_hook(obj, "signal", _emission_hook)
253 self.assertEqual(obj.status, 3)
255 def test_callback_return_true(self):
259 def _emission_hook(obj):
262 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
265 GObject.remove_emission_hook(obj, "signal", hook_id)
266 self.assertEqual(obj.status, 4)
268 def test_callback_return_true_but_remove(self):
272 def _emission_hook(obj):
275 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
277 GObject.remove_emission_hook(obj, "signal", hook_id)
279 self.assertEqual(obj.status, 3)
282 class TestMatching(unittest.TestCase):
283 class Object(GObject.Object):
285 prop = GObject.Property(type=int, default=0)
291 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=692918
292 def test_signal_handler_block_matching(self):
294 "Hack to work around: "
300 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
303 self.assertEqual(obj.status, 0)
304 obj.emit('my-signal')
305 self.assertEqual(obj.status, 1)
307 # Blocking by match criteria disables the foo callback
308 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
309 count = GObject.signal_handlers_block_matched(obj,
310 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
311 signal_id=signal_id, detail=detail,
312 closure=foo, func=dummy, data=dummy)
313 self.assertEqual(count, 1)
314 obj.emit('my-signal')
315 self.assertEqual(obj.status, 1)
317 # Unblocking by the same match criteria allows callback to work again
318 count = GObject.signal_handlers_unblock_matched(obj,
319 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
320 signal_id=signal_id, detail=detail,
321 closure=foo, func=dummy, data=dummy)
322 self.assertEqual(count, 1)
323 obj.emit('my-signal')
324 self.assertEqual(obj.status, 2)
326 # Disconnecting by match criteria completely removes the handler
327 count = GObject.signal_handlers_disconnect_matched(obj,
328 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
329 signal_id=signal_id, detail=detail,
330 closure=foo, func=dummy, data=dummy)
331 self.assertEqual(count, 1)
332 obj.emit('my-signal')
333 self.assertEqual(obj.status, 2)
335 def test_signal_handler_find(self):
340 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
342 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
343 found_id = GObject.signal_handler_find(obj,
344 GObject.SignalMatchType.ID,
345 signal_id=signal_id, detail=detail,
346 closure=None, func=0, data=0)
347 self.assertEqual(handler_id, found_id)
350 class TestClosures(unittest.TestCase):
353 self.emission_stopped = False
354 self.emission_error = False
355 self.handler_pending = False
357 def _callback_handler_pending(self, e):
358 signal_id, detail = GObject.signal_parse_name('signal', e, True)
359 self.handler_pending = GObject.signal_has_handler_pending(e, signal_id, detail,
360 may_be_blocked=False)
362 def _callback(self, e):
365 def _callback_stop_emission(self, obj, prop, stop_it):
367 obj.stop_emission_by_name('notify::prop')
368 self.emission_stopped = True
372 def _callback_invalid_stop_emission_name(self, obj, prop):
373 with capture_glib_warnings(allow_warnings=True) as warn:
374 obj.stop_emission_by_name('notasignal::baddetail')
375 self.emission_error = True
376 self.assertTrue(warn)
378 def test_disconnect_by_func(self):
380 e.connect('signal', self._callback)
381 e.disconnect_by_func(self._callback)
383 self.assertEqual(self.count, 0)
385 def test_disconnect(self):
387 handler_id = e.connect('signal', self._callback)
388 self.assertTrue(e.handler_is_connected(handler_id))
389 e.disconnect(handler_id)
391 self.assertEqual(self.count, 0)
392 self.assertFalse(e.handler_is_connected(handler_id))
394 def test_stop_emission_by_name(self):
397 # Sandwich a callback that stops emission in between a callback that increments
398 e.connect('notify::prop', self._callback_stop_emission, False)
399 e.connect('notify::prop', self._callback_stop_emission, True)
400 e.connect('notify::prop', self._callback_stop_emission, False)
402 e.set_property('prop', 1234)
403 self.assertEqual(e.get_property('prop'), 1234)
404 self.assertEqual(self.count, 1)
405 self.assertTrue(self.emission_stopped)
407 def test_stop_emission_by_name_error(self):
410 e.connect('notify::prop', self._callback_invalid_stop_emission_name)
411 with capture_glib_warnings():
412 e.set_property('prop', 1234)
413 self.assertTrue(self.emission_error)
415 def test_handler_block(self):
417 e.connect('signal', self._callback)
418 e.handler_block_by_func(self._callback)
420 self.assertEqual(self.count, 0)
422 def test_handler_unblock(self):
424 handler_id = e.connect('signal', self._callback)
425 e.handler_block(handler_id)
426 e.handler_unblock_by_func(self._callback)
428 self.assertEqual(self.count, 1)
430 def test_handler_block_method(self):
436 def callback(self, o):
438 o.handler_block_by_func(self.callback)
442 e.connect("signal", inst.callback)
444 self.assertEqual(inst.a, 1)
447 def test_gstring(self):
448 class C(GObject.GObject):
449 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_GSTRING,
450 (GObject.TYPE_GSTRING,))}
452 def __init__(self, test):
453 GObject.GObject.__init__(self)
456 def do_my_signal(self, data):
458 self.test.assertEqual(len(data), 3)
459 return ''.join([data[2], data[1], data[0]])
461 data = c.emit("my_signal", "\01\00\02")
462 self.assertEqual(data, "\02\00\01")
464 def test_handler_pending(self):
466 obj.connect('signal', self._callback_handler_pending)
467 obj.connect('signal', self._callback)
469 self.assertEqual(self.count, 0)
470 self.assertEqual(self.handler_pending, False)
473 self.assertEqual(self.count, 1)
474 self.assertEqual(self.handler_pending, True)
476 def test_signal_handlers_destroy(self):
478 obj.connect('signal', self._callback)
479 obj.connect('signal', self._callback)
480 obj.connect('signal', self._callback)
483 self.assertEqual(self.count, 3)
485 # count should remain at 3 after all handlers are destroyed
486 GObject.signal_handlers_destroy(obj)
488 self.assertEqual(self.count, 3)
491 class SigPropClass(GObject.GObject):
492 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None,
493 (GObject.TYPE_INT,))}
496 'foo': (str, None, None, '',
497 GObject.ParamFlags.WRITABLE | GObject.ParamFlags.CONSTRUCT),
500 signal_emission_failed = False
502 def do_my_signal(self, arg):
505 def do_set_property(self, pspec, value):
506 if pspec.name == 'foo':
509 raise AttributeError('unknown property %s' % pspec.name)
511 self.emit("my-signal", 1)
513 self.signal_emission_failed = True
516 class TestSigProp(unittest.TestCase):
517 def test_emit_in_property_setter(self):
519 self.assertFalse(obj.signal_emission_failed)
522 class CM(GObject.GObject):
524 test1=(GObject.SignalFlags.RUN_FIRST, None, ()),
525 test2=(GObject.SignalFlags.RUN_LAST, None, (str,)),
526 test3=(GObject.SignalFlags.RUN_LAST, int, (GObject.TYPE_DOUBLE,)),
527 test4=(GObject.SignalFlags.RUN_FIRST, None,
528 (bool, _long, GObject.TYPE_FLOAT, GObject.TYPE_DOUBLE, int,
529 GObject.TYPE_UINT, GObject.TYPE_ULONG)),
530 test_float=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_FLOAT, (GObject.TYPE_FLOAT,)),
531 test_double=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_DOUBLE, (GObject.TYPE_DOUBLE,)),
532 test_int64=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_INT64, (GObject.TYPE_INT64,)),
533 test_string=(GObject.SignalFlags.RUN_LAST, str, (str,)),
534 test_object=(GObject.SignalFlags.RUN_LAST, object, (object,)),
535 test_paramspec=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, ()),
536 test_paramspec_in=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, (GObject.ParamSpec, )),
537 test_gvalue=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.Value,)),
538 test_gvalue_ret=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.TYPE_GTYPE,)),
541 testprop = GObject.Property(type=int)
544 class _TestCMarshaller:
547 testhelper.connectcallbacks(self.obj)
549 def test_test1(self):
550 self.obj.emit("test1")
552 def test_test2(self):
553 self.obj.emit("test2", "string")
555 def test_test3(self):
556 rv = self.obj.emit("test3", 42.0)
557 self.assertEqual(rv, 20)
559 def test_test4(self):
560 self.obj.emit("test4", True, _long(10), 3.14, 1.78, 20, _long(30), _long(31))
562 def test_float(self):
563 rv = self.obj.emit("test-float", 1.234)
564 self.assertTrue(rv >= 1.233999 and rv <= 1.2400001, rv)
566 def test_double(self):
567 rv = self.obj.emit("test-double", 1.234)
568 self.assertEqual(rv, 1.234)
570 def test_int64(self):
571 rv = self.obj.emit("test-int64", 102030405)
572 self.assertEqual(rv, 102030405)
574 rv = self.obj.emit("test-int64", GLib.MAXINT64)
575 self.assertEqual(rv, GLib.MAXINT64 - 1)
577 rv = self.obj.emit("test-int64", GLib.MININT64)
578 self.assertEqual(rv, GLib.MININT64)
580 def test_string(self):
581 rv = self.obj.emit("test-string", "str")
582 self.assertEqual(rv, "str")
584 def test_object(self):
585 rv = self.obj.emit("test-object", self)
586 self.assertEqual(rv, self)
588 def test_paramspec(self):
589 rv = self.obj.emit("test-paramspec")
590 self.assertEqual(rv.name, "test-param")
591 self.assertEqual(rv.nick, "test")
593 @unittest.skipUnless(hasattr(GObject, 'param_spec_boolean'),
594 'too old gobject-introspection')
595 def test_paramspec_in(self):
596 rv = GObject.param_spec_boolean('mybool', 'test-bool', 'do something',
597 True, GObject.ParamFlags.READABLE)
599 rv2 = self.obj.emit("test-paramspec-in", rv)
600 self.assertEqual(type(rv), type(rv2))
601 self.assertEqual(rv2.name, "mybool")
602 self.assertEqual(rv2.nick, "test-bool")
604 def test_C_paramspec(self):
605 self.notify_called = False
607 def cb_notify(obj, prop):
608 self.notify_called = True
609 self.assertEqual(obj, self.obj)
610 self.assertEqual(prop.name, "testprop")
612 self.obj.connect("notify", cb_notify)
613 self.obj.set_property("testprop", 42)
614 self.assertTrue(self.notify_called)
616 def test_gvalue(self):
618 rv = self.obj.emit("test-gvalue", 42)
619 self.assertEqual(rv, 42)
622 v = GObject.Value(GObject.TYPE_FLOAT, 1.234)
623 rv = self.obj.emit("test-gvalue", v)
624 self.assertAlmostEqual(rv, 1.234, 4)
627 rv = self.obj.emit("test-gvalue", 1.234)
628 self.assertAlmostEqual(rv, 1.234, 4)
631 v = GObject.Value(GObject.TYPE_INT64, GLib.MAXINT64)
632 rv = self.obj.emit("test-gvalue", v)
633 self.assertEqual(rv, GLib.MAXINT64)
636 v = GObject.Value(GObject.TYPE_UINT64, GLib.MAXUINT64)
637 rv = self.obj.emit("test-gvalue", v)
638 self.assertEqual(rv, GLib.MAXUINT64)
640 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=705291
641 def test_gvalue_implicit_int64(self):
643 rv = self.obj.emit("test-gvalue", GLib.MAXINT64)
644 self.assertEqual(rv, GLib.MAXINT64)
647 rv = self.obj.emit("test-gvalue", GLib.MAXUINT64)
648 self.assertEqual(rv, GLib.MAXUINT64)
650 def test_gvalue_ret(self):
651 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT),
653 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT),
655 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT64),
657 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT64),
659 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_STRING),
662 if 'generic-c-marshaller' in GObject.features:
663 class TestCMarshaller(_TestCMarshaller, unittest.TestCase):
667 print('** WARNING: LIBFFI disabled, not testing')
673 class TestPyGValue(unittest.TestCase):
674 def test_none_null_boxed_conversion(self):
675 class C(GObject.GObject):
676 __gsignals__ = dict(my_boxed_signal=(
677 GObject.SignalFlags.RUN_LAST,
678 GObject.TYPE_STRV, ()))
681 obj.connect('my-boxed-signal', lambda obj: None)
683 obj.emit('my-boxed-signal')
684 assert not sys.last_type
687 class TestSignalDecorator(unittest.TestCase):
688 class Decorated(GObject.GObject):
696 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
700 stomped = GObject.Signal('stomped', arg_types=(int,), doc='this will stomp')
701 unnamed = GObject.Signal()
703 class DecoratedOverride(GObject.GObject):
704 overridden_closure_called = False
705 notify_called = False
706 value = GObject.Property(type=int, default=0)
708 @GObject.SignalOverride
709 def notify(self, *args, **kargs):
710 self.overridden_closure_called = True
712 def on_notify(self, obj, prop):
713 self.notify_called = True
716 self.unnamedCalled = False
718 def onUnnamed(self, obj):
719 self.unnamedCalled = True
721 def test_get_signal_args(self):
722 self.assertEqual(self.Decorated.pushed.get_signal_args(),
723 (GObject.SignalFlags.RUN_FIRST, None, tuple(), None, None))
724 self.assertEqual(self.Decorated.pulled.get_signal_args(),
725 (GObject.SignalFlags.RUN_LAST, None, tuple(), None, None))
726 self.assertEqual(self.Decorated.stomped.get_signal_args(),
727 (GObject.SignalFlags.RUN_FIRST, None, (int,), None, None))
729 def test_closures_called(self):
730 decorated = self.Decorated()
731 self.assertEqual(decorated.value, 0)
732 decorated.pushed.emit()
733 self.assertEqual(decorated.value, 1)
734 decorated.pulled.emit()
735 self.assertEqual(decorated.value, 0)
737 def test_signal_copy(self):
738 blah = self.Decorated.stomped.copy('blah')
739 self.assertEqual(str(blah), blah)
740 self.assertEqual(blah.func, self.Decorated.stomped.func)
741 self.assertEqual(blah.flags, self.Decorated.stomped.flags)
742 self.assertEqual(blah.return_type, self.Decorated.stomped.return_type)
743 self.assertEqual(blah.arg_types, self.Decorated.stomped.arg_types)
744 self.assertEqual(blah.__doc__, self.Decorated.stomped.__doc__)
746 def test_doc_string(self):
747 # Test the two techniques for setting doc strings on the signals
748 # class variables, through the "doc" keyword or as the getter doc string.
749 self.assertEqual(self.Decorated.stomped.__doc__, 'this will stomp')
750 self.assertEqual(self.Decorated.pushed.__doc__, 'this will push')
752 def test_unnamed_signal_gets_named(self):
753 self.assertEqual(str(self.Decorated.unnamed), 'unnamed')
755 def test_unnamed_signal_gets_called(self):
756 obj = self.Decorated()
757 obj.connect('unnamed', self.onUnnamed)
758 self.assertEqual(self.unnamedCalled, False)
760 self.assertEqual(self.unnamedCalled, True)
762 def test_overridden_signal(self):
763 # Test that the pushed signal is called in with super and the override
764 # which should both increment the "value" to 3
765 obj = self.DecoratedOverride()
766 obj.connect("notify", obj.on_notify)
767 self.assertEqual(obj.value, 0)
769 self.assertEqual(obj.value, 1)
770 self.assertTrue(obj.overridden_closure_called)
771 self.assertTrue(obj.notify_called)
774 class TestSignalConnectors(unittest.TestCase):
775 class CustomButton(GObject.GObject):
776 on_notify_called = False
777 value = GObject.Property(type=int)
779 @GObject.Signal(arg_types=(int,))
780 def clicked(self, value):
787 def on_clicked(self, obj, value):
791 def test_signal_notify(self):
792 def on_notify(obj, param):
793 obj.on_notify_called = True
795 obj = self.CustomButton()
796 obj.connect('notify', on_notify)
797 self.assertFalse(obj.on_notify_called)
799 self.assertTrue(obj.on_notify_called)
801 def test_signal_emit(self):
802 # standard callback connection with different forms of emit.
803 obj = self.CustomButton()
804 obj.connect('clicked', self.on_clicked)
807 obj.emit('clicked', 1)
808 self.assertEqual(obj.value, 1)
809 self.assertEqual(obj, self.obj)
810 self.assertEqual(self.value, 1)
812 # using class signal as param
815 obj.emit(self.CustomButton.clicked, 1)
816 self.assertEqual(obj, self.obj)
817 self.assertEqual(self.value, 1)
819 # using bound signal as param
822 obj.emit(obj.clicked, 1)
823 self.assertEqual(obj, self.obj)
824 self.assertEqual(self.value, 1)
826 # using bound signal with emit
830 self.assertEqual(obj, self.obj)
831 self.assertEqual(self.value, 1)
833 def test_signal_class_connect(self):
834 obj = self.CustomButton()
835 obj.connect(self.CustomButton.clicked, self.on_clicked)
836 obj.emit('clicked', 2)
837 self.assertEqual(obj, self.obj)
838 self.assertEqual(self.value, 2)
840 def test_signal_bound_connect(self):
841 obj = self.CustomButton()
842 obj.clicked.connect(self.on_clicked)
843 obj.emit('clicked', 3)
844 self.assertEqual(obj, self.obj)
845 self.assertEqual(self.value, 3)
848 class _ConnectDataTestBase(object):
850 # - self.Object is overridden in sub-classes.
851 # - Numeric suffixes indicate the number of user data args passed in.
854 def run_connect_test(self, emit_args, user_data, flags=0):
859 callback_args.append(args)
862 obj.connect_data('sig-with-int64-prop', callback, connect_flags=flags, *user_data)
863 obj.emit('sig-with-int64-prop', *emit_args)
864 self.assertEqual(len(callback_args), 1)
865 return callback_args[0]
868 obj, value = self.run_connect_test([GLib.MAXINT64], user_data=[])
869 self.assertIsInstance(obj, self.Object)
870 self.assertEqual(value, GLib.MAXINT64)
873 obj, value, data = self.run_connect_test([GLib.MAXINT64],
874 user_data=['mydata'])
875 self.assertIsInstance(obj, self.Object)
876 self.assertEqual(value, GLib.MAXINT64)
877 self.assertEqual(data, 'mydata')
879 def test_after_0(self):
880 obj, value = self.run_connect_test([GLib.MAXINT64],
882 flags=GObject.ConnectFlags.AFTER)
883 self.assertIsInstance(obj, self.Object)
884 self.assertEqual(value, GLib.MAXINT64)
886 def test_after_1(self):
887 obj, value, data = self.run_connect_test([GLib.MAXINT64],
888 user_data=['mydata'],
889 flags=GObject.ConnectFlags.AFTER)
890 self.assertIsInstance(obj, self.Object)
891 self.assertEqual(value, GLib.MAXINT64)
892 self.assertEqual(data, 'mydata')
894 def test_swaped_0(self):
895 # Swapped only works with a single user data argument.
896 with self.assertRaises(ValueError):
897 self.run_connect_test([GLib.MAXINT64],
899 flags=GObject.ConnectFlags.SWAPPED)
901 def test_swaped_1(self):
902 # Notice obj and data are reversed in the return.
903 data, value, obj = self.run_connect_test([GLib.MAXINT64],
904 user_data=['mydata'],
905 flags=GObject.ConnectFlags.SWAPPED)
906 self.assertIsInstance(obj, self.Object)
907 self.assertEqual(value, GLib.MAXINT64)
908 self.assertEqual(data, 'mydata')
910 def test_swaped_2(self):
911 # Swapped only works with a single user data argument.
912 with self.assertRaises(ValueError):
913 self.run_connect_test([GLib.MAXINT64],
915 flags=GObject.ConnectFlags.SWAPPED)
917 def test_after_and_swapped_0(self):
918 # Swapped only works with a single user data argument.
919 with self.assertRaises(ValueError):
920 self.run_connect_test([GLib.MAXINT64],
922 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
924 def test_after_and_swapped_1(self):
925 # Notice obj and data are reversed in the return.
926 data, value, obj = self.run_connect_test([GLib.MAXINT64],
927 user_data=['mydata'],
928 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
929 self.assertIsInstance(obj, self.Object)
930 self.assertEqual(value, GLib.MAXINT64)
931 self.assertEqual(data, 'mydata')
933 def test_after_and_swapped_2(self):
934 # Swapped only works with a single user data argument.
935 with self.assertRaises(ValueError):
936 self.run_connect_test([GLib.MAXINT64],
938 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
941 class TestConnectDataNonIntrospected(unittest.TestCase, _ConnectDataTestBase):
942 # This tests connect_data with non-introspected signals
943 # (created in Python in this case).
944 class Object(GObject.Object):
945 test = GObject.Signal()
946 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
947 arg_types=[GObject.TYPE_INT64],
948 flags=GObject.SignalFlags.RUN_LAST)
951 @unittest.skipUnless(has_cairo, 'built without cairo support')
952 class TestConnectDataIntrospected(unittest.TestCase, _ConnectDataTestBase):
953 # This tests connect_data with introspected signals brought in from Regress.
954 Object = Regress.TestObj
957 class TestInstallSignals(unittest.TestCase):
958 # These tests only test how signalhelper.install_signals works
959 # with the __gsignals__ dict and therefore does not need to use
960 # GObject as a base class because that would automatically call
961 # install_signals within the meta-class.
963 __gsignals__ = {'test': (0, None, tuple())}
974 self.assertEqual(len(self.Base.__gsignals__), 1)
975 signalhelper.install_signals(self.Base)
976 self.assertEqual(len(self.Base.__gsignals__), 1)
978 def test_subclass_gets_empty_gsignals_dict(self):
979 # Installing signals will add the __gsignals__ dict to a class
980 # if it doesn't already exists.
981 self.assertFalse('__gsignals__' in self.Sub1.__dict__)
982 signalhelper.install_signals(self.Sub1)
983 self.assertTrue('__gsignals__' in self.Sub1.__dict__)
984 # Sub1 should only contain an empty signals dict, this tests:
985 # https://bugzilla.gnome.org/show_bug.cgi?id=686496
986 self.assertEqual(self.Sub1.__dict__['__gsignals__'], {})
988 def test_subclass_with_decorator_gets_gsignals_dict(self):
989 self.assertFalse('__gsignals__' in self.Sub2.__dict__)
990 signalhelper.install_signals(self.Sub2)
991 self.assertTrue('__gsignals__' in self.Sub2.__dict__)
992 self.assertEqual(len(self.Base.__gsignals__), 1)
993 self.assertEqual(len(self.Sub2.__gsignals__), 1)
994 self.assertTrue('sub2test' in self.Sub2.__gsignals__)
996 # Make sure the vfunc was added
997 self.assertTrue(hasattr(self.Sub2, 'do_sub2test'))
1000 # For this test to work with both python2 and 3 we need to dynamically
1001 # exec the given code due to the new syntax causing an error in python 2.
1002 annotated_class_code = """
1003 class AnnotatedSignalClass(GObject.GObject):
1005 def sig1(self, a:int, b:float):
1008 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
1009 def sig2_with_return(self, a:int, b:float) -> str:
1014 @unittest.skipUnless(sys.version_info >= (3, 0),
1015 'Argument annotations require Python 3')
1016 class TestPython3Signals(unittest.TestCase):
1017 AnnotatedClass = None
1020 exec(annotated_class_code, globals(), globals())
1021 self.assertTrue('AnnotatedSignalClass' in globals())
1022 self.AnnotatedClass = globals()['AnnotatedSignalClass']
1024 def test_annotations(self):
1025 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig1.func),
1026 (None, (int, float)))
1027 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig2_with_return.func),
1028 (str, (int, float)))
1030 self.assertEqual(self.AnnotatedClass.sig2_with_return.get_signal_args(),
1031 (GObject.SignalFlags.RUN_LAST, str, (int, float), None, None))
1032 self.assertEqual(self.AnnotatedClass.sig2_with_return.arg_types,
1034 self.assertEqual(self.AnnotatedClass.sig2_with_return.return_type,
1037 def test_emit_return(self):
1038 obj = self.AnnotatedClass()
1039 self.assertEqual(obj.sig2_with_return.emit(1, 2.0),
1043 class TestSignalModuleLevelFunctions(unittest.TestCase):
1044 def test_signal_list_ids_with_invalid_type(self):
1045 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1046 GObject.signal_list_ids(GObject.TYPE_INVALID)
1048 def test_signal_list_ids(self):
1049 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1050 GObject.signal_list_ids(GObject.TYPE_INT)
1052 ids = GObject.signal_list_ids(C)
1053 self.assertEqual(len(ids), 1)
1054 # Note canonicalized names
1055 self.assertEqual(GObject.signal_name(ids[0]), 'my-signal')
1056 # There is no signal 0 in gobject
1057 self.assertEqual(GObject.signal_name(0), None)
1059 def test_signal_lookup_with_invalid_type(self):
1060 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1061 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INVALID)
1063 def test_signal_lookup(self):
1064 ids = GObject.signal_list_ids(C)
1065 self.assertEqual(ids[0], GObject.signal_lookup('my_signal', C))
1066 self.assertEqual(ids[0], GObject.signal_lookup('my-signal', C))
1068 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1069 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INT)
1071 # Invalid signal names return 0 instead of raising
1072 self.assertEqual(GObject.signal_lookup('NOT_A_SIGNAL_NAME', C),
1075 def test_signal_query(self):
1076 my_signal_id, = GObject.signal_list_ids(C)
1078 # Form is: (id, name, gtype, arg_count, return_type, (arg_type1, ...))
1079 my_signal_expected_query_result = [my_signal_id, 'my-signal', C.__gtype__,
1080 1, GObject.TYPE_NONE, (GObject.TYPE_INT,)]
1081 # signal_query(name, type)
1082 self.assertEqual(list(GObject.signal_query('my-signal', C)), my_signal_expected_query_result)
1083 # signal_query(signal_id)
1084 self.assertEqual(list(GObject.signal_query(my_signal_id)), my_signal_expected_query_result)
1085 # invalid query returns None instead of raising
1086 self.assertEqual(GObject.signal_query(0), None)
1087 self.assertEqual(GObject.signal_query('NOT_A_SIGNAL', C),
1091 @unittest.skipUnless(has_cairo, 'built without cairo support')
1092 class TestIntrospectedSignals(unittest.TestCase):
1093 def test_object_param_signal(self):
1094 obj = Regress.TestObj()
1096 def callback(obj, obj_param):
1097 self.assertEqual(obj_param.props.int, 3)
1098 self.assertGreater(obj_param.__grefcount__, 1)
1102 obj.connect('sig-with-obj', callback)
1103 obj.emit_sig_with_obj()
1104 self.assertTrue(obj.called)
1106 def test_connect_after(self):
1107 obj = Regress.TestObj()
1109 def callback(obj, obj_param):
1113 obj.connect_after('sig-with-obj', callback)
1114 obj.emit_sig_with_obj()
1115 self.assertTrue(obj.called)
1117 def test_int64_param_from_py(self):
1118 obj = Regress.TestObj()
1120 def callback(obj, i):
1124 obj.callback_i = None
1125 obj.connect('sig-with-int64-prop', callback)
1126 rv = obj.emit('sig-with-int64-prop', GLib.MAXINT64)
1127 self.assertEqual(rv, GLib.MAXINT64)
1128 self.assertEqual(obj.callback_i, GLib.MAXINT64)
1130 def test_uint64_param_from_py(self):
1131 obj = Regress.TestObj()
1133 def callback(obj, i):
1137 obj.callback_i = None
1138 obj.connect('sig-with-uint64-prop', callback)
1139 rv = obj.emit('sig-with-uint64-prop', GLib.MAXUINT64)
1140 self.assertEqual(rv, GLib.MAXUINT64)
1141 self.assertEqual(obj.callback_i, GLib.MAXUINT64)
1143 def test_int64_param_from_c(self):
1144 obj = Regress.TestObj()
1146 def callback(obj, i):
1150 obj.callback_i = None
1152 obj.connect('sig-with-int64-prop', callback)
1153 obj.emit_sig_with_int64()
1154 self.assertEqual(obj.callback_i, GLib.MAXINT64)
1156 def test_uint64_param_from_c(self):
1157 obj = Regress.TestObj()
1159 def callback(obj, i):
1163 obj.callback_i = None
1165 obj.connect('sig-with-uint64-prop', callback)
1166 obj.emit_sig_with_uint64()
1167 self.assertEqual(obj.callback_i, GLib.MAXUINT64)
1169 def test_intarray_ret(self):
1170 obj = Regress.TestObj()
1172 def callback(obj, i):
1176 obj.callback_i = None
1179 obj.connect('sig-with-intarray-ret', callback)
1180 except TypeError as e:
1181 # compat with g-i 1.34.x
1182 if 'unknown signal' in str(e):
1186 rv = obj.emit('sig-with-intarray-ret', 42)
1187 self.assertEqual(obj.callback_i, 42)
1188 self.assertEqual(type(rv), GLib.Array)
1189 self.assertEqual(rv.len, 2)
1191 @unittest.skip('https://bugzilla.gnome.org/show_bug.cgi?id=669496')
1192 def test_array_parm(self):
1193 obj = Regress.TestObj()
1195 def callback(obj, arr):
1196 obj.callback_arr = arr
1198 obj.connect('sig-with-array-prop', callback)
1199 obj.callback_arr = None
1200 self.assertEqual(obj.emit('sig-with-array-prop', [1, 2, GLib.MAXUINT]), None)
1201 self.assertEqual(obj.callback_arr, [1, 2, GLib.MAXUINT])
1203 def test_held_struct_ref(self):
1206 def callback(obj, struct):
1207 # The struct held by Python will become a copy after this callback exits.
1208 struct.some_int = 42
1209 struct.some_int8 = 42
1210 held_structs.append(struct)
1212 struct = Regress.TestSimpleBoxedA()
1213 obj = Regress.TestObj()
1215 self.assertEqual(struct.some_int, 0)
1216 self.assertEqual(struct.some_int8, 0)
1218 obj.connect('test-with-static-scope-arg', callback)
1219 obj.emit('test-with-static-scope-arg', struct)
1221 # The held struct will be a copy of the modified struct.
1222 self.assertEqual(len(held_structs), 1)
1223 held_struct = held_structs[0]
1224 self.assertEqual(held_struct.some_int, 42)
1225 self.assertEqual(held_struct.some_int8, 42)
1227 # Boxed equality checks pointers by default.
1228 self.assertNotEqual(struct, held_struct)
1231 class _ConnectObjectTestBase(object):
1233 # - self.Object is overridden in sub-classes.
1234 # - Numeric suffixes indicate the number of user data args passed in.
1238 def run_connect_test(self, emit_args, user_data, flags=0):
1241 swap_obj = self.SwapObject()
1243 def callback(*args):
1244 callback_args.append(args)
1247 if flags & GObject.ConnectFlags.AFTER:
1248 connect_func = obj.connect_object_after
1250 connect_func = obj.connect_object
1252 with capture_gi_deprecation_warnings():
1253 connect_func('sig-with-int64-prop', callback, swap_obj, *user_data)
1254 obj.emit('sig-with-int64-prop', *emit_args)
1255 self.assertEqual(len(callback_args), 1)
1256 return callback_args[0]
1259 obj, value = self.run_connect_test([GLib.MAXINT64], user_data=[])
1260 self.assertIsInstance(obj, self.SwapObject)
1261 self.assertEqual(value, GLib.MAXINT64)
1264 obj, value, data = self.run_connect_test([GLib.MAXINT64],
1265 user_data=['mydata'])
1266 self.assertIsInstance(obj, self.SwapObject)
1267 self.assertEqual(value, GLib.MAXINT64)
1268 self.assertEqual(data, 'mydata')
1271 obj, value, data1, data2 = self.run_connect_test([GLib.MAXINT64],
1272 user_data=['mydata1', 'mydata2'])
1273 self.assertIsInstance(obj, self.SwapObject)
1274 self.assertEqual(value, GLib.MAXINT64)
1275 self.assertEqual(data1, 'mydata1')
1276 self.assertEqual(data2, 'mydata2')
1278 def test_after_0(self):
1279 obj, value = self.run_connect_test([GLib.MAXINT64],
1281 flags=GObject.ConnectFlags.AFTER)
1282 self.assertIsInstance(obj, self.SwapObject)
1283 self.assertEqual(value, GLib.MAXINT64)
1285 def test_after_1(self):
1286 obj, value, data = self.run_connect_test([GLib.MAXINT64],
1287 user_data=['mydata'],
1288 flags=GObject.ConnectFlags.AFTER)
1289 self.assertIsInstance(obj, self.SwapObject)
1290 self.assertEqual(value, GLib.MAXINT64)
1291 self.assertEqual(data, 'mydata')
1293 def test_after_2(self):
1294 obj, value, data1, data2 = self.run_connect_test([GLib.MAXINT64],
1295 user_data=['mydata1', 'mydata2'],
1296 flags=GObject.ConnectFlags.AFTER)
1297 self.assertIsInstance(obj, self.SwapObject)
1298 self.assertEqual(value, GLib.MAXINT64)
1299 self.assertEqual(data1, 'mydata1')
1300 self.assertEqual(data2, 'mydata2')
1303 class TestConnectGObjectNonIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1304 # This tests connect_object with non-introspected signals
1305 # (created in Python in this case).
1306 class Object(GObject.Object):
1307 test = GObject.Signal()
1308 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1309 arg_types=[GObject.TYPE_INT64],
1310 flags=GObject.SignalFlags.RUN_LAST)
1312 # Object passed for swapping is GObject based.
1313 class SwapObject(GObject.Object):
1317 @unittest.skipUnless(has_cairo, 'built without cairo support')
1318 class TestConnectGObjectIntrospected(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 GObject based.
1323 class SwapObject(GObject.Object):
1327 class TestConnectPyObjectNonIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1328 # This tests connect_object with non-introspected signals
1329 # (created in Python in this case).
1330 class Object(GObject.Object):
1331 test = GObject.Signal()
1332 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1333 arg_types=[GObject.TYPE_INT64],
1334 flags=GObject.SignalFlags.RUN_LAST)
1336 # Object passed for swapping is pure Python
1340 @unittest.skipUnless(has_cairo, 'built without cairo support')
1341 class TestConnectPyObjectIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1342 # This tests connect_object with introspected signals brought in from Regress.
1343 Object = Regress.TestObj
1345 # Object passed for swapping is pure Python
1349 class _RefCountTestBase(object):
1350 # NOTE: ref counts are always one more than expected because the getrefcount()
1351 # function adds a ref for the input argument.
1353 # Sub-classes set this
1356 class PyData(object):
1359 def test_callback_ref_count_del(self):
1360 def callback(obj, value):
1363 callback_ref = weakref.ref(callback)
1364 self.assertEqual(sys.getrefcount(callback), 2)
1367 obj.connect('sig-with-int64-prop', callback)
1368 self.assertEqual(sys.getrefcount(callback), 3)
1371 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1373 res = obj.emit('sig-with-int64-prop', 42)
1374 self.assertEqual(res, 21)
1375 self.assertEqual(sys.getrefcount(callback_ref), 2)
1378 self.assertIsNone(callback_ref())
1380 def test_callback_ref_count_disconnect(self):
1381 def callback(obj, value):
1384 callback_ref = weakref.ref(callback)
1385 self.assertEqual(sys.getrefcount(callback), 2)
1388 handler_id = obj.connect('sig-with-int64-prop', callback)
1389 self.assertEqual(sys.getrefcount(callback), 3)
1392 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1394 res = obj.emit('sig-with-int64-prop', 42)
1395 self.assertEqual(res, 21)
1396 self.assertEqual(sys.getrefcount(callback_ref), 2)
1398 obj.disconnect(handler_id)
1399 self.assertIsNone(callback_ref())
1401 def test_callback_ref_count_disconnect_by_func(self):
1402 def callback(obj, value):
1405 callback_ref = weakref.ref(callback)
1406 self.assertEqual(sys.getrefcount(callback), 2)
1409 obj.connect('sig-with-int64-prop', callback)
1410 self.assertEqual(sys.getrefcount(callback), 3)
1413 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1415 res = obj.emit('sig-with-int64-prop', 42)
1416 self.assertEqual(res, 21)
1417 self.assertEqual(sys.getrefcount(callback_ref), 2)
1419 obj.disconnect_by_func(callback_ref())
1420 self.assertIsNone(callback_ref())
1422 def test_user_data_ref_count(self):
1423 def callback(obj, value, data):
1426 data = self.PyData()
1427 data_ref = weakref.ref(data)
1428 self.assertEqual(sys.getrefcount(data), 2)
1431 obj.connect('sig-with-int64-prop', callback, data)
1432 self.assertEqual(sys.getrefcount(data), 3)
1435 self.assertEqual(sys.getrefcount(data_ref()), 2)
1437 res = obj.emit('sig-with-int64-prop', 42)
1438 self.assertEqual(res, 21)
1439 self.assertEqual(sys.getrefcount(data_ref()), 2)
1442 self.assertIsNone(data_ref())
1444 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=688064
1445 def test_object_ref_count(self):
1446 # connect_object() should only weakly reference the object passed in
1447 # and auto-disconnect the signal when the object is destroyed.
1448 def callback(data, value):
1451 data = GObject.Object()
1452 data_ref = weakref.ref(data)
1453 self.assertEqual(sys.getrefcount(data), 2)
1456 handler_id = obj.connect_object('sig-with-int64-prop', callback, data)
1457 self.assertEqual(sys.getrefcount(data), 2)
1459 res = obj.emit('sig-with-int64-prop', 42)
1460 self.assertEqual(res, 21)
1461 self.assertEqual(sys.getrefcount(data), 2)
1465 self.assertIsNone(data_ref())
1466 self.assertFalse(obj.handler_is_connected(handler_id))
1469 class TestRefCountsNonIntrospected(unittest.TestCase, _RefCountTestBase):
1470 class Object(GObject.Object):
1471 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1472 arg_types=[GObject.TYPE_INT64],
1473 flags=GObject.SignalFlags.RUN_LAST)
1476 @unittest.skipUnless(has_cairo, 'built without cairo support')
1477 class TestRefCountsIntrospected(unittest.TestCase, _RefCountTestBase):
1478 Object = Regress.TestObj
1481 if __name__ == '__main__':