8 from gi.repository import GObject, GLib, Regress
9 from gi import _signalhelper as signalhelper
11 from compathelper import _long
12 from helper import capture_glib_warnings, capture_gi_deprecation_warnings
15 class C(GObject.GObject):
16 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None,
19 def do_my_signal(self, arg):
24 def do_my_signal(self, arg2):
26 C.do_my_signal(self, arg2)
29 class TestSignalCreation(unittest.TestCase):
31 def test_illegals(self):
32 self.assertRaises(TypeError, lambda: GObject.signal_new('test',
36 (GObject.TYPE_LONG,)))
39 class TestChaining(unittest.TestCase):
42 self.inst.connect("my_signal", self.my_signal_handler_cb, 1, 2, 3)
44 def my_signal_handler_cb(self, *args):
46 assert isinstance(args[0], C)
47 assert args[0] == self.inst
49 assert isinstance(args[1], int)
52 assert args[2:] == (1, 2, 3)
54 def test_chaining(self):
55 self.inst.emit("my_signal", 42)
56 assert self.inst.arg == 42
58 def test_chaining2(self):
60 inst2.emit("my_signal", 44)
61 assert inst2.arg == 44
62 assert inst2.arg2 == 44
64 # This is for bug 153718
67 class TestGSignalsError(unittest.TestCase):
68 def test_invalid_type(self, *args):
70 class Foo(GObject.GObject):
72 self.assertRaises(TypeError, foo)
75 def test_invalid_name(self, *args):
77 class Foo(GObject.GObject):
78 __gsignals__ = {'not-exists': 'override'}
80 with capture_glib_warnings(allow_warnings=True):
81 self.assertRaises(TypeError, foo)
85 class TestGPropertyError(unittest.TestCase):
86 def test_invalid_type(self, *args):
88 class Foo(GObject.GObject):
89 __gproperties__ = None
90 self.assertRaises(TypeError, foo)
93 def test_invalid_name(self, *args):
95 class Foo(GObject.GObject):
96 __gproperties__ = {None: None}
98 self.assertRaises(TypeError, foo)
102 class TestList(unittest.TestCase):
103 def test_list_names(self):
104 self.assertEqual(GObject.signal_list_names(C), ('my-signal',))
107 def my_accumulator(ihint, return_accu, handler_return, user_data):
108 """An accumulator that stops emission when the sum of handler
109 returned values reaches 3"""
110 assert user_data == "accum data"
112 return False, return_accu
113 return True, return_accu + handler_return
116 class Foo(GObject.GObject):
117 my_acc_signal = GObject.Signal(return_type=GObject.TYPE_INT,
118 flags=GObject.SignalFlags.RUN_LAST,
119 accumulator=my_accumulator,
120 accu_data="accum data")
122 my_other_acc_signal = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
123 flags=GObject.SignalFlags.RUN_LAST,
124 accumulator=GObject.signal_accumulator_true_handled)
126 my_acc_first_wins = GObject.Signal(return_type=GObject.TYPE_BOOLEAN,
127 flags=GObject.SignalFlags.RUN_LAST,
128 accumulator=GObject.signal_accumulator_first_wins)
131 class TestAccumulator(unittest.TestCase):
133 def test_accumulator(self):
135 inst.my_acc_signal.connect(lambda obj: 1)
136 inst.my_acc_signal.connect(lambda obj: 2)
137 # the value returned in the following handler will not be
138 # considered, because at this point the accumulator already
140 inst.my_acc_signal.connect(lambda obj: 3)
141 retval = inst.my_acc_signal.emit()
142 self.assertEqual(retval, 3)
144 def test_accumulator_true_handled(self):
146 inst.my_other_acc_signal.connect(self._true_handler1)
147 inst.my_other_acc_signal.connect(self._true_handler2)
148 # the following handler will not be called because handler2
149 # returns True, so it should stop the emission.
150 inst.my_other_acc_signal.connect(self._true_handler3)
151 self.__true_val = None
152 inst.my_other_acc_signal.emit()
153 self.assertEqual(self.__true_val, 2)
155 def test_accumulator_first_wins(self):
156 # First signal hit will always win
158 inst.my_acc_first_wins.connect(self._true_handler3)
159 inst.my_acc_first_wins.connect(self._true_handler1)
160 inst.my_acc_first_wins.connect(self._true_handler2)
161 self.__true_val = None
162 inst.my_acc_first_wins.emit()
163 self.assertEqual(self.__true_val, 3)
165 def _true_handler1(self, obj):
169 def _true_handler2(self, obj):
173 def _true_handler3(self, obj):
178 class E(GObject.GObject):
179 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
182 # Property used to test detailed signal
183 prop = GObject.Property(type=int, default=0)
186 GObject.GObject.__init__(self)
190 assert self.status == 0
194 class F(GObject.GObject):
195 __gsignals__ = {'signal': (GObject.SignalFlags.RUN_FIRST, None,
199 GObject.GObject.__init__(self)
206 class TestEmissionHook(unittest.TestCase):
210 e.connect('signal', self._callback)
211 GObject.add_emission_hook(E, "signal", self._emission_hook)
213 self.assertEqual(e.status, 3)
215 def test_remove(self):
218 e.connect('signal', self._callback)
219 hook_id = GObject.add_emission_hook(E, "signal", self._emission_hook)
220 GObject.remove_emission_hook(E, "signal", hook_id)
222 self.assertEqual(e.status, 3)
224 def _emission_hook(self, e):
225 self.assertEqual(e.status, 1)
228 def _callback(self, e):
230 self.assertEqual(e.status, 2)
232 self.assertEqual(e.status, 1)
235 def test_callback_return_false(self):
239 def _emission_hook(obj):
242 GObject.add_emission_hook(obj, "signal", _emission_hook)
245 self.assertEqual(obj.status, 3)
247 def test_callback_return_true(self):
251 def _emission_hook(obj):
254 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
257 GObject.remove_emission_hook(obj, "signal", hook_id)
258 self.assertEqual(obj.status, 4)
260 def test_callback_return_true_but_remove(self):
264 def _emission_hook(obj):
267 hook_id = GObject.add_emission_hook(obj, "signal", _emission_hook)
269 GObject.remove_emission_hook(obj, "signal", hook_id)
271 self.assertEqual(obj.status, 3)
274 class TestMatching(unittest.TestCase):
275 class Object(GObject.Object):
277 prop = GObject.Property(type=int, default=0)
283 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=692918
284 def test_signal_handler_block_matching(self):
286 "Hack to work around: "
292 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
295 self.assertEqual(obj.status, 0)
296 obj.emit('my-signal')
297 self.assertEqual(obj.status, 1)
299 # Blocking by match criteria disables the foo callback
300 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
301 count = GObject.signal_handlers_block_matched(obj,
302 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
303 signal_id=signal_id, detail=detail,
304 closure=foo, func=dummy, data=dummy)
305 self.assertEqual(count, 1)
306 obj.emit('my-signal')
307 self.assertEqual(obj.status, 1)
309 # Unblocking by the same match criteria allows callback to work again
310 count = GObject.signal_handlers_unblock_matched(obj,
311 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
312 signal_id=signal_id, detail=detail,
313 closure=foo, func=dummy, data=dummy)
314 self.assertEqual(count, 1)
315 obj.emit('my-signal')
316 self.assertEqual(obj.status, 2)
318 # Disconnecting by match criteria completely removes the handler
319 count = GObject.signal_handlers_disconnect_matched(obj,
320 GObject.SignalMatchType.ID | GObject.SignalMatchType.CLOSURE,
321 signal_id=signal_id, detail=detail,
322 closure=foo, func=dummy, data=dummy)
323 self.assertEqual(count, 1)
324 obj.emit('my-signal')
325 self.assertEqual(obj.status, 2)
327 def test_signal_handler_find(self):
332 handler_id = GObject.signal_connect_closure(obj, 'my-signal', foo, after=False)
334 signal_id, detail = GObject.signal_parse_name('my-signal', obj, True)
335 found_id = GObject.signal_handler_find(obj,
336 GObject.SignalMatchType.ID,
337 signal_id=signal_id, detail=detail,
338 closure=None, func=0, data=0)
339 self.assertEqual(handler_id, found_id)
342 class TestClosures(unittest.TestCase):
345 self.emission_stopped = False
346 self.emission_error = False
347 self.handler_pending = False
349 def _callback_handler_pending(self, e):
350 signal_id, detail = GObject.signal_parse_name('signal', e, True)
351 self.handler_pending = GObject.signal_has_handler_pending(e, signal_id, detail,
352 may_be_blocked=False)
354 def _callback(self, e):
357 def _callback_stop_emission(self, obj, prop, stop_it):
359 obj.stop_emission_by_name('notify::prop')
360 self.emission_stopped = True
364 def _callback_invalid_stop_emission_name(self, obj, prop):
365 with capture_glib_warnings(allow_warnings=True) as warn:
366 obj.stop_emission_by_name('notasignal::baddetail')
367 self.emission_error = True
368 self.assertTrue(warn)
370 def test_disconnect_by_func(self):
372 e.connect('signal', self._callback)
373 e.disconnect_by_func(self._callback)
375 self.assertEqual(self.count, 0)
377 def test_disconnect(self):
379 handler_id = e.connect('signal', self._callback)
380 self.assertTrue(e.handler_is_connected(handler_id))
381 e.disconnect(handler_id)
383 self.assertEqual(self.count, 0)
384 self.assertFalse(e.handler_is_connected(handler_id))
386 def test_stop_emission_by_name(self):
389 # Sandwich a callback that stops emission in between a callback that increments
390 e.connect('notify::prop', self._callback_stop_emission, False)
391 e.connect('notify::prop', self._callback_stop_emission, True)
392 e.connect('notify::prop', self._callback_stop_emission, False)
394 e.set_property('prop', 1234)
395 self.assertEqual(e.get_property('prop'), 1234)
396 self.assertEqual(self.count, 1)
397 self.assertTrue(self.emission_stopped)
399 def test_stop_emission_by_name_error(self):
402 e.connect('notify::prop', self._callback_invalid_stop_emission_name)
403 with capture_glib_warnings():
404 e.set_property('prop', 1234)
405 self.assertTrue(self.emission_error)
407 def test_handler_block(self):
409 e.connect('signal', self._callback)
410 e.handler_block_by_func(self._callback)
412 self.assertEqual(self.count, 0)
414 def test_handler_unblock(self):
416 handler_id = e.connect('signal', self._callback)
417 e.handler_block(handler_id)
418 e.handler_unblock_by_func(self._callback)
420 self.assertEqual(self.count, 1)
422 def test_handler_block_method(self):
428 def callback(self, o):
430 o.handler_block_by_func(self.callback)
434 e.connect("signal", inst.callback)
436 self.assertEqual(inst.a, 1)
439 def test_gstring(self):
440 class C(GObject.GObject):
441 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_GSTRING,
442 (GObject.TYPE_GSTRING,))}
444 def __init__(self, test):
445 GObject.GObject.__init__(self)
448 def do_my_signal(self, data):
450 self.test.assertEqual(len(data), 3)
451 return ''.join([data[2], data[1], data[0]])
453 data = c.emit("my_signal", "\01\00\02")
454 self.assertEqual(data, "\02\00\01")
456 def test_handler_pending(self):
458 obj.connect('signal', self._callback_handler_pending)
459 obj.connect('signal', self._callback)
461 self.assertEqual(self.count, 0)
462 self.assertEqual(self.handler_pending, False)
465 self.assertEqual(self.count, 1)
466 self.assertEqual(self.handler_pending, True)
468 def test_signal_handlers_destroy(self):
470 obj.connect('signal', self._callback)
471 obj.connect('signal', self._callback)
472 obj.connect('signal', self._callback)
475 self.assertEqual(self.count, 3)
477 # count should remain at 3 after all handlers are destroyed
478 GObject.signal_handlers_destroy(obj)
480 self.assertEqual(self.count, 3)
483 class SigPropClass(GObject.GObject):
484 __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None,
485 (GObject.TYPE_INT,))}
488 'foo': (str, None, None, '',
489 GObject.ParamFlags.WRITABLE | GObject.ParamFlags.CONSTRUCT),
492 signal_emission_failed = False
494 def do_my_signal(self, arg):
497 def do_set_property(self, pspec, value):
498 if pspec.name == 'foo':
501 raise AttributeError('unknown property %s' % pspec.name)
503 self.emit("my-signal", 1)
505 self.signal_emission_failed = True
508 class TestSigProp(unittest.TestCase):
509 def test_emit_in_property_setter(self):
511 self.assertFalse(obj.signal_emission_failed)
514 class CM(GObject.GObject):
516 test1=(GObject.SignalFlags.RUN_FIRST, None, ()),
517 test2=(GObject.SignalFlags.RUN_LAST, None, (str,)),
518 test3=(GObject.SignalFlags.RUN_LAST, int, (GObject.TYPE_DOUBLE,)),
519 test4=(GObject.SignalFlags.RUN_FIRST, None,
520 (bool, _long, GObject.TYPE_FLOAT, GObject.TYPE_DOUBLE, int,
521 GObject.TYPE_UINT, GObject.TYPE_ULONG)),
522 test_float=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_FLOAT, (GObject.TYPE_FLOAT,)),
523 test_double=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_DOUBLE, (GObject.TYPE_DOUBLE,)),
524 test_int64=(GObject.SignalFlags.RUN_LAST, GObject.TYPE_INT64, (GObject.TYPE_INT64,)),
525 test_string=(GObject.SignalFlags.RUN_LAST, str, (str,)),
526 test_object=(GObject.SignalFlags.RUN_LAST, object, (object,)),
527 test_paramspec=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, ()),
528 test_paramspec_in=(GObject.SignalFlags.RUN_LAST, GObject.ParamSpec, (GObject.ParamSpec, )),
529 test_gvalue=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.Value,)),
530 test_gvalue_ret=(GObject.SignalFlags.RUN_LAST, GObject.Value, (GObject.TYPE_GTYPE,)),
533 testprop = GObject.Property(type=int)
536 class _TestCMarshaller:
539 testhelper.connectcallbacks(self.obj)
541 def test_test1(self):
542 self.obj.emit("test1")
544 def test_test2(self):
545 self.obj.emit("test2", "string")
547 def test_test3(self):
548 rv = self.obj.emit("test3", 42.0)
549 self.assertEqual(rv, 20)
551 def test_test4(self):
552 self.obj.emit("test4", True, _long(10), 3.14, 1.78, 20, _long(30), _long(31))
554 def test_float(self):
555 rv = self.obj.emit("test-float", 1.234)
556 self.assertTrue(rv >= 1.233999 and rv <= 1.2400001, rv)
558 def test_double(self):
559 rv = self.obj.emit("test-double", 1.234)
560 self.assertEqual(rv, 1.234)
562 def test_int64(self):
563 rv = self.obj.emit("test-int64", 102030405)
564 self.assertEqual(rv, 102030405)
566 rv = self.obj.emit("test-int64", GLib.MAXINT64)
567 self.assertEqual(rv, GLib.MAXINT64 - 1)
569 rv = self.obj.emit("test-int64", GLib.MININT64)
570 self.assertEqual(rv, GLib.MININT64)
572 def test_string(self):
573 rv = self.obj.emit("test-string", "str")
574 self.assertEqual(rv, "str")
576 def test_object(self):
577 rv = self.obj.emit("test-object", self)
578 self.assertEqual(rv, self)
580 def test_paramspec(self):
581 rv = self.obj.emit("test-paramspec")
582 self.assertEqual(rv.name, "test-param")
583 self.assertEqual(rv.nick, "test")
585 @unittest.skipUnless(hasattr(GObject, 'param_spec_boolean'),
586 'too old gobject-introspection')
587 def test_paramspec_in(self):
588 rv = GObject.param_spec_boolean('mybool', 'test-bool', 'do something',
589 True, GObject.ParamFlags.READABLE)
591 rv2 = self.obj.emit("test-paramspec-in", rv)
592 self.assertEqual(type(rv), type(rv2))
593 self.assertEqual(rv2.name, "mybool")
594 self.assertEqual(rv2.nick, "test-bool")
596 def test_C_paramspec(self):
597 self.notify_called = False
599 def cb_notify(obj, prop):
600 self.notify_called = True
601 self.assertEqual(obj, self.obj)
602 self.assertEqual(prop.name, "testprop")
604 self.obj.connect("notify", cb_notify)
605 self.obj.set_property("testprop", 42)
606 self.assertTrue(self.notify_called)
608 def test_gvalue(self):
610 rv = self.obj.emit("test-gvalue", 42)
611 self.assertEqual(rv, 42)
614 v = GObject.Value(GObject.TYPE_FLOAT, 1.234)
615 rv = self.obj.emit("test-gvalue", v)
616 self.assertAlmostEqual(rv, 1.234, places=4)
619 rv = self.obj.emit("test-gvalue", 1.234)
620 self.assertAlmostEqual(rv, 1.234, places=4)
623 v = GObject.Value(GObject.TYPE_INT64, GLib.MAXINT64)
624 rv = self.obj.emit("test-gvalue", v)
625 self.assertEqual(rv, GLib.MAXINT64)
628 v = GObject.Value(GObject.TYPE_UINT64, GLib.MAXUINT64)
629 rv = self.obj.emit("test-gvalue", v)
630 self.assertEqual(rv, GLib.MAXUINT64)
632 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=705291
633 def test_gvalue_implicit_int64(self):
635 rv = self.obj.emit("test-gvalue", GLib.MAXINT64)
636 self.assertEqual(rv, GLib.MAXINT64)
639 rv = self.obj.emit("test-gvalue", GLib.MAXUINT64)
640 self.assertEqual(rv, GLib.MAXUINT64)
642 def test_gvalue_ret(self):
643 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT),
645 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT),
647 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_INT64),
649 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_UINT64),
651 self.assertEqual(self.obj.emit("test-gvalue-ret", GObject.TYPE_STRING),
655 if 'generic-c-marshaller' in GObject.features:
656 class TestCMarshaller(_TestCMarshaller, unittest.TestCase):
660 print('** WARNING: LIBFFI disabled, not testing')
666 class TestPyGValue(unittest.TestCase):
667 def test_none_null_boxed_conversion(self):
668 class C(GObject.GObject):
669 __gsignals__ = dict(my_boxed_signal=(
670 GObject.SignalFlags.RUN_LAST,
671 GObject.TYPE_STRV, ()))
674 obj.connect('my-boxed-signal', lambda obj: None)
676 obj.emit('my-boxed-signal')
677 assert not sys.last_type
680 class TestSignalDecorator(unittest.TestCase):
681 class Decorated(GObject.GObject):
689 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
693 stomped = GObject.Signal('stomped', arg_types=(int,), doc='this will stomp')
694 unnamed = GObject.Signal()
696 class DecoratedOverride(GObject.GObject):
697 overridden_closure_called = False
698 notify_called = False
699 value = GObject.Property(type=int, default=0)
701 @GObject.SignalOverride
702 def notify(self, *args, **kargs):
703 self.overridden_closure_called = True
705 def on_notify(self, obj, prop):
706 self.notify_called = True
709 self.unnamedCalled = False
711 def onUnnamed(self, obj):
712 self.unnamedCalled = True
714 def test_get_signal_args(self):
715 self.assertEqual(self.Decorated.pushed.get_signal_args(),
716 (GObject.SignalFlags.RUN_FIRST, None, tuple(), None, None))
717 self.assertEqual(self.Decorated.pulled.get_signal_args(),
718 (GObject.SignalFlags.RUN_LAST, None, tuple(), None, None))
719 self.assertEqual(self.Decorated.stomped.get_signal_args(),
720 (GObject.SignalFlags.RUN_FIRST, None, (int,), None, None))
722 def test_closures_called(self):
723 decorated = self.Decorated()
724 self.assertEqual(decorated.value, 0)
725 decorated.pushed.emit()
726 self.assertEqual(decorated.value, 1)
727 decorated.pulled.emit()
728 self.assertEqual(decorated.value, 0)
730 def test_signal_copy(self):
731 blah = self.Decorated.stomped.copy('blah')
732 self.assertEqual(str(blah), blah)
733 self.assertEqual(blah.func, self.Decorated.stomped.func)
734 self.assertEqual(blah.flags, self.Decorated.stomped.flags)
735 self.assertEqual(blah.return_type, self.Decorated.stomped.return_type)
736 self.assertEqual(blah.arg_types, self.Decorated.stomped.arg_types)
737 self.assertEqual(blah.__doc__, self.Decorated.stomped.__doc__)
739 def test_doc_string(self):
740 # Test the two techniques for setting doc strings on the signals
741 # class variables, through the "doc" keyword or as the getter doc string.
742 self.assertEqual(self.Decorated.stomped.__doc__, 'this will stomp')
743 self.assertEqual(self.Decorated.pushed.__doc__, 'this will push')
745 def test_unnamed_signal_gets_named(self):
746 self.assertEqual(str(self.Decorated.unnamed), 'unnamed')
748 def test_unnamed_signal_gets_called(self):
749 obj = self.Decorated()
750 obj.connect('unnamed', self.onUnnamed)
751 self.assertEqual(self.unnamedCalled, False)
753 self.assertEqual(self.unnamedCalled, True)
755 def test_overridden_signal(self):
756 # Test that the pushed signal is called in with super and the override
757 # which should both increment the "value" to 3
758 obj = self.DecoratedOverride()
759 obj.connect("notify", obj.on_notify)
760 self.assertEqual(obj.value, 0)
762 self.assertEqual(obj.value, 1)
763 self.assertTrue(obj.overridden_closure_called)
764 self.assertTrue(obj.notify_called)
767 class TestSignalConnectors(unittest.TestCase):
768 class CustomButton(GObject.GObject):
769 on_notify_called = False
770 value = GObject.Property(type=int)
772 @GObject.Signal(arg_types=(int,))
773 def clicked(self, value):
780 def on_clicked(self, obj, value):
784 def test_signal_notify(self):
785 def on_notify(obj, param):
786 obj.on_notify_called = True
788 obj = self.CustomButton()
789 obj.connect('notify', on_notify)
790 self.assertFalse(obj.on_notify_called)
792 self.assertTrue(obj.on_notify_called)
794 def test_signal_emit(self):
795 # standard callback connection with different forms of emit.
796 obj = self.CustomButton()
797 obj.connect('clicked', self.on_clicked)
800 obj.emit('clicked', 1)
801 self.assertEqual(obj.value, 1)
802 self.assertEqual(obj, self.obj)
803 self.assertEqual(self.value, 1)
805 # using class signal as param
808 obj.emit(self.CustomButton.clicked, 1)
809 self.assertEqual(obj, self.obj)
810 self.assertEqual(self.value, 1)
812 # using bound signal as param
815 obj.emit(obj.clicked, 1)
816 self.assertEqual(obj, self.obj)
817 self.assertEqual(self.value, 1)
819 # using bound signal with emit
823 self.assertEqual(obj, self.obj)
824 self.assertEqual(self.value, 1)
826 def test_signal_class_connect(self):
827 obj = self.CustomButton()
828 obj.connect(self.CustomButton.clicked, self.on_clicked)
829 obj.emit('clicked', 2)
830 self.assertEqual(obj, self.obj)
831 self.assertEqual(self.value, 2)
833 def test_signal_bound_connect(self):
834 obj = self.CustomButton()
835 obj.clicked.connect(self.on_clicked)
836 obj.emit('clicked', 3)
837 self.assertEqual(obj, self.obj)
838 self.assertEqual(self.value, 3)
841 class _ConnectDataTestBase(object):
843 # - self.Object is overridden in sub-classes.
844 # - Numeric suffixes indicate the number of user data args passed in.
847 def run_connect_test(self, emit_args, user_data, flags=0):
852 callback_args.append(args)
855 obj.connect_data('sig-with-int64-prop', callback, connect_flags=flags, *user_data)
856 obj.emit('sig-with-int64-prop', *emit_args)
857 self.assertEqual(len(callback_args), 1)
858 return callback_args[0]
861 obj, value = self.run_connect_test([GLib.MAXINT64], user_data=[])
862 self.assertIsInstance(obj, self.Object)
863 self.assertEqual(value, GLib.MAXINT64)
866 obj, value, data = self.run_connect_test([GLib.MAXINT64],
867 user_data=['mydata'])
868 self.assertIsInstance(obj, self.Object)
869 self.assertEqual(value, GLib.MAXINT64)
870 self.assertEqual(data, 'mydata')
872 def test_after_0(self):
873 obj, value = self.run_connect_test([GLib.MAXINT64],
875 flags=GObject.ConnectFlags.AFTER)
876 self.assertIsInstance(obj, self.Object)
877 self.assertEqual(value, GLib.MAXINT64)
879 def test_after_1(self):
880 obj, value, data = self.run_connect_test([GLib.MAXINT64],
881 user_data=['mydata'],
882 flags=GObject.ConnectFlags.AFTER)
883 self.assertIsInstance(obj, self.Object)
884 self.assertEqual(value, GLib.MAXINT64)
885 self.assertEqual(data, 'mydata')
887 def test_swaped_0(self):
888 # Swapped only works with a single user data argument.
889 with self.assertRaises(ValueError):
890 self.run_connect_test([GLib.MAXINT64],
892 flags=GObject.ConnectFlags.SWAPPED)
894 def test_swaped_1(self):
895 # Notice obj and data are reversed in the return.
896 data, value, obj = self.run_connect_test([GLib.MAXINT64],
897 user_data=['mydata'],
898 flags=GObject.ConnectFlags.SWAPPED)
899 self.assertIsInstance(obj, self.Object)
900 self.assertEqual(value, GLib.MAXINT64)
901 self.assertEqual(data, 'mydata')
903 def test_swaped_2(self):
904 # Swapped only works with a single user data argument.
905 with self.assertRaises(ValueError):
906 self.run_connect_test([GLib.MAXINT64],
908 flags=GObject.ConnectFlags.SWAPPED)
910 def test_after_and_swapped_0(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.AFTER | GObject.ConnectFlags.SWAPPED)
917 def test_after_and_swapped_1(self):
918 # Notice obj and data are reversed in the return.
919 data, value, obj = self.run_connect_test([GLib.MAXINT64],
920 user_data=['mydata'],
921 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
922 self.assertIsInstance(obj, self.Object)
923 self.assertEqual(value, GLib.MAXINT64)
924 self.assertEqual(data, 'mydata')
926 def test_after_and_swapped_2(self):
927 # Swapped only works with a single user data argument.
928 with self.assertRaises(ValueError):
929 self.run_connect_test([GLib.MAXINT64],
931 flags=GObject.ConnectFlags.AFTER | GObject.ConnectFlags.SWAPPED)
934 class TestConnectDataNonIntrospected(unittest.TestCase, _ConnectDataTestBase):
935 # This tests connect_data with non-introspected signals
936 # (created in Python in this case).
937 class Object(GObject.Object):
938 test = GObject.Signal()
939 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
940 arg_types=[GObject.TYPE_INT64],
941 flags=GObject.SignalFlags.RUN_LAST)
944 class TestConnectDataIntrospected(unittest.TestCase, _ConnectDataTestBase):
945 # This tests connect_data with introspected signals brought in from Regress.
946 Object = Regress.TestObj
949 class TestInstallSignals(unittest.TestCase):
950 # These tests only test how signalhelper.install_signals works
951 # with the __gsignals__ dict and therefore does not need to use
952 # GObject as a base class because that would automatically call
953 # install_signals within the meta-class.
955 __gsignals__ = {'test': (0, None, tuple())}
966 self.assertEqual(len(self.Base.__gsignals__), 1)
967 signalhelper.install_signals(self.Base)
968 self.assertEqual(len(self.Base.__gsignals__), 1)
970 def test_subclass_gets_empty_gsignals_dict(self):
971 # Installing signals will add the __gsignals__ dict to a class
972 # if it doesn't already exists.
973 self.assertFalse('__gsignals__' in self.Sub1.__dict__)
974 signalhelper.install_signals(self.Sub1)
975 self.assertTrue('__gsignals__' in self.Sub1.__dict__)
976 # Sub1 should only contain an empty signals dict, this tests:
977 # https://bugzilla.gnome.org/show_bug.cgi?id=686496
978 self.assertEqual(self.Sub1.__dict__['__gsignals__'], {})
980 def test_subclass_with_decorator_gets_gsignals_dict(self):
981 self.assertFalse('__gsignals__' in self.Sub2.__dict__)
982 signalhelper.install_signals(self.Sub2)
983 self.assertTrue('__gsignals__' in self.Sub2.__dict__)
984 self.assertEqual(len(self.Base.__gsignals__), 1)
985 self.assertEqual(len(self.Sub2.__gsignals__), 1)
986 self.assertTrue('sub2test' in self.Sub2.__gsignals__)
988 # Make sure the vfunc was added
989 self.assertTrue(hasattr(self.Sub2, 'do_sub2test'))
992 # For this test to work with both python2 and 3 we need to dynamically
993 # exec the given code due to the new syntax causing an error in python 2.
994 annotated_class_code = """
995 class AnnotatedSignalClass(GObject.GObject):
997 def sig1(self, a:int, b:float):
1000 @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST)
1001 def sig2_with_return(self, a:int, b:float) -> str:
1006 @unittest.skipUnless(sys.version_info >= (3, 0),
1007 'Argument annotations require Python 3')
1008 class TestPython3Signals(unittest.TestCase):
1009 AnnotatedClass = None
1012 exec(annotated_class_code, globals(), globals())
1013 self.assertTrue('AnnotatedSignalClass' in globals())
1014 self.AnnotatedClass = globals()['AnnotatedSignalClass']
1016 def test_annotations(self):
1017 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig1.func),
1018 (None, (int, float)))
1019 self.assertEqual(signalhelper.get_signal_annotations(self.AnnotatedClass.sig2_with_return.func),
1020 (str, (int, float)))
1022 self.assertEqual(self.AnnotatedClass.sig2_with_return.get_signal_args(),
1023 (GObject.SignalFlags.RUN_LAST, str, (int, float), None, None))
1024 self.assertEqual(self.AnnotatedClass.sig2_with_return.arg_types,
1026 self.assertEqual(self.AnnotatedClass.sig2_with_return.return_type,
1029 def test_emit_return(self):
1030 obj = self.AnnotatedClass()
1031 self.assertEqual(obj.sig2_with_return.emit(1, 2.0),
1035 class TestSignalModuleLevelFunctions(unittest.TestCase):
1036 def test_signal_list_ids_with_invalid_type(self):
1037 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1038 GObject.signal_list_ids(GObject.TYPE_INVALID)
1040 def test_signal_list_ids(self):
1041 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1042 GObject.signal_list_ids(GObject.TYPE_INT)
1044 ids = GObject.signal_list_ids(C)
1045 self.assertEqual(len(ids), 1)
1046 # Note canonicalized names
1047 self.assertEqual(GObject.signal_name(ids[0]), 'my-signal')
1048 # There is no signal 0 in gobject
1049 self.assertEqual(GObject.signal_name(0), None)
1051 def test_signal_lookup_with_invalid_type(self):
1052 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1053 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INVALID)
1055 def test_signal_lookup(self):
1056 ids = GObject.signal_list_ids(C)
1057 self.assertEqual(ids[0], GObject.signal_lookup('my_signal', C))
1058 self.assertEqual(ids[0], GObject.signal_lookup('my-signal', C))
1060 with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'):
1061 GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INT)
1063 # Invalid signal names return 0 instead of raising
1064 self.assertEqual(GObject.signal_lookup('NOT_A_SIGNAL_NAME', C),
1067 def test_signal_query(self):
1068 my_signal_id, = GObject.signal_list_ids(C)
1070 # Form is: (id, name, gtype, arg_count, return_type, (arg_type1, ...))
1071 my_signal_expected_query_result = [my_signal_id, 'my-signal', C.__gtype__,
1072 1, GObject.TYPE_NONE, (GObject.TYPE_INT,)]
1073 # signal_query(name, type)
1074 self.assertEqual(list(GObject.signal_query('my-signal', C)), my_signal_expected_query_result)
1075 # signal_query(signal_id)
1076 self.assertEqual(list(GObject.signal_query(my_signal_id)), my_signal_expected_query_result)
1077 # invalid query returns None instead of raising
1078 self.assertEqual(GObject.signal_query(0), None)
1079 self.assertEqual(GObject.signal_query('NOT_A_SIGNAL', C),
1083 class TestIntrospectedSignals(unittest.TestCase):
1084 def test_object_param_signal(self):
1085 obj = Regress.TestObj()
1087 def callback(obj, obj_param):
1088 self.assertEqual(obj_param.props.int, 3)
1089 self.assertGreater(obj_param.__grefcount__, 1)
1093 obj.connect('sig-with-obj', callback)
1094 obj.emit_sig_with_obj()
1095 self.assertTrue(obj.called)
1097 def test_connect_after(self):
1098 obj = Regress.TestObj()
1100 def callback(obj, obj_param):
1104 obj.connect_after('sig-with-obj', callback)
1105 obj.emit_sig_with_obj()
1106 self.assertTrue(obj.called)
1108 def test_int64_param_from_py(self):
1109 obj = Regress.TestObj()
1111 def callback(obj, i):
1115 obj.callback_i = None
1116 obj.connect('sig-with-int64-prop', callback)
1117 rv = obj.emit('sig-with-int64-prop', GLib.MAXINT64)
1118 self.assertEqual(rv, GLib.MAXINT64)
1119 self.assertEqual(obj.callback_i, GLib.MAXINT64)
1121 def test_uint64_param_from_py(self):
1122 obj = Regress.TestObj()
1124 def callback(obj, i):
1128 obj.callback_i = None
1129 obj.connect('sig-with-uint64-prop', callback)
1130 rv = obj.emit('sig-with-uint64-prop', GLib.MAXUINT64)
1131 self.assertEqual(rv, GLib.MAXUINT64)
1132 self.assertEqual(obj.callback_i, GLib.MAXUINT64)
1134 def test_int64_param_from_c(self):
1135 obj = Regress.TestObj()
1137 def callback(obj, i):
1141 obj.callback_i = None
1143 obj.connect('sig-with-int64-prop', callback)
1144 obj.emit_sig_with_int64()
1145 self.assertEqual(obj.callback_i, GLib.MAXINT64)
1147 def test_uint64_param_from_c(self):
1148 obj = Regress.TestObj()
1150 def callback(obj, i):
1154 obj.callback_i = None
1156 obj.connect('sig-with-uint64-prop', callback)
1157 obj.emit_sig_with_uint64()
1158 self.assertEqual(obj.callback_i, GLib.MAXUINT64)
1160 def test_intarray_ret(self):
1161 obj = Regress.TestObj()
1163 def callback(obj, i):
1167 obj.callback_i = None
1170 obj.connect('sig-with-intarray-ret', callback)
1171 except TypeError as e:
1172 # compat with g-i 1.34.x
1173 if 'unknown signal' in str(e):
1177 rv = obj.emit('sig-with-intarray-ret', 42)
1178 self.assertEqual(obj.callback_i, 42)
1179 self.assertEqual(type(rv), GLib.Array)
1180 self.assertEqual(rv.len, 2)
1182 @unittest.skip('https://bugzilla.gnome.org/show_bug.cgi?id=669496')
1183 def test_array_parm(self):
1184 obj = Regress.TestObj()
1186 def callback(obj, arr):
1187 obj.callback_arr = arr
1189 obj.connect('sig-with-array-prop', callback)
1190 obj.callback_arr = None
1191 self.assertEqual(obj.emit('sig-with-array-prop', [1, 2, GLib.MAXUINT]), None)
1192 self.assertEqual(obj.callback_arr, [1, 2, GLib.MAXUINT])
1194 def test_held_struct_ref(self):
1197 def callback(obj, struct):
1198 # The struct held by Python will become a copy after this callback exits.
1199 struct.some_int = 42
1200 struct.some_int8 = 42
1201 held_structs.append(struct)
1203 struct = Regress.TestSimpleBoxedA()
1204 obj = Regress.TestObj()
1206 self.assertEqual(struct.some_int, 0)
1207 self.assertEqual(struct.some_int8, 0)
1209 obj.connect('test-with-static-scope-arg', callback)
1210 obj.emit('test-with-static-scope-arg', struct)
1212 # The held struct will be a copy of the modified struct.
1213 self.assertEqual(len(held_structs), 1)
1214 held_struct = held_structs[0]
1215 self.assertEqual(held_struct.some_int, 42)
1216 self.assertEqual(held_struct.some_int8, 42)
1218 # Boxed equality checks pointers by default.
1219 self.assertNotEqual(struct, held_struct)
1222 class _ConnectObjectTestBase(object):
1224 # - self.Object is overridden in sub-classes.
1225 # - Numeric suffixes indicate the number of user data args passed in.
1229 def run_connect_test(self, emit_args, user_data, flags=0):
1232 swap_obj = self.SwapObject()
1234 def callback(*args):
1235 callback_args.append(args)
1238 if flags & GObject.ConnectFlags.AFTER:
1239 connect_func = obj.connect_object_after
1241 connect_func = obj.connect_object
1243 with capture_gi_deprecation_warnings():
1244 connect_func('sig-with-int64-prop', callback, swap_obj, *user_data)
1245 obj.emit('sig-with-int64-prop', *emit_args)
1246 self.assertEqual(len(callback_args), 1)
1247 return callback_args[0]
1250 obj, value = self.run_connect_test([GLib.MAXINT64], user_data=[])
1251 self.assertIsInstance(obj, self.SwapObject)
1252 self.assertEqual(value, GLib.MAXINT64)
1255 obj, value, data = self.run_connect_test([GLib.MAXINT64],
1256 user_data=['mydata'])
1257 self.assertIsInstance(obj, self.SwapObject)
1258 self.assertEqual(value, GLib.MAXINT64)
1259 self.assertEqual(data, 'mydata')
1262 obj, value, data1, data2 = self.run_connect_test([GLib.MAXINT64],
1263 user_data=['mydata1', 'mydata2'])
1264 self.assertIsInstance(obj, self.SwapObject)
1265 self.assertEqual(value, GLib.MAXINT64)
1266 self.assertEqual(data1, 'mydata1')
1267 self.assertEqual(data2, 'mydata2')
1269 def test_after_0(self):
1270 obj, value = self.run_connect_test([GLib.MAXINT64],
1272 flags=GObject.ConnectFlags.AFTER)
1273 self.assertIsInstance(obj, self.SwapObject)
1274 self.assertEqual(value, GLib.MAXINT64)
1276 def test_after_1(self):
1277 obj, value, data = self.run_connect_test([GLib.MAXINT64],
1278 user_data=['mydata'],
1279 flags=GObject.ConnectFlags.AFTER)
1280 self.assertIsInstance(obj, self.SwapObject)
1281 self.assertEqual(value, GLib.MAXINT64)
1282 self.assertEqual(data, 'mydata')
1284 def test_after_2(self):
1285 obj, value, data1, data2 = self.run_connect_test([GLib.MAXINT64],
1286 user_data=['mydata1', 'mydata2'],
1287 flags=GObject.ConnectFlags.AFTER)
1288 self.assertIsInstance(obj, self.SwapObject)
1289 self.assertEqual(value, GLib.MAXINT64)
1290 self.assertEqual(data1, 'mydata1')
1291 self.assertEqual(data2, 'mydata2')
1294 class TestConnectGObjectNonIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1295 # This tests connect_object with non-introspected signals
1296 # (created in Python in this case).
1297 class Object(GObject.Object):
1298 test = GObject.Signal()
1299 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1300 arg_types=[GObject.TYPE_INT64],
1301 flags=GObject.SignalFlags.RUN_LAST)
1303 # Object passed for swapping is GObject based.
1304 class SwapObject(GObject.Object):
1308 class TestConnectGObjectIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1309 # This tests connect_object with introspected signals brought in from Regress.
1310 Object = Regress.TestObj
1312 # Object passed for swapping is GObject based.
1313 class SwapObject(GObject.Object):
1317 class TestConnectPyObjectNonIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1318 # This tests connect_object with non-introspected signals
1319 # (created in Python in this case).
1320 class Object(GObject.Object):
1321 test = GObject.Signal()
1322 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1323 arg_types=[GObject.TYPE_INT64],
1324 flags=GObject.SignalFlags.RUN_LAST)
1326 # Object passed for swapping is pure Python
1330 class TestConnectPyObjectIntrospected(unittest.TestCase, _ConnectObjectTestBase):
1331 # This tests connect_object with introspected signals brought in from Regress.
1332 Object = Regress.TestObj
1334 # Object passed for swapping is pure Python
1338 class _RefCountTestBase(object):
1339 # NOTE: ref counts are always one more than expected because the getrefcount()
1340 # function adds a ref for the input argument.
1342 # Sub-classes set this
1345 class PyData(object):
1348 def test_callback_ref_count_del(self):
1349 def callback(obj, value):
1352 callback_ref = weakref.ref(callback)
1353 self.assertEqual(sys.getrefcount(callback), 2)
1356 obj.connect('sig-with-int64-prop', callback)
1357 self.assertEqual(sys.getrefcount(callback), 3)
1360 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1362 res = obj.emit('sig-with-int64-prop', 42)
1363 self.assertEqual(res, 21)
1364 self.assertEqual(sys.getrefcount(callback_ref), 2)
1367 self.assertIsNone(callback_ref())
1369 def test_callback_ref_count_disconnect(self):
1370 def callback(obj, value):
1373 callback_ref = weakref.ref(callback)
1374 self.assertEqual(sys.getrefcount(callback), 2)
1377 handler_id = obj.connect('sig-with-int64-prop', callback)
1378 self.assertEqual(sys.getrefcount(callback), 3)
1381 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1383 res = obj.emit('sig-with-int64-prop', 42)
1384 self.assertEqual(res, 21)
1385 self.assertEqual(sys.getrefcount(callback_ref), 2)
1387 obj.disconnect(handler_id)
1388 self.assertIsNone(callback_ref())
1390 def test_callback_ref_count_disconnect_by_func(self):
1391 def callback(obj, value):
1394 callback_ref = weakref.ref(callback)
1395 self.assertEqual(sys.getrefcount(callback), 2)
1398 obj.connect('sig-with-int64-prop', callback)
1399 self.assertEqual(sys.getrefcount(callback), 3)
1402 self.assertEqual(sys.getrefcount(callback_ref()), 2)
1404 res = obj.emit('sig-with-int64-prop', 42)
1405 self.assertEqual(res, 21)
1406 self.assertEqual(sys.getrefcount(callback_ref), 2)
1408 obj.disconnect_by_func(callback_ref())
1409 self.assertIsNone(callback_ref())
1411 def test_user_data_ref_count(self):
1412 def callback(obj, value, data):
1415 data = self.PyData()
1416 data_ref = weakref.ref(data)
1417 self.assertEqual(sys.getrefcount(data), 2)
1420 obj.connect('sig-with-int64-prop', callback, data)
1421 self.assertEqual(sys.getrefcount(data), 3)
1424 self.assertEqual(sys.getrefcount(data_ref()), 2)
1426 res = obj.emit('sig-with-int64-prop', 42)
1427 self.assertEqual(res, 21)
1428 self.assertEqual(sys.getrefcount(data_ref()), 2)
1431 self.assertIsNone(data_ref())
1433 @unittest.expectedFailure # https://bugzilla.gnome.org/show_bug.cgi?id=688064
1434 def test_object_ref_count(self):
1435 # connect_object() should only weakly reference the object passed in
1436 # and auto-disconnect the signal when the object is destroyed.
1437 def callback(data, value):
1440 data = GObject.Object()
1441 data_ref = weakref.ref(data)
1442 self.assertEqual(sys.getrefcount(data), 2)
1445 handler_id = obj.connect_object('sig-with-int64-prop', callback, data)
1446 self.assertEqual(sys.getrefcount(data), 2)
1448 res = obj.emit('sig-with-int64-prop', 42)
1449 self.assertEqual(res, 21)
1450 self.assertEqual(sys.getrefcount(data), 2)
1454 self.assertIsNone(data_ref())
1455 self.assertFalse(obj.handler_is_connected(handler_id))
1458 class TestRefCountsNonIntrospected(unittest.TestCase, _RefCountTestBase):
1459 class Object(GObject.Object):
1460 sig_with_int64_prop = GObject.Signal(return_type=GObject.TYPE_INT64,
1461 arg_types=[GObject.TYPE_INT64],
1462 flags=GObject.SignalFlags.RUN_LAST)
1465 class TestRefCountsIntrospected(unittest.TestCase, _RefCountTestBase):
1466 Object = Regress.TestObj
1469 if __name__ == '__main__':