diff options
author | TizenOpenSource <tizenopensrc@samsung.com> | 2023-12-08 13:16:12 +0900 |
---|---|---|
committer | TizenOpenSource <tizenopensrc@samsung.com> | 2023-12-08 13:16:12 +0900 |
commit | 454aa52f950f2348756dfffc9c26fb74d4aa3bef (patch) | |
tree | 4e8be3f8c8218dbafe65e22dfc73e9351c1d5bbb /tests/test_gtk_template.py | |
parent | f3eae5a895fc60cb99c0c366bdd011018ce3bc7b (diff) | |
download | pygobject2-454aa52f950f2348756dfffc9c26fb74d4aa3bef.tar.gz pygobject2-454aa52f950f2348756dfffc9c26fb74d4aa3bef.tar.bz2 pygobject2-454aa52f950f2348756dfffc9c26fb74d4aa3bef.zip |
Imported Upstream version 3.46.0upstream/3.46.0
Diffstat (limited to 'tests/test_gtk_template.py')
-rw-r--r-- | tests/test_gtk_template.py | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/tests/test_gtk_template.py b/tests/test_gtk_template.py new file mode 100644 index 0000000..9dd7a90 --- /dev/null +++ b/tests/test_gtk_template.py @@ -0,0 +1,721 @@ +import tempfile +import os +import pytest + +Gtk = pytest.importorskip("gi.repository.Gtk") +GLib = pytest.importorskip("gi.repository.GLib") +GObject = pytest.importorskip("gi.repository.GObject") +Gio = pytest.importorskip("gi.repository.Gio") + +from .helper import capture_exceptions + +GTK4 = (Gtk._version == "4.0") + + +def new_gtype_name(_count=[0]): + _count[0] += 1 + return "GtkTemplateTest%d" % _count[0] + + +def ensure_resource_registered(): + resource_path = "/org/gnome/pygobject/test/a.ui" + + def is_registered(path): + try: + Gio.resources_get_info(path, Gio.ResourceLookupFlags.NONE) + except GLib.Error: + return False + return True + + if is_registered(resource_path): + return resource_path + + gresource_data = ( + b'GVariant\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00' + b'\xc8\x00\x00\x00\x00\x00\x00(\x06\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00' + b'\x06\x00\x00\x00KP\x90\x0b\x03\x00\x00\x00\xc8\x00\x00\x00' + b'\x04\x00L\x00\xcc\x00\x00\x00\xd0\x00\x00\x00\xb0\xb7$0' + b'\x00\x00\x00\x00\xd0\x00\x00\x00\x06\x00L\x00\xd8\x00\x00\x00' + b'\xdc\x00\x00\x00f\xc30\xd1\x01\x00\x00\x00\xdc\x00\x00\x00' + b'\n\x00L\x00\xe8\x00\x00\x00\xec\x00\x00\x00\xd4\xb5\x02\x00' + b'\xff\xff\xff\xff\xec\x00\x00\x00\x01\x00L\x00\xf0\x00\x00\x00' + b'\xf4\x00\x00\x005H}\xe3\x02\x00\x00\x00\xf4\x00\x00\x00' + b'\x05\x00L\x00\xfc\x00\x00\x00\x00\x01\x00\x00\xa2^\xd6t' + b'\x04\x00\x00\x00\x00\x01\x00\x00\x04\x00v\x00\x08\x01\x00\x00' + b'\xa5\x01\x00\x00org/\x01\x00\x00\x00gnome/\x00\x00\x02\x00\x00\x00' + b'pygobject/\x00\x00\x04\x00\x00\x00/\x00\x00\x00\x00\x00\x00\x00' + b'test/\x00\x00\x00\x05\x00\x00\x00a.ui\x00\x00\x00\x00' + b'\x8d\x00\x00\x00\x00\x00\x00\x00<interface>\n <template class="G' + b'tkTemplateTestResource" parent="GtkBox">\n <property name="spaci' + b'ng">42</property>\n </template>\n</interface>\n\x00\x00(uuay)' + ) + + resource = Gio.Resource.new_from_data(GLib.Bytes.new(gresource_data)) + Gio.resources_register(resource) + assert is_registered(resource_path) + return resource_path + + +def test_allow_init_template_call(): + + type_name = new_gtype_name() + + xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + </template> +</interface> +""".format(type_name) + + @Gtk.Template.from_string(xml) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + def __init__(self): + super(Foo, self).__init__() + self.init_template() + + # Stop current pygobject from handling the initialisation + del Foo.__dontuse_ginstance_init__ + + Foo() + + +def test_init_template_second_instance(): + type_name = new_gtype_name() + + xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + <child> + <object class="GtkLabel" id="label"> + </object> + </child> + </template> +</interface> +""".format(type_name) + + @Gtk.Template.from_string(xml) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + label = Gtk.Template.Child("label") + + def __init__(self): + super(Foo, self).__init__() + self.init_template() + + # Stop current pygobject from handling the initialisation + del Foo.__dontuse_ginstance_init__ + + foo = Foo() + assert isinstance(foo.label, Gtk.Label) + + foo2 = Foo() + assert isinstance(foo2.label, Gtk.Label) + + +def test_main_example(): + + type_name = new_gtype_name() + + example_xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property> + <property name="spacing">4</property> + <child> + <object class="GtkButton" id="hello_button"> + <property name="label">Hello World</property> + <signal name="clicked" handler="hello_button_clicked" + object="{0}" swapped="no"/> + <signal name="clicked" handler="hello_button_clicked_after" + object="{0}" swapped="no" after="yes"/> + </object> + </child> + <child> + <object class="GtkButton" id="goodbye_button"> + <property name="label">Goodbye World</property> + <signal name="clicked" handler="goodbye_button_clicked"/> + <signal name="clicked" handler="goodbye_button_clicked_after" + after="yes"/> + </object> + </child> + </template> +</interface> +""".format(type_name) + + @Gtk.Template.from_string(example_xml) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + def __init__(self): + super(Foo, self).__init__() + self.callback_hello = [] + self.callback_hello_after = [] + self.callback_goodbye = [] + self.callback_goodbye_after = [] + + @Gtk.Template.Callback("hello_button_clicked") + def _hello_button_clicked(self, *args): + self.callback_hello.append(args) + + @Gtk.Template.Callback("hello_button_clicked_after") + def _hello_after(self, *args): + self.callback_hello_after.append(args) + + _hello_button = Gtk.Template.Child("hello_button") + + goodbye_button = Gtk.Template.Child() + + @Gtk.Template.Callback("goodbye_button_clicked") + def _goodbye_button_clicked(self, *args): + self.callback_goodbye.append(args) + + @Gtk.Template.Callback("goodbye_button_clicked_after") + def _goodbye_after(self, *args): + self.callback_goodbye_after.append(args) + + w = Foo() + assert w.__gtype__.name == type_name + assert w.props.orientation == Gtk.Orientation.HORIZONTAL + assert w.props.spacing == 4 + assert isinstance(w._hello_button, Gtk.Button) + assert w._hello_button.props.label == "Hello World" + assert isinstance(w.goodbye_button, Gtk.Button) + assert w.goodbye_button.props.label == "Goodbye World" + + assert w.callback_hello == [] + w._hello_button.emit("clicked") + assert w.callback_hello == [(w,)] + assert w.callback_hello_after == [(w,)] + + assert w.callback_goodbye == [] + w.goodbye_button.emit("clicked") + assert w.callback_goodbye == [(w.goodbye_button,)] + assert w.callback_goodbye_after == [(w.goodbye_button,)] + + +def test_duplicate_handler(): + type_name = new_gtype_name() + + xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + <child> + <object class="GtkButton" id="hello_button"> + <signal name="clicked" handler="hello_button_clicked" /> + </object> + </child> + </template> +</interface> +""".format(type_name) + + class Foo(Gtk.Box): + __gtype_name__ = type_name + + @Gtk.Template.Callback("hello_button_clicked") + def _hello_button_clicked(self, *args): + pass + + @Gtk.Template.Callback() + def hello_button_clicked(self, *args): + pass + + with pytest.raises(RuntimeError, match=".*hello_button_clicked.*"): + Gtk.Template.from_string(xml)(Foo) + + +def test_duplicate_child(): + type_name = new_gtype_name() + + xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + <child> + <object class="GtkButton" id="hello_button" /> + </child> + </template> +</interface> +""".format(type_name) + + class Foo(Gtk.Box): + __gtype_name__ = type_name + + foo = Gtk.Template.Child("hello_button") + hello_button = Gtk.Template.Child() + + with pytest.raises(RuntimeError, match=".*hello_button.*"): + Gtk.Template.from_string(xml)(Foo) + + +def test_nonexist_handler(): + type_name = new_gtype_name() + + xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + </template> +</interface> +""".format(type_name) + + @Gtk.Template.from_string(xml) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + @Gtk.Template.Callback("nonexit") + def foo(self, *args): + pass + + with capture_exceptions() as exc_info: + Foo() + assert "nonexit" in str(exc_info[0].value) + assert exc_info[0].type is RuntimeError + + +def test_missing_handler_callback(): + type_name = new_gtype_name() + + xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + <child> + <object class="GtkButton" id="hello_button"> + <signal name="clicked" handler="i_am_not_used_in_python" /> + </object> + </child> + </template> +</interface> +""".format(type_name) + + class Foo(Gtk.Box): + __gtype_name__ = type_name + + Gtk.Template.from_string(xml)(Foo)() + + +def test_handler_swapped_not_supported(): + type_name = new_gtype_name() + + xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + <child> + <object class="GtkButton" id="hello_button"> + <signal name="clicked" handler="hello_button_clicked" + object="{0}" swapped="yes" /> + </object> + </child> + </template> +</interface> +""".format(type_name) + + @Gtk.Template.from_string(xml) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + hello_button = Gtk.Template.Child() + + @Gtk.Template.Callback("hello_button_clicked") + def foo(self, *args): + pass + + with capture_exceptions() as exc_info: + Foo() + assert "G_CONNECT_SWAPPED" in str(exc_info[0].value) + + +def test_handler_class_staticmethod(): + type_name = new_gtype_name() + + xml = """\ +<interface> + <template class="{0}" parent="GtkBox"> + <child> + <object class="GtkButton" id="hello_button"> + <signal name="clicked" handler="clicked_class" /> + <signal name="clicked" handler="clicked_static" /> + </object> + </child> + </template> +</interface> +""".format(type_name) + + signal_args_class = [] + signal_args_static = [] + + @Gtk.Template.from_string(xml) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + hello_button = Gtk.Template.Child() + + @Gtk.Template.Callback("clicked_class") + @classmethod + def cb1(*args): + signal_args_class.append(args) + + @Gtk.Template.Callback("clicked_static") + @staticmethod + def cb2(*args): + signal_args_static.append(args) + + foo = Foo() + foo.hello_button.emit("clicked") + assert signal_args_class == [(Foo, foo.hello_button)] + assert signal_args_static == [(foo.hello_button,)] + + +@pytest.mark.skipif(Gtk._version == "4.0", reason="errors out first with gtk4") +def test_check_decorated_class(): + NonWidget = type("Foo", (object,), {}) + with pytest.raises(TypeError, match=".*on Widgets.*"): + Gtk.Template.from_string("")(NonWidget) + + Widget = type("Foo", (Gtk.Widget,), {"__gtype_name__": new_gtype_name()}) + with pytest.raises(TypeError, match=".*Cannot nest.*"): + Gtk.Template.from_string("")(Gtk.Template.from_string("")(Widget)) + + Widget = type("Foo", (Gtk.Widget,), {}) + with pytest.raises(TypeError, match=".*__gtype_name__.*"): + Gtk.Template.from_string("")(Widget) + + with pytest.raises(TypeError, match=".*on Widgets.*"): + Gtk.Template.from_string("")(object()) + + +@pytest.mark.skipif(Gtk._version == "4.0", reason="errors out first with gtk4") +def test_subclass_fail(): + @Gtk.Template.from_string("") + class Base(Gtk.Widget): + __gtype_name__ = new_gtype_name() + + with capture_exceptions() as exc_info: + type("Sub", (Base,), {})() + assert "not allowed at this time" in str(exc_info[0].value) + assert exc_info[0].type is TypeError + + +def test_from_file(): + fd, name = tempfile.mkstemp() + try: + os.close(fd) + + type_name = new_gtype_name() + + with open(name, "wb") as h: + h.write(u"""\ + <interface> + <template class="{0}" parent="GtkBox"> + <property name="spacing">42</property> + </template> + </interface> + """.format(type_name).encode()) + + @Gtk.Template.from_file(name) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + foo = Foo() + assert foo.props.spacing == 42 + finally: + os.remove(name) + + +def test_property_override(): + type_name = new_gtype_name() + + xml = """\ + <interface> + <template class="{0}" parent="GtkBox"> + <property name="spacing">42</property> + </template> + </interface> +""".format(type_name) + + @Gtk.Template.from_string(xml) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + foo = Foo() + assert foo.props.spacing == 42 + + foo = Foo(spacing=124) + assert foo.props.spacing == 124 + + +def test_from_file_non_exist(): + dirname = tempfile.mkdtemp() + try: + path = os.path.join(dirname, "noexist") + + Widget = type( + "Foo", (Gtk.Widget,), {"__gtype_name__": new_gtype_name()}) + with pytest.raises(GLib.Error, match=".*No such file.*"): + Gtk.Template.from_file(path)(Widget) + finally: + os.rmdir(dirname) + + +def test_from_string_bytes(): + type_name = new_gtype_name() + + xml = u"""\ + <interface> + <template class="{0}" parent="GtkBox"> + <property name="spacing">42</property> + </template> + </interface> + """.format(type_name).encode() + + @Gtk.Template.from_string(xml) + class Foo(Gtk.Box): + __gtype_name__ = type_name + + foo = Foo() + assert foo.props.spacing == 42 + + +def test_from_resource(): + resource_path = ensure_resource_registered() + + @Gtk.Template.from_resource(resource_path) + class Foo(Gtk.Box): + __gtype_name__ = "GtkTemplateTestResource" + + foo = Foo() + assert foo.props.spacing == 42 + + +def test_from_resource_non_exit(): + Widget = type("Foo", (Gtk.Widget,), {"__gtype_name__": new_gtype_name()}) + with pytest.raises(GLib.Error, match=".*/or/gnome/pygobject/noexit.*"): + Gtk.Template.from_resource("/or/gnome/pygobject/noexit")(Widget) + + +def test_constructors(): + with pytest.raises(TypeError): + Gtk.Template() + + with pytest.raises(TypeError): + Gtk.Template(foo=1) + + Gtk.Template(filename="foo") + Gtk.Template(resource_path="foo") + Gtk.Template(string="foo") + + with pytest.raises(TypeError): + Gtk.Template(filename="foo", resource_path="bar") + + with pytest.raises(TypeError): + Gtk.Template(filename="foo", nope="bar") + + Gtk.Template.from_string("bla") + Gtk.Template.from_resource("foo") + Gtk.Template.from_file("foo") + + +def test_child_construct(): + Gtk.Template.Child() + Gtk.Template.Child("name") + with pytest.raises(TypeError): + Gtk.Template.Child("name", True) + Gtk.Template.Child("name", internal=True) + with pytest.raises(TypeError): + Gtk.Template.Child("name", internal=True, something=False) + + +def test_internal_child(): + + main_type_name = new_gtype_name() + + xml = """\ + <interface> + <template class="{0}" parent="GtkBox"> + <child> + <object class="GtkBox" id="somechild"> + <property name="margin-top">42</property> + </object> + </child> + </template> + </interface> + """.format(main_type_name) + + @Gtk.Template.from_string(xml) + class MainThing(Gtk.Box): + __gtype_name__ = main_type_name + + somechild = Gtk.Template.Child(internal=True) + + thing = MainThing() + assert thing.somechild.props.margin_top == 42 + + other_type_name = new_gtype_name() + + xml = """\ + <interface> + <template class="{0}" parent="GtkBox"> + <child> + <object class="{1}"> + <child internal-child="somechild"> + <object class="GtkBox"> + <property name="margin-top">24</property> + <child> + <object class="GtkLabel"> + <property name="label">foo</property> + </object> + </child> + </object> + </child> + </object> + </child> + </template> + </interface> + """.format(other_type_name, main_type_name) + + @Gtk.Template.from_string(xml) + class OtherThing(Gtk.Box): + __gtype_name__ = other_type_name + + other = OtherThing() + if not GTK4: + child = other.get_children()[0] + else: + child = other.get_first_child() + + assert isinstance(child, MainThing) + + if not GTK4: + child = child.get_children()[0] + else: + child = child.get_first_child() + + assert isinstance(child, Gtk.Box) + assert child.props.margin_top == 24 + + if not GTK4: + child = child.get_children()[0] + else: + child = child.get_first_child() + + assert isinstance(child, Gtk.Label) + assert child.props.label == "foo" + + +def test_template_hierarchy(): + testlabel = """ + <interface> + <template class="TestLabel" parent="GtkLabel"> + </template> + </interface> + """ + + @Gtk.Template(string=testlabel) + class TestLabel(Gtk.Label): + + __gtype_name__ = "TestLabel" + + def __init__(self): + super().__init__() + self.props.label = "TestLabel" + + testbox = """ + <interface> + <template class="TestBox" parent="GtkBox"> + <child> + <object class="TestLabel" id="_testlabel"/> + </child> + </template> + </interface> + """ + + @Gtk.Template(string=testbox) + class TestBox(Gtk.Box): + + __gtype_name__ = "TestBox" + + _testlabel = Gtk.Template.Child() + + def __init__(self): + super().__init__() + + assert isinstance(self._testlabel, TestLabel) + + window = """ + <interface> + <template class="MyWindow" parent="GtkWindow"> + <property name="title">"Hellow World"</property> + <child> + <object class="TestBox" id="_testbox"> + <child> + <object class="TestLabel" id="_testlabel"/> + </child> + </object> + </child> + </template> + </interface> + """ + + @Gtk.Template(string=window) + class MyWindow(Gtk.Window): + + __gtype_name__ = "MyWindow" + + _testbox = Gtk.Template.Child() + _testlabel = Gtk.Template.Child() + + def __init__(self): + super().__init__() + + assert isinstance(self._testbox, TestBox) + assert isinstance(self._testlabel, TestLabel) + if not GTK4: + children = self._testbox.get_children() + else: + children = list(self._testbox) + + assert len(children) == 2 + + win = MyWindow() + assert isinstance(win, MyWindow) + + +def test_multiple_init_template_calls(): + xml = """ + <interface> + <template class="MyBox" parent="GtkBox"> + <child> + <object class="GtkLabel" id="_label"/> + </child> + </template> + </interface> + """ + + @Gtk.Template(string=xml) + class MyBox(Gtk.Box): + + __gtype_name__ = "MyBox" + + _label = Gtk.Template.Child() + + def __init__(self): + super().__init__() + self._label.props.label = "awesome label" + + my_box = MyBox() + assert isinstance(my_box, MyBox) + if not GTK4: + children = my_box.get_children() + else: + children = list(my_box) + + assert len(children) == 1 + my_box.init_template() + assert isinstance(my_box, MyBox) + if not GTK4: + children = my_box.get_children() + else: + children = list(my_box) + + assert len(children) == 1 |