2 # -*- coding: utf-8 -*-
4 # Copyright © 2019 Endless Mobile, Inc.
6 # SPDX-License-Identifier: LGPL-2.1-or-later
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23 """Integration tests for glib-genmarshal utility."""
31 from textwrap import dedent
37 # Disable line length warnings as wrapping the C code templates would be hard
41 Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
44 class TestGenmarshal(unittest.TestCase):
45 """Integration test for running glib-genmarshal.
47 This can be run when installed or uninstalled. When uninstalled, it
48 requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
50 The idea with this test harness is to test the glib-genmarshal utility, its
51 handling of command line arguments, its exit statuses, and its handling of
52 various marshaller lists. In future we could split the core glib-genmarshal
53 parsing and generation code out into a library and unit test that, and
54 convert this test to just check command line behaviour.
57 # Track the cwd, we want to back out to that to clean up our tempdir
61 self.timeout_seconds = 10 # seconds per test
62 self.tmpdir = tempfile.TemporaryDirectory()
63 self.cwd = os.getcwd()
64 os.chdir(self.tmpdir.name)
65 print("tmpdir:", self.tmpdir.name)
66 if "G_TEST_BUILDDIR" in os.environ:
67 self.__genmarshal = os.path.join(
68 os.environ["G_TEST_BUILDDIR"], "..", "glib-genmarshal"
71 self.__genmarshal = shutil.which("glib-genmarshal")
72 print("genmarshal:", self.__genmarshal)
78 def runGenmarshal(self, *args):
79 argv = [self.__genmarshal]
81 # shebang lines are not supported on native
84 argv.insert(0, sys.executable)
87 print("Running:", argv)
89 env = os.environ.copy()
90 env["LC_ALL"] = "C.UTF-8"
91 print("Environment:", env)
93 # We want to ensure consistent line endings...
94 info = subprocess.run(
96 timeout=self.timeout_seconds,
97 stdout=subprocess.PIPE,
98 stderr=subprocess.PIPE,
100 universal_newlines=True,
102 info.check_returncode()
103 out = info.stdout.strip()
104 err = info.stderr.strip()
106 # Known substitutions for standard boilerplate
108 "standard_top_comment": "This file is generated by glib-genmarshal, do not modify "
109 "it. This code is licensed under the same license as the "
110 "containing project. Note that it links to GLib, so must "
111 "comply with the LGPL linking clauses.",
112 "standard_top_pragma": dedent(
114 #ifndef __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
115 #define __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
118 "standard_bottom_pragma": dedent(
120 #endif /* __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__ */
123 "standard_includes": dedent(
125 #include <glib-object.h>
128 "standard_marshal_peek_defines": dedent(
130 #ifdef G_ENABLE_DEBUG
131 #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
132 #define g_marshal_value_peek_char(v) g_value_get_schar (v)
133 #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
134 #define g_marshal_value_peek_int(v) g_value_get_int (v)
135 #define g_marshal_value_peek_uint(v) g_value_get_uint (v)
136 #define g_marshal_value_peek_long(v) g_value_get_long (v)
137 #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
138 #define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
139 #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
140 #define g_marshal_value_peek_enum(v) g_value_get_enum (v)
141 #define g_marshal_value_peek_flags(v) g_value_get_flags (v)
142 #define g_marshal_value_peek_float(v) g_value_get_float (v)
143 #define g_marshal_value_peek_double(v) g_value_get_double (v)
144 #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
145 #define g_marshal_value_peek_param(v) g_value_get_param (v)
146 #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
147 #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
148 #define g_marshal_value_peek_object(v) g_value_get_object (v)
149 #define g_marshal_value_peek_variant(v) g_value_get_variant (v)
150 #else /* !G_ENABLE_DEBUG */
151 /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
152 * Do not access GValues directly in your code. Instead, use the
153 * g_value_get_*() functions
155 #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
156 #define g_marshal_value_peek_char(v) (v)->data[0].v_int
157 #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
158 #define g_marshal_value_peek_int(v) (v)->data[0].v_int
159 #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
160 #define g_marshal_value_peek_long(v) (v)->data[0].v_long
161 #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
162 #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
163 #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
164 #define g_marshal_value_peek_enum(v) (v)->data[0].v_long
165 #define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
166 #define g_marshal_value_peek_float(v) (v)->data[0].v_float
167 #define g_marshal_value_peek_double(v) (v)->data[0].v_double
168 #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
169 #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
170 #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
171 #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
172 #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
173 #define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
174 #endif /* !G_ENABLE_DEBUG */
179 result = Result(info, out, err, subs)
181 print("Output:", result.out)
184 def runGenmarshalWithList(self, list_contents, *args):
185 with tempfile.NamedTemporaryFile(
186 dir=self.tmpdir.name, suffix=".list", delete=False
188 # Write out the list.
189 list_file.write(list_contents.encode("utf-8"))
190 print(list_file.name + ":", list_contents)
193 header_result = self.runGenmarshal(list_file.name, "--header", *args)
194 body_result = self.runGenmarshal(list_file.name, "--body", *args)
196 header_result.subs["list_path"] = list_file.name
197 body_result.subs["list_path"] = list_file.name
199 return (header_result, body_result)
202 """Test the --help argument."""
203 result = self.runGenmarshal("--help")
204 self.assertIn("usage: glib-genmarshal", result.out)
206 def test_no_args(self):
207 """Test running with no arguments at all."""
208 result = self.runGenmarshal()
209 self.assertEqual("", result.err)
210 self.assertEqual("", result.out)
212 def test_empty_list(self):
213 """Test running with an empty list."""
214 (header_result, body_result) = self.runGenmarshalWithList("", "--quiet")
216 self.assertEqual("", header_result.err)
217 self.assertEqual("", body_result.err)
222 /* {standard_top_comment} */
223 {standard_top_pragma}
232 {standard_bottom_pragma}
236 .format(**header_result.subs),
237 header_result.out.strip(),
243 /* {standard_top_comment} */
246 {standard_marshal_peek_defines}
250 .format(**body_result.subs),
251 body_result.out.strip(),
254 def test_void_boolean(self):
255 """Test running with a basic VOID:BOOLEAN list."""
256 (header_result, body_result) = self.runGenmarshalWithList(
257 "VOID:BOOLEAN", "--quiet"
260 self.assertEqual("", header_result.err)
261 self.assertEqual("", body_result.err)
266 /* {standard_top_comment} */
267 {standard_top_pragma}
273 /* VOID:BOOLEAN ({list_path}:1) */
274 #define g_cclosure_user_marshal_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN
279 {standard_bottom_pragma}
283 .format(**header_result.subs),
284 header_result.out.strip(),
290 /* {standard_top_comment} */
293 {standard_marshal_peek_defines}
297 .format(**body_result.subs),
298 body_result.out.strip(),
301 def test_void_boolean_int64(self):
302 """Test running with a non-trivial VOID:BOOLEAN,INT64 list."""
303 (header_result, body_result) = self.runGenmarshalWithList(
304 "VOID:BOOLEAN,INT64", "--quiet"
307 self.assertEqual("", header_result.err)
308 self.assertEqual("", body_result.err)
313 /* {standard_top_comment} */
314 {standard_top_pragma}
320 /* VOID:BOOLEAN,INT64 ({list_path}:1) */
322 void g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure *closure,
323 GValue *return_value,
324 guint n_param_values,
325 const GValue *param_values,
326 gpointer invocation_hint,
327 gpointer marshal_data);
332 {standard_bottom_pragma}
336 .format(**header_result.subs),
337 header_result.out.strip(),
343 /* {standard_top_comment} */
346 {standard_marshal_peek_defines}
348 /* VOID:BOOLEAN,INT64 ({list_path}:1) */
350 g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure *closure,
351 GValue *return_value G_GNUC_UNUSED,
352 guint n_param_values,
353 const GValue *param_values,
354 gpointer invocation_hint G_GNUC_UNUSED,
355 gpointer marshal_data)
357 typedef void (*GMarshalFunc_VOID__BOOLEAN_INT64) (gpointer data1,
361 GCClosure *cc = (GCClosure *) closure;
362 gpointer data1, data2;
363 GMarshalFunc_VOID__BOOLEAN_INT64 callback;
365 g_return_if_fail (n_param_values == 3);
367 if (G_CCLOSURE_SWAP_DATA (closure))
369 data1 = closure->data;
370 data2 = g_value_peek_pointer (param_values + 0);
374 data1 = g_value_peek_pointer (param_values + 0);
375 data2 = closure->data;
377 callback = (GMarshalFunc_VOID__BOOLEAN_INT64) (marshal_data ? marshal_data : cc->callback);
380 g_marshal_value_peek_boolean (param_values + 1),
381 g_marshal_value_peek_int64 (param_values + 2),
387 .format(**body_result.subs),
388 body_result.out.strip(),
391 def test_void_variant_nostdinc_valist_marshaller(self):
392 """Test running with a basic VOID:VARIANT list, but without the
393 standard marshallers, and with valist support enabled. This checks that
394 the valist marshaller for VARIANT correctly sinks floating variants.
398 (header_result, body_result) = self.runGenmarshalWithList(
399 "VOID:VARIANT", "--quiet", "--nostdinc", "--valist-marshaller"
402 self.assertEqual("", header_result.err)
403 self.assertEqual("", body_result.err)
408 /* {standard_top_comment} */
409 {standard_top_pragma}
413 /* VOID:VARIANT ({list_path}:1) */
415 void g_cclosure_user_marshal_VOID__VARIANT (GClosure *closure,
416 GValue *return_value,
417 guint n_param_values,
418 const GValue *param_values,
419 gpointer invocation_hint,
420 gpointer marshal_data);
422 void g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
423 GValue *return_value,
426 gpointer marshal_data,
433 {standard_bottom_pragma}
437 .format(**header_result.subs),
438 header_result.out.strip(),
444 /* {standard_top_comment} */
445 {standard_marshal_peek_defines}
447 /* VOID:VARIANT ({list_path}:1) */
449 g_cclosure_user_marshal_VOID__VARIANT (GClosure *closure,
450 GValue *return_value G_GNUC_UNUSED,
451 guint n_param_values,
452 const GValue *param_values,
453 gpointer invocation_hint G_GNUC_UNUSED,
454 gpointer marshal_data)
456 typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
459 GCClosure *cc = (GCClosure *) closure;
460 gpointer data1, data2;
461 GMarshalFunc_VOID__VARIANT callback;
463 g_return_if_fail (n_param_values == 2);
465 if (G_CCLOSURE_SWAP_DATA (closure))
467 data1 = closure->data;
468 data2 = g_value_peek_pointer (param_values + 0);
472 data1 = g_value_peek_pointer (param_values + 0);
473 data2 = closure->data;
475 callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
478 g_marshal_value_peek_variant (param_values + 1),
483 g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
484 GValue *return_value G_GNUC_UNUSED,
487 gpointer marshal_data,
491 typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
494 GCClosure *cc = (GCClosure *) closure;
495 gpointer data1, data2;
496 GMarshalFunc_VOID__VARIANT callback;
500 va_copy (args_copy, args);
501 arg0 = (gpointer) va_arg (args_copy, gpointer);
502 if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
503 arg0 = g_variant_ref_sink (arg0);
507 if (G_CCLOSURE_SWAP_DATA (closure))
509 data1 = closure->data;
515 data2 = closure->data;
517 callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
522 if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
523 g_variant_unref (arg0);
528 .format(**body_result.subs),
529 body_result.out.strip(),
532 def test_void_string_nostdinc(self):
533 """Test running with a basic VOID:STRING list, but without the
534 standard marshallers, and with valist support enabled. This checks that
535 the valist marshaller for STRING correctly skips a string copy if the
540 (header_result, body_result) = self.runGenmarshalWithList(
541 "VOID:STRING", "--quiet", "--nostdinc", "--valist-marshaller"
544 self.assertEqual("", header_result.err)
545 self.assertEqual("", body_result.err)
550 /* {standard_top_comment} */
551 {standard_top_pragma}
555 /* VOID:STRING ({list_path}:1) */
557 void g_cclosure_user_marshal_VOID__STRING (GClosure *closure,
558 GValue *return_value,
559 guint n_param_values,
560 const GValue *param_values,
561 gpointer invocation_hint,
562 gpointer marshal_data);
564 void g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
565 GValue *return_value,
568 gpointer marshal_data,
575 {standard_bottom_pragma}
579 .format(**header_result.subs),
580 header_result.out.strip(),
586 /* {standard_top_comment} */
587 {standard_marshal_peek_defines}
589 /* VOID:STRING ({list_path}:1) */
591 g_cclosure_user_marshal_VOID__STRING (GClosure *closure,
592 GValue *return_value G_GNUC_UNUSED,
593 guint n_param_values,
594 const GValue *param_values,
595 gpointer invocation_hint G_GNUC_UNUSED,
596 gpointer marshal_data)
598 typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
601 GCClosure *cc = (GCClosure *) closure;
602 gpointer data1, data2;
603 GMarshalFunc_VOID__STRING callback;
605 g_return_if_fail (n_param_values == 2);
607 if (G_CCLOSURE_SWAP_DATA (closure))
609 data1 = closure->data;
610 data2 = g_value_peek_pointer (param_values + 0);
614 data1 = g_value_peek_pointer (param_values + 0);
615 data2 = closure->data;
617 callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
620 g_marshal_value_peek_string (param_values + 1),
625 g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
626 GValue *return_value G_GNUC_UNUSED,
629 gpointer marshal_data,
633 typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
636 GCClosure *cc = (GCClosure *) closure;
637 gpointer data1, data2;
638 GMarshalFunc_VOID__STRING callback;
642 va_copy (args_copy, args);
643 arg0 = (gpointer) va_arg (args_copy, gpointer);
644 if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
645 arg0 = g_strdup (arg0);
649 if (G_CCLOSURE_SWAP_DATA (closure))
651 data1 = closure->data;
657 data2 = closure->data;
659 callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
664 if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
670 .format(**body_result.subs),
671 body_result.out.strip(),
674 def test_void_param_nostdinc(self):
675 """Test running with a basic VOID:PARAM list, but without the
676 standard marshallers, and with valist support enabled. This checks that
677 the valist marshaller for PARAM correctly skips a param copy if the
682 self.maxDiff = None # TODO
683 (header_result, body_result) = self.runGenmarshalWithList(
684 "VOID:PARAM", "--quiet", "--nostdinc", "--valist-marshaller"
687 self.assertEqual("", header_result.err)
688 self.assertEqual("", body_result.err)
693 /* {standard_top_comment} */
694 {standard_top_pragma}
698 /* VOID:PARAM ({list_path}:1) */
700 void g_cclosure_user_marshal_VOID__PARAM (GClosure *closure,
701 GValue *return_value,
702 guint n_param_values,
703 const GValue *param_values,
704 gpointer invocation_hint,
705 gpointer marshal_data);
707 void g_cclosure_user_marshal_VOID__PARAMv (GClosure *closure,
708 GValue *return_value,
711 gpointer marshal_data,
718 {standard_bottom_pragma}
722 .format(**header_result.subs),
723 header_result.out.strip(),
729 /* {standard_top_comment} */
730 {standard_marshal_peek_defines}
732 /* VOID:PARAM ({list_path}:1) */
734 g_cclosure_user_marshal_VOID__PARAM (GClosure *closure,
735 GValue *return_value G_GNUC_UNUSED,
736 guint n_param_values,
737 const GValue *param_values,
738 gpointer invocation_hint G_GNUC_UNUSED,
739 gpointer marshal_data)
741 typedef void (*GMarshalFunc_VOID__PARAM) (gpointer data1,
744 GCClosure *cc = (GCClosure *) closure;
745 gpointer data1, data2;
746 GMarshalFunc_VOID__PARAM callback;
748 g_return_if_fail (n_param_values == 2);
750 if (G_CCLOSURE_SWAP_DATA (closure))
752 data1 = closure->data;
753 data2 = g_value_peek_pointer (param_values + 0);
757 data1 = g_value_peek_pointer (param_values + 0);
758 data2 = closure->data;
760 callback = (GMarshalFunc_VOID__PARAM) (marshal_data ? marshal_data : cc->callback);
763 g_marshal_value_peek_param (param_values + 1),
768 g_cclosure_user_marshal_VOID__PARAMv (GClosure *closure,
769 GValue *return_value G_GNUC_UNUSED,
772 gpointer marshal_data,
776 typedef void (*GMarshalFunc_VOID__PARAM) (gpointer data1,
779 GCClosure *cc = (GCClosure *) closure;
780 gpointer data1, data2;
781 GMarshalFunc_VOID__PARAM callback;
785 va_copy (args_copy, args);
786 arg0 = (gpointer) va_arg (args_copy, gpointer);
787 if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
788 arg0 = g_param_spec_ref (arg0);
792 if (G_CCLOSURE_SWAP_DATA (closure))
794 data1 = closure->data;
800 data2 = closure->data;
802 callback = (GMarshalFunc_VOID__PARAM) (marshal_data ? marshal_data : cc->callback);
807 if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
808 g_param_spec_unref (arg0);
813 .format(**body_result.subs),
814 body_result.out.strip(),
818 if __name__ == "__main__":
819 unittest.main(testRunner=taptestrunner.TAPTestRunner())