tests: don't test for specific device labels
[pygobject.git] / tests / test_gtk_template.py
blobf0cc963d8c2ab40b57b5f5a66467ad5577781355
1 # coding: UTF-8
3 from __future__ import absolute_import
5 import tempfile
6 import os
7 import pytest
9 Gtk = pytest.importorskip("gi.repository.Gtk")
10 GLib = pytest.importorskip("gi.repository.GLib")
11 GObject = pytest.importorskip("gi.repository.GObject")
12 Gio = pytest.importorskip("gi.repository.Gio")
15 from .helper import capture_exceptions
18 def new_gtype_name(_count=[0]):
19 _count[0] += 1
20 return "GtkTemplateTest%d" % _count[0]
23 def ensure_resource_registered():
24 resource_path = "/org/gnome/pygobject/test/a.ui"
26 def is_registered(path):
27 try:
28 Gio.resources_get_info(path, Gio.ResourceLookupFlags.NONE)
29 except GLib.Error:
30 return False
31 return True
33 if is_registered(resource_path):
34 return resource_path
36 gresource_data = (
37 b'GVariant\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00'
38 b'\xc8\x00\x00\x00\x00\x00\x00(\x06\x00\x00\x00\x00\x00\x00\x00'
39 b'\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00'
40 b'\x06\x00\x00\x00KP\x90\x0b\x03\x00\x00\x00\xc8\x00\x00\x00'
41 b'\x04\x00L\x00\xcc\x00\x00\x00\xd0\x00\x00\x00\xb0\xb7$0'
42 b'\x00\x00\x00\x00\xd0\x00\x00\x00\x06\x00L\x00\xd8\x00\x00\x00'
43 b'\xdc\x00\x00\x00f\xc30\xd1\x01\x00\x00\x00\xdc\x00\x00\x00'
44 b'\n\x00L\x00\xe8\x00\x00\x00\xec\x00\x00\x00\xd4\xb5\x02\x00'
45 b'\xff\xff\xff\xff\xec\x00\x00\x00\x01\x00L\x00\xf0\x00\x00\x00'
46 b'\xf4\x00\x00\x005H}\xe3\x02\x00\x00\x00\xf4\x00\x00\x00'
47 b'\x05\x00L\x00\xfc\x00\x00\x00\x00\x01\x00\x00\xa2^\xd6t'
48 b'\x04\x00\x00\x00\x00\x01\x00\x00\x04\x00v\x00\x08\x01\x00\x00'
49 b'\xa5\x01\x00\x00org/\x01\x00\x00\x00gnome/\x00\x00\x02\x00\x00\x00'
50 b'pygobject/\x00\x00\x04\x00\x00\x00/\x00\x00\x00\x00\x00\x00\x00'
51 b'test/\x00\x00\x00\x05\x00\x00\x00a.ui\x00\x00\x00\x00'
52 b'\x8d\x00\x00\x00\x00\x00\x00\x00<interface>\n <template class="G'
53 b'tkTemplateTestResource" parent="GtkBox">\n <property name="spaci'
54 b'ng">42</property>\n </template>\n</interface>\n\x00\x00(uuay)'
57 resource = Gio.Resource.new_from_data(GLib.Bytes.new(gresource_data))
58 Gio.resources_register(resource)
59 assert is_registered(resource_path)
60 return resource_path
63 def test_allow_init_template_call():
65 type_name = new_gtype_name()
67 xml = """\
68 <interface>
69 <template class="{0}" parent="GtkBox">
70 </template>
71 </interface>
72 """.format(type_name)
74 @Gtk.Template.from_string(xml)
75 class Foo(Gtk.Box):
76 __gtype_name__ = type_name
78 def __init__(self):
79 super(Foo, self).__init__()
80 self.init_template()
82 # Stop current pygobject from handling the initialisation
83 del Foo.__dontuse_ginstance_init__
85 Foo()
88 def test_init_template_second_instance():
89 type_name = new_gtype_name()
91 xml = """\
92 <interface>
93 <template class="{0}" parent="GtkBox">
94 <child>
95 <object class="GtkLabel" id="label">
96 </object>
97 </child>
98 </template>
99 </interface>
100 """.format(type_name)
102 @Gtk.Template.from_string(xml)
103 class Foo(Gtk.Box):
104 __gtype_name__ = type_name
106 label = Gtk.Template.Child("label")
108 def __init__(self):
109 super(Foo, self).__init__()
110 self.init_template()
112 # Stop current pygobject from handling the initialisation
113 del Foo.__dontuse_ginstance_init__
115 foo = Foo()
116 assert isinstance(foo.label, Gtk.Label)
118 foo2 = Foo()
119 assert isinstance(foo2.label, Gtk.Label)
122 def test_main_example():
124 type_name = new_gtype_name()
126 example_xml = """\
127 <interface>
128 <template class="{0}" parent="GtkBox">
129 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
130 <property name="spacing">4</property>
131 <child>
132 <object class="GtkButton" id="hello_button">
133 <property name="label">Hello World</property>
134 <signal name="clicked" handler="hello_button_clicked"
135 object="{0}" swapped="no"/>
136 <signal name="clicked" handler="hello_button_clicked_after"
137 object="{0}" swapped="no" after="yes"/>
138 </object>
139 </child>
140 <child>
141 <object class="GtkButton" id="goodbye_button">
142 <property name="label">Goodbye World</property>
143 <signal name="clicked" handler="goodbye_button_clicked"/>
144 <signal name="clicked" handler="goodbye_button_clicked_after"
145 after="yes"/>
146 </object>
147 </child>
148 </template>
149 </interface>
150 """.format(type_name)
152 @Gtk.Template.from_string(example_xml)
153 class Foo(Gtk.Box):
154 __gtype_name__ = type_name
156 def __init__(self):
157 super(Foo, self).__init__()
158 self.callback_hello = []
159 self.callback_hello_after = []
160 self.callback_goodbye = []
161 self.callback_goodbye_after = []
163 @Gtk.Template.Callback("hello_button_clicked")
164 def _hello_button_clicked(self, *args):
165 self.callback_hello.append(args)
167 @Gtk.Template.Callback("hello_button_clicked_after")
168 def _hello_after(self, *args):
169 self.callback_hello_after.append(args)
171 _hello_button = Gtk.Template.Child("hello_button")
173 goodbye_button = Gtk.Template.Child()
175 @Gtk.Template.Callback("goodbye_button_clicked")
176 def _goodbye_button_clicked(self, *args):
177 self.callback_goodbye.append(args)
179 @Gtk.Template.Callback("goodbye_button_clicked_after")
180 def _goodbye_after(self, *args):
181 self.callback_goodbye_after.append(args)
183 w = Foo()
184 assert w.__gtype__.name == type_name
185 assert w.props.orientation == Gtk.Orientation.HORIZONTAL
186 assert w.props.spacing == 4
187 assert isinstance(w._hello_button, Gtk.Button)
188 assert w._hello_button.props.label == "Hello World"
189 assert isinstance(w.goodbye_button, Gtk.Button)
190 assert w.goodbye_button.props.label == "Goodbye World"
192 assert w.callback_hello == []
193 w._hello_button.clicked()
194 assert w.callback_hello == [(w,)]
195 assert w.callback_hello_after == [(w,)]
197 assert w.callback_goodbye == []
198 w.goodbye_button.clicked()
199 assert w.callback_goodbye == [(w.goodbye_button,)]
200 assert w.callback_goodbye_after == [(w.goodbye_button,)]
203 def test_duplicate_handler():
205 type_name = new_gtype_name()
207 xml = """\
208 <interface>
209 <template class="{0}" parent="GtkBox">
210 <child>
211 <object class="GtkButton" id="hello_button">
212 <signal name="clicked" handler="hello_button_clicked">
213 </object>
214 </child>
215 </template>
216 </interface>
217 """.format(type_name)
219 class Foo(Gtk.Box):
220 __gtype_name__ = type_name
222 @Gtk.Template.Callback("hello_button_clicked")
223 def _hello_button_clicked(self, *args):
224 pass
226 @Gtk.Template.Callback()
227 def hello_button_clicked(self, *args):
228 pass
230 with pytest.raises(RuntimeError, match=".*hello_button_clicked.*"):
231 Gtk.Template.from_string(xml)(Foo)
234 def test_duplicate_child():
235 type_name = new_gtype_name()
237 xml = """\
238 <interface>
239 <template class="{0}" parent="GtkBox">
240 <child>
241 <object class="GtkButton" id="hello_button" />
242 </child>
243 </template>
244 </interface>
245 """.format(type_name)
247 class Foo(Gtk.Box):
248 __gtype_name__ = type_name
250 foo = Gtk.Template.Child("hello_button")
251 hello_button = Gtk.Template.Child()
253 with pytest.raises(RuntimeError, match=".*hello_button.*"):
254 Gtk.Template.from_string(xml)(Foo)
257 def test_nonexist_handler():
258 type_name = new_gtype_name()
260 xml = """\
261 <interface>
262 <template class="{0}" parent="GtkBox">
263 </template>
264 </interface>
265 """.format(type_name)
267 @Gtk.Template.from_string(xml)
268 class Foo(Gtk.Box):
269 __gtype_name__ = type_name
271 @Gtk.Template.Callback("nonexit")
272 def foo(self, *args):
273 pass
275 with capture_exceptions() as exc_info:
276 Foo()
277 assert "nonexit" in str(exc_info[0].value)
278 assert exc_info[0].type is RuntimeError
281 def test_missing_handler_callback():
282 type_name = new_gtype_name()
284 xml = """\
285 <interface>
286 <template class="{0}" parent="GtkBox">
287 <child>
288 <object class="GtkButton" id="hello_button">
289 <signal name="clicked" handler="i_am_not_used_in_python" />
290 </object>
291 </child>
292 </template>
293 </interface>
294 """.format(type_name)
296 class Foo(Gtk.Box):
297 __gtype_name__ = type_name
299 Gtk.Template.from_string(xml)(Foo)()
302 def test_handler_swapped_not_supported():
304 type_name = new_gtype_name()
306 xml = """\
307 <interface>
308 <template class="{0}" parent="GtkBox">
309 <child>
310 <object class="GtkButton" id="hello_button">
311 <signal name="clicked" handler="hello_button_clicked"
312 object="{0}" swapped="yes" />
313 </object>
314 </child>
315 </template>
316 </interface>
317 """.format(type_name)
319 @Gtk.Template.from_string(xml)
320 class Foo(Gtk.Box):
321 __gtype_name__ = type_name
323 hello_button = Gtk.Template.Child()
325 @Gtk.Template.Callback("hello_button_clicked")
326 def foo(self, *args):
327 pass
329 with capture_exceptions() as exc_info:
330 Foo()
331 assert "G_CONNECT_SWAPPED" in str(exc_info[0].value)
334 def test_handler_class_staticmethod():
336 type_name = new_gtype_name()
338 xml = """\
339 <interface>
340 <template class="{0}" parent="GtkBox">
341 <child>
342 <object class="GtkButton" id="hello_button">
343 <signal name="clicked" handler="clicked_class" />
344 <signal name="clicked" handler="clicked_static" />
345 </object>
346 </child>
347 </template>
348 </interface>
349 """.format(type_name)
351 signal_args_class = []
352 signal_args_static = []
354 @Gtk.Template.from_string(xml)
355 class Foo(Gtk.Box):
356 __gtype_name__ = type_name
358 hello_button = Gtk.Template.Child()
360 @Gtk.Template.Callback("clicked_class")
361 @classmethod
362 def cb1(*args):
363 signal_args_class.append(args)
365 @Gtk.Template.Callback("clicked_static")
366 @staticmethod
367 def cb2(*args):
368 signal_args_static.append(args)
370 foo = Foo()
371 foo.hello_button.clicked()
372 assert signal_args_class == [(Foo, foo.hello_button)]
373 assert signal_args_static == [(foo.hello_button,)]
376 def test_check_decorated_class():
378 NonWidget = type("Foo", (object,), {})
379 with pytest.raises(TypeError, match=".*on Widgets.*"):
380 Gtk.Template.from_string("")(NonWidget)
382 Widget = type("Foo", (Gtk.Widget,), {"__gtype_name__": new_gtype_name()})
383 with pytest.raises(TypeError, match=".*Cannot nest.*"):
384 Gtk.Template.from_string("")(Gtk.Template.from_string("")(Widget))
386 Widget = type("Foo", (Gtk.Widget,), {})
387 with pytest.raises(TypeError, match=".*__gtype_name__.*"):
388 Gtk.Template.from_string("")(Widget)
390 with pytest.raises(TypeError, match=".*on Widgets.*"):
391 Gtk.Template.from_string("")(object())
393 @Gtk.Template.from_string("")
394 class Base(Gtk.Widget):
395 __gtype_name__ = new_gtype_name()
397 with capture_exceptions() as exc_info:
398 type("Sub", (Base,), {})()
399 assert "not allowed at this time" in str(exc_info[0].value)
400 assert exc_info[0].type is TypeError
403 def test_from_file():
404 fd, name = tempfile.mkstemp()
405 try:
406 os.close(fd)
408 type_name = new_gtype_name()
410 with open(name, "wb") as h:
411 h.write(u"""\
412 <interface>
413 <template class="{0}" parent="GtkBox">
414 <property name="spacing">42</property>
415 </template>
416 </interface>
417 """.format(type_name).encode())
419 @Gtk.Template.from_file(name)
420 class Foo(Gtk.Box):
421 __gtype_name__ = type_name
423 foo = Foo()
424 assert foo.props.spacing == 42
425 finally:
426 os.remove(name)
429 def test_property_override():
430 type_name = new_gtype_name()
432 xml = """\
433 <interface>
434 <template class="{0}" parent="GtkBox">
435 <property name="spacing">42</property>
436 </template>
437 </interface>
438 """.format(type_name)
440 @Gtk.Template.from_string(xml)
441 class Foo(Gtk.Box):
442 __gtype_name__ = type_name
444 foo = Foo()
445 assert foo.props.spacing == 42
447 foo = Foo(spacing=124)
448 assert foo.props.spacing == 124
451 def test_from_file_non_exist():
452 dirname = tempfile.mkdtemp()
453 try:
454 path = os.path.join(dirname, "noexist")
456 Widget = type(
457 "Foo", (Gtk.Widget,), {"__gtype_name__": new_gtype_name()})
458 with pytest.raises(GLib.Error, match=".*No such file.*"):
459 Gtk.Template.from_file(path)(Widget)
460 finally:
461 os.rmdir(dirname)
464 def test_from_string_bytes():
465 type_name = new_gtype_name()
467 xml = u"""\
468 <interface>
469 <template class="{0}" parent="GtkBox">
470 <property name="spacing">42</property>
471 </template>
472 </interface>
473 """.format(type_name).encode()
475 @Gtk.Template.from_string(xml)
476 class Foo(Gtk.Box):
477 __gtype_name__ = type_name
479 foo = Foo()
480 assert foo.props.spacing == 42
483 def test_from_resource():
484 resource_path = ensure_resource_registered()
486 @Gtk.Template.from_resource(resource_path)
487 class Foo(Gtk.Box):
488 __gtype_name__ = "GtkTemplateTestResource"
490 foo = Foo()
491 assert foo.props.spacing == 42
494 def test_from_resource_non_exit():
495 Widget = type("Foo", (Gtk.Widget,), {"__gtype_name__": new_gtype_name()})
496 with pytest.raises(GLib.Error, match=".*/or/gnome/pygobject/noexit.*"):
497 Gtk.Template.from_resource("/or/gnome/pygobject/noexit")(Widget)
500 def test_constructors():
501 with pytest.raises(TypeError):
502 Gtk.Template()
504 with pytest.raises(TypeError):
505 Gtk.Template(foo=1)
507 Gtk.Template(filename="foo")
508 Gtk.Template(resource_path="foo")
509 Gtk.Template(string="foo")
511 with pytest.raises(TypeError):
512 Gtk.Template(filename="foo", resource_path="bar")
514 with pytest.raises(TypeError):
515 Gtk.Template(filename="foo", nope="bar")
517 Gtk.Template.from_string("bla")
518 Gtk.Template.from_resource("foo")
519 Gtk.Template.from_file("foo")
522 def test_child_construct():
523 Gtk.Template.Child()
524 Gtk.Template.Child("name")
525 with pytest.raises(TypeError):
526 Gtk.Template.Child("name", True)
527 Gtk.Template.Child("name", internal=True)
528 with pytest.raises(TypeError):
529 Gtk.Template.Child("name", internal=True, something=False)
532 def test_internal_child():
534 main_type_name = new_gtype_name()
536 xml = """\
537 <interface>
538 <template class="{0}" parent="GtkBox">
539 <child>
540 <object class="GtkBox" id="somechild">
541 <property name="margin">42</property>
542 </object>
543 </child>
544 </template>
545 </interface>
546 """.format(main_type_name)
548 @Gtk.Template.from_string(xml)
549 class MainThing(Gtk.Box):
550 __gtype_name__ = main_type_name
552 somechild = Gtk.Template.Child(internal=True)
554 thing = MainThing()
555 assert thing.somechild.props.margin == 42
557 other_type_name = new_gtype_name()
559 xml = """\
560 <interface>
561 <template class="{0}" parent="GtkBox">
562 <child>
563 <object class="{1}">
564 <child internal-child="somechild">
565 <object class="GtkBox">
566 <property name="margin">24</property>
567 <child>
568 <object class="GtkLabel">
569 <property name="label">foo</property>
570 </object>
571 </child>
572 </object>
573 </child>
574 </object>
575 </child>
576 </template>
577 </interface>
578 """.format(other_type_name, main_type_name)
580 @Gtk.Template.from_string(xml)
581 class OtherThing(Gtk.Box):
582 __gtype_name__ = other_type_name
584 other = OtherThing()
585 child = other.get_children()[0]
586 assert isinstance(child, MainThing)
587 child = child.get_children()[0]
588 assert isinstance(child, Gtk.Box)
589 assert child.props.margin == 24
590 child = child.get_children()[0]
591 assert isinstance(child, Gtk.Label)
592 assert child.props.label == "foo"