1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3#
4# Copyright © 2019 Endless Mobile, Inc.
5#
6# This library is free software; you can redistribute it and/or
7# modify it under the terms of the GNU Lesser General Public
8# License as published by the Free Software Foundation; either
9# version 2.1 of the License, or (at your option) any later version.
10#
11# This library is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14# Lesser General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public
17# License along with this library; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19# MA  02110-1301  USA
20
21"""Integration tests for glib-genmarshal utility."""
22
23import collections
24import os
25import shutil
26import subprocess
27import sys
28import tempfile
29from textwrap import dedent
30import unittest
31
32import taptestrunner
33
34
35# Disable line length warnings as wrapping the C code templates would be hard
36# flake8: noqa: E501
37
38
39Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
40
41
42class TestGenmarshal(unittest.TestCase):
43    """Integration test for running glib-genmarshal.
44
45    This can be run when installed or uninstalled. When uninstalled, it
46    requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
47
48    The idea with this test harness is to test the glib-genmarshal utility, its
49    handling of command line arguments, its exit statuses, and its handling of
50    various marshaller lists. In future we could split the core glib-genmarshal
51    parsing and generation code out into a library and unit test that, and
52    convert this test to just check command line behaviour.
53    """
54
55    # Track the cwd, we want to back out to that to clean up our tempdir
56    cwd = ""
57
58    def setUp(self):
59        self.timeout_seconds = 10  # seconds per test
60        self.tmpdir = tempfile.TemporaryDirectory()
61        self.cwd = os.getcwd()
62        os.chdir(self.tmpdir.name)
63        print("tmpdir:", self.tmpdir.name)
64        if "G_TEST_BUILDDIR" in os.environ:
65            self.__genmarshal = os.path.join(
66                os.environ["G_TEST_BUILDDIR"], "..", "glib-genmarshal"
67            )
68        else:
69            self.__genmarshal = shutil.which("glib-genmarshal")
70        print("genmarshal:", self.__genmarshal)
71
72    def tearDown(self):
73        os.chdir(self.cwd)
74        self.tmpdir.cleanup()
75
76    def runGenmarshal(self, *args):
77        argv = [self.__genmarshal]
78
79        # shebang lines are not supported on native
80        # Windows consoles
81        if os.name == "nt":
82            argv.insert(0, sys.executable)
83
84        argv.extend(args)
85        print("Running:", argv)
86
87        env = os.environ.copy()
88        env["LC_ALL"] = "C.UTF-8"
89        print("Environment:", env)
90
91        # We want to ensure consistent line endings...
92        info = subprocess.run(
93            argv,
94            timeout=self.timeout_seconds,
95            stdout=subprocess.PIPE,
96            stderr=subprocess.PIPE,
97            env=env,
98            universal_newlines=True,
99        )
100        info.check_returncode()
101        out = info.stdout.strip()
102        err = info.stderr.strip()
103
104        # Known substitutions for standard boilerplate
105        subs = {
106            "standard_top_comment": "This file is generated by glib-genmarshal, do not modify "
107            "it. This code is licensed under the same license as the "
108            "containing project. Note that it links to GLib, so must "
109            "comply with the LGPL linking clauses.",
110            "standard_top_pragma": dedent(
111                """
112                #ifndef __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
113                #define __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
114                """
115            ).strip(),
116            "standard_bottom_pragma": dedent(
117                """
118                #endif /* __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__ */
119                """
120            ).strip(),
121            "standard_includes": dedent(
122                """
123                #include <glib-object.h>
124                """
125            ).strip(),
126            "standard_marshal_peek_defines": dedent(
127                """
128                #ifdef G_ENABLE_DEBUG
129                #define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
130                #define g_marshal_value_peek_char(v)     g_value_get_schar (v)
131                #define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
132                #define g_marshal_value_peek_int(v)      g_value_get_int (v)
133                #define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
134                #define g_marshal_value_peek_long(v)     g_value_get_long (v)
135                #define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
136                #define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
137                #define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
138                #define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
139                #define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
140                #define g_marshal_value_peek_float(v)    g_value_get_float (v)
141                #define g_marshal_value_peek_double(v)   g_value_get_double (v)
142                #define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
143                #define g_marshal_value_peek_param(v)    g_value_get_param (v)
144                #define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
145                #define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
146                #define g_marshal_value_peek_object(v)   g_value_get_object (v)
147                #define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
148                #else /* !G_ENABLE_DEBUG */
149                /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
150                 *          Do not access GValues directly in your code. Instead, use the
151                 *          g_value_get_*() functions
152                 */
153                #define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
154                #define g_marshal_value_peek_char(v)     (v)->data[0].v_int
155                #define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
156                #define g_marshal_value_peek_int(v)      (v)->data[0].v_int
157                #define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
158                #define g_marshal_value_peek_long(v)     (v)->data[0].v_long
159                #define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
160                #define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
161                #define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
162                #define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
163                #define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
164                #define g_marshal_value_peek_float(v)    (v)->data[0].v_float
165                #define g_marshal_value_peek_double(v)   (v)->data[0].v_double
166                #define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
167                #define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
168                #define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
169                #define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
170                #define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
171                #define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
172                #endif /* !G_ENABLE_DEBUG */
173                """
174            ).strip(),
175        }
176
177        result = Result(info, out, err, subs)
178
179        print("Output:", result.out)
180        return result
181
182    def runGenmarshalWithList(self, list_contents, *args):
183        with tempfile.NamedTemporaryFile(
184            dir=self.tmpdir.name, suffix=".list", delete=False
185        ) as list_file:
186            # Write out the list.
187            list_file.write(list_contents.encode("utf-8"))
188            print(list_file.name + ":", list_contents)
189            list_file.flush()
190
191            header_result = self.runGenmarshal(list_file.name, "--header", *args)
192            body_result = self.runGenmarshal(list_file.name, "--body", *args)
193
194            header_result.subs["list_path"] = list_file.name
195            body_result.subs["list_path"] = list_file.name
196
197            return (header_result, body_result)
198
199    def test_help(self):
200        """Test the --help argument."""
201        result = self.runGenmarshal("--help")
202        self.assertIn("usage: glib-genmarshal", result.out)
203
204    def test_no_args(self):
205        """Test running with no arguments at all."""
206        result = self.runGenmarshal()
207        self.assertEqual("", result.err)
208        self.assertEqual("", result.out)
209
210    def test_empty_list(self):
211        """Test running with an empty list."""
212        (header_result, body_result) = self.runGenmarshalWithList("", "--quiet")
213
214        self.assertEqual("", header_result.err)
215        self.assertEqual("", body_result.err)
216
217        self.assertEqual(
218            dedent(
219                """
220            /* {standard_top_comment} */
221            {standard_top_pragma}
222
223            {standard_includes}
224
225            G_BEGIN_DECLS
226
227
228            G_END_DECLS
229
230            {standard_bottom_pragma}
231            """
232            )
233            .strip()
234            .format(**header_result.subs),
235            header_result.out.strip(),
236        )
237
238        self.assertEqual(
239            dedent(
240                """
241            /* {standard_top_comment} */
242            {standard_includes}
243
244            {standard_marshal_peek_defines}
245            """
246            )
247            .strip()
248            .format(**body_result.subs),
249            body_result.out.strip(),
250        )
251
252    def test_void_boolean(self):
253        """Test running with a basic VOID:BOOLEAN list."""
254        (header_result, body_result) = self.runGenmarshalWithList(
255            "VOID:BOOLEAN", "--quiet"
256        )
257
258        self.assertEqual("", header_result.err)
259        self.assertEqual("", body_result.err)
260
261        self.assertEqual(
262            dedent(
263                """
264            /* {standard_top_comment} */
265            {standard_top_pragma}
266
267            {standard_includes}
268
269            G_BEGIN_DECLS
270
271            /* VOID:BOOLEAN ({list_path}:1) */
272            #define g_cclosure_user_marshal_VOID__BOOLEAN	g_cclosure_marshal_VOID__BOOLEAN
273
274
275            G_END_DECLS
276
277            {standard_bottom_pragma}
278            """
279            )
280            .strip()
281            .format(**header_result.subs),
282            header_result.out.strip(),
283        )
284
285        self.assertEqual(
286            dedent(
287                """
288            /* {standard_top_comment} */
289            {standard_includes}
290
291            {standard_marshal_peek_defines}
292            """
293            )
294            .strip()
295            .format(**body_result.subs),
296            body_result.out.strip(),
297        )
298
299    def test_void_boolean_int64(self):
300        """Test running with a non-trivial VOID:BOOLEAN,INT64 list."""
301        (header_result, body_result) = self.runGenmarshalWithList(
302            "VOID:BOOLEAN,INT64", "--quiet"
303        )
304
305        self.assertEqual("", header_result.err)
306        self.assertEqual("", body_result.err)
307
308        self.assertEqual(
309            dedent(
310                """
311            /* {standard_top_comment} */
312            {standard_top_pragma}
313
314            {standard_includes}
315
316            G_BEGIN_DECLS
317
318            /* VOID:BOOLEAN,INT64 ({list_path}:1) */
319            extern
320            void g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure     *closure,
321                                                              GValue       *return_value,
322                                                              guint         n_param_values,
323                                                              const GValue *param_values,
324                                                              gpointer      invocation_hint,
325                                                              gpointer      marshal_data);
326
327
328            G_END_DECLS
329
330            {standard_bottom_pragma}
331            """
332            )
333            .strip()
334            .format(**header_result.subs),
335            header_result.out.strip(),
336        )
337
338        self.assertEqual(
339            dedent(
340                """
341            /* {standard_top_comment} */
342            {standard_includes}
343
344            {standard_marshal_peek_defines}
345
346            /* VOID:BOOLEAN,INT64 ({list_path}:1) */
347            void
348            g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure     *closure,
349                                                         GValue       *return_value G_GNUC_UNUSED,
350                                                         guint         n_param_values,
351                                                         const GValue *param_values,
352                                                         gpointer      invocation_hint G_GNUC_UNUSED,
353                                                         gpointer      marshal_data)
354            {{
355              typedef void (*GMarshalFunc_VOID__BOOLEAN_INT64) (gpointer data1,
356                                                                gboolean arg1,
357                                                                gint64 arg2,
358                                                                gpointer data2);
359              GCClosure *cc = (GCClosure *) closure;
360              gpointer data1, data2;
361              GMarshalFunc_VOID__BOOLEAN_INT64 callback;
362
363              g_return_if_fail (n_param_values == 3);
364
365              if (G_CCLOSURE_SWAP_DATA (closure))
366                {{
367                  data1 = closure->data;
368                  data2 = g_value_peek_pointer (param_values + 0);
369                }}
370              else
371                {{
372                  data1 = g_value_peek_pointer (param_values + 0);
373                  data2 = closure->data;
374                }}
375              callback = (GMarshalFunc_VOID__BOOLEAN_INT64) (marshal_data ? marshal_data : cc->callback);
376
377              callback (data1,
378                        g_marshal_value_peek_boolean (param_values + 1),
379                        g_marshal_value_peek_int64 (param_values + 2),
380                        data2);
381            }}
382            """
383            )
384            .strip()
385            .format(**body_result.subs),
386            body_result.out.strip(),
387        )
388
389    def test_void_variant_nostdinc_valist_marshaller(self):
390        """Test running with a basic VOID:VARIANT list, but without the
391        standard marshallers, and with valist support enabled. This checks that
392        the valist marshaller for VARIANT correctly sinks floating variants.
393
394        See issue #1793.
395        """
396        (header_result, body_result) = self.runGenmarshalWithList(
397            "VOID:VARIANT", "--quiet", "--nostdinc", "--valist-marshaller"
398        )
399
400        self.assertEqual("", header_result.err)
401        self.assertEqual("", body_result.err)
402
403        self.assertEqual(
404            dedent(
405                """
406            /* {standard_top_comment} */
407            {standard_top_pragma}
408
409            G_BEGIN_DECLS
410
411            /* VOID:VARIANT ({list_path}:1) */
412            extern
413            void g_cclosure_user_marshal_VOID__VARIANT (GClosure     *closure,
414                                                        GValue       *return_value,
415                                                        guint         n_param_values,
416                                                        const GValue *param_values,
417                                                        gpointer      invocation_hint,
418                                                        gpointer      marshal_data);
419            extern
420            void g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
421                                                         GValue   *return_value,
422                                                         gpointer  instance,
423                                                         va_list   args,
424                                                         gpointer  marshal_data,
425                                                         int       n_params,
426                                                         GType    *param_types);
427
428
429            G_END_DECLS
430
431            {standard_bottom_pragma}
432            """
433            )
434            .strip()
435            .format(**header_result.subs),
436            header_result.out.strip(),
437        )
438
439        self.assertEqual(
440            dedent(
441                """
442            /* {standard_top_comment} */
443            {standard_marshal_peek_defines}
444
445            /* VOID:VARIANT ({list_path}:1) */
446            void
447            g_cclosure_user_marshal_VOID__VARIANT (GClosure     *closure,
448                                                   GValue       *return_value G_GNUC_UNUSED,
449                                                   guint         n_param_values,
450                                                   const GValue *param_values,
451                                                   gpointer      invocation_hint G_GNUC_UNUSED,
452                                                   gpointer      marshal_data)
453            {{
454              typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
455                                                          gpointer arg1,
456                                                          gpointer data2);
457              GCClosure *cc = (GCClosure *) closure;
458              gpointer data1, data2;
459              GMarshalFunc_VOID__VARIANT callback;
460
461              g_return_if_fail (n_param_values == 2);
462
463              if (G_CCLOSURE_SWAP_DATA (closure))
464                {{
465                  data1 = closure->data;
466                  data2 = g_value_peek_pointer (param_values + 0);
467                }}
468              else
469                {{
470                  data1 = g_value_peek_pointer (param_values + 0);
471                  data2 = closure->data;
472                }}
473              callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
474
475              callback (data1,
476                        g_marshal_value_peek_variant (param_values + 1),
477                        data2);
478            }}
479
480            void
481            g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
482                                                    GValue   *return_value G_GNUC_UNUSED,
483                                                    gpointer  instance,
484                                                    va_list   args,
485                                                    gpointer  marshal_data,
486                                                    int       n_params,
487                                                    GType    *param_types)
488            {{
489              typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
490                                                          gpointer arg1,
491                                                          gpointer data2);
492              GCClosure *cc = (GCClosure *) closure;
493              gpointer data1, data2;
494              GMarshalFunc_VOID__VARIANT callback;
495              gpointer arg0;
496              va_list args_copy;
497
498              G_VA_COPY (args_copy, args);
499              arg0 = (gpointer) va_arg (args_copy, gpointer);
500              if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
501                arg0 = g_variant_ref_sink (arg0);
502              va_end (args_copy);
503
504
505              if (G_CCLOSURE_SWAP_DATA (closure))
506                {{
507                  data1 = closure->data;
508                  data2 = instance;
509                }}
510              else
511                {{
512                  data1 = instance;
513                  data2 = closure->data;
514                }}
515              callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
516
517              callback (data1,
518                        arg0,
519                        data2);
520              if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
521                g_variant_unref (arg0);
522            }}
523            """
524            )
525            .strip()
526            .format(**body_result.subs),
527            body_result.out.strip(),
528        )
529
530    def test_void_string_nostdinc(self):
531        """Test running with a basic VOID:STRING list, but without the
532        standard marshallers, and with valist support enabled. This checks that
533        the valist marshaller for STRING correctly skips a string copy if the
534        argument is static.
535
536        See issue #1792.
537        """
538        (header_result, body_result) = self.runGenmarshalWithList(
539            "VOID:STRING", "--quiet", "--nostdinc", "--valist-marshaller"
540        )
541
542        self.assertEqual("", header_result.err)
543        self.assertEqual("", body_result.err)
544
545        self.assertEqual(
546            dedent(
547                """
548            /* {standard_top_comment} */
549            {standard_top_pragma}
550
551            G_BEGIN_DECLS
552
553            /* VOID:STRING ({list_path}:1) */
554            extern
555            void g_cclosure_user_marshal_VOID__STRING (GClosure     *closure,
556                                                       GValue       *return_value,
557                                                       guint         n_param_values,
558                                                       const GValue *param_values,
559                                                       gpointer      invocation_hint,
560                                                       gpointer      marshal_data);
561            extern
562            void g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
563                                                        GValue   *return_value,
564                                                        gpointer  instance,
565                                                        va_list   args,
566                                                        gpointer  marshal_data,
567                                                        int       n_params,
568                                                        GType    *param_types);
569
570
571            G_END_DECLS
572
573            {standard_bottom_pragma}
574            """
575            )
576            .strip()
577            .format(**header_result.subs),
578            header_result.out.strip(),
579        )
580
581        self.assertEqual(
582            dedent(
583                """
584            /* {standard_top_comment} */
585            {standard_marshal_peek_defines}
586
587            /* VOID:STRING ({list_path}:1) */
588            void
589            g_cclosure_user_marshal_VOID__STRING (GClosure     *closure,
590                                                  GValue       *return_value G_GNUC_UNUSED,
591                                                  guint         n_param_values,
592                                                  const GValue *param_values,
593                                                  gpointer      invocation_hint G_GNUC_UNUSED,
594                                                  gpointer      marshal_data)
595            {{
596              typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
597                                                         gpointer arg1,
598                                                         gpointer data2);
599              GCClosure *cc = (GCClosure *) closure;
600              gpointer data1, data2;
601              GMarshalFunc_VOID__STRING callback;
602
603              g_return_if_fail (n_param_values == 2);
604
605              if (G_CCLOSURE_SWAP_DATA (closure))
606                {{
607                  data1 = closure->data;
608                  data2 = g_value_peek_pointer (param_values + 0);
609                }}
610              else
611                {{
612                  data1 = g_value_peek_pointer (param_values + 0);
613                  data2 = closure->data;
614                }}
615              callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
616
617              callback (data1,
618                        g_marshal_value_peek_string (param_values + 1),
619                        data2);
620            }}
621
622            void
623            g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
624                                                   GValue   *return_value G_GNUC_UNUSED,
625                                                   gpointer  instance,
626                                                   va_list   args,
627                                                   gpointer  marshal_data,
628                                                   int       n_params,
629                                                   GType    *param_types)
630            {{
631              typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
632                                                         gpointer arg1,
633                                                         gpointer data2);
634              GCClosure *cc = (GCClosure *) closure;
635              gpointer data1, data2;
636              GMarshalFunc_VOID__STRING callback;
637              gpointer arg0;
638              va_list args_copy;
639
640              G_VA_COPY (args_copy, args);
641              arg0 = (gpointer) va_arg (args_copy, gpointer);
642              if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
643                arg0 = g_strdup (arg0);
644              va_end (args_copy);
645
646
647              if (G_CCLOSURE_SWAP_DATA (closure))
648                {{
649                  data1 = closure->data;
650                  data2 = instance;
651                }}
652              else
653                {{
654                  data1 = instance;
655                  data2 = closure->data;
656                }}
657              callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
658
659              callback (data1,
660                        arg0,
661                        data2);
662              if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
663                g_free (arg0);
664            }}
665            """
666            )
667            .strip()
668            .format(**body_result.subs),
669            body_result.out.strip(),
670        )
671
672    def test_void_param_nostdinc(self):
673        """Test running with a basic VOID:PARAM list, but without the
674        standard marshallers, and with valist support enabled. This checks that
675        the valist marshaller for PARAM correctly skips a param copy if the
676        argument is static.
677
678        See issue #1792.
679        """
680        self.maxDiff = None  # TODO
681        (header_result, body_result) = self.runGenmarshalWithList(
682            "VOID:PARAM", "--quiet", "--nostdinc", "--valist-marshaller"
683        )
684
685        self.assertEqual("", header_result.err)
686        self.assertEqual("", body_result.err)
687
688        self.assertEqual(
689            dedent(
690                """
691            /* {standard_top_comment} */
692            {standard_top_pragma}
693
694            G_BEGIN_DECLS
695
696            /* VOID:PARAM ({list_path}:1) */
697            extern
698            void g_cclosure_user_marshal_VOID__PARAM (GClosure     *closure,
699                                                      GValue       *return_value,
700                                                      guint         n_param_values,
701                                                      const GValue *param_values,
702                                                      gpointer      invocation_hint,
703                                                      gpointer      marshal_data);
704            extern
705            void g_cclosure_user_marshal_VOID__PARAMv (GClosure *closure,
706                                                       GValue   *return_value,
707                                                       gpointer  instance,
708                                                       va_list   args,
709                                                       gpointer  marshal_data,
710                                                       int       n_params,
711                                                       GType    *param_types);
712
713
714            G_END_DECLS
715
716            {standard_bottom_pragma}
717            """
718            )
719            .strip()
720            .format(**header_result.subs),
721            header_result.out.strip(),
722        )
723
724        self.assertEqual(
725            dedent(
726                """
727            /* {standard_top_comment} */
728            {standard_marshal_peek_defines}
729
730            /* VOID:PARAM ({list_path}:1) */
731            void
732            g_cclosure_user_marshal_VOID__PARAM (GClosure     *closure,
733                                                 GValue       *return_value G_GNUC_UNUSED,
734                                                 guint         n_param_values,
735                                                 const GValue *param_values,
736                                                 gpointer      invocation_hint G_GNUC_UNUSED,
737                                                 gpointer      marshal_data)
738            {{
739              typedef void (*GMarshalFunc_VOID__PARAM) (gpointer data1,
740                                                        gpointer arg1,
741                                                        gpointer data2);
742              GCClosure *cc = (GCClosure *) closure;
743              gpointer data1, data2;
744              GMarshalFunc_VOID__PARAM callback;
745
746              g_return_if_fail (n_param_values == 2);
747
748              if (G_CCLOSURE_SWAP_DATA (closure))
749                {{
750                  data1 = closure->data;
751                  data2 = g_value_peek_pointer (param_values + 0);
752                }}
753              else
754                {{
755                  data1 = g_value_peek_pointer (param_values + 0);
756                  data2 = closure->data;
757                }}
758              callback = (GMarshalFunc_VOID__PARAM) (marshal_data ? marshal_data : cc->callback);
759
760              callback (data1,
761                        g_marshal_value_peek_param (param_values + 1),
762                        data2);
763            }}
764
765            void
766            g_cclosure_user_marshal_VOID__PARAMv (GClosure *closure,
767                                                  GValue   *return_value G_GNUC_UNUSED,
768                                                  gpointer  instance,
769                                                  va_list   args,
770                                                  gpointer  marshal_data,
771                                                  int       n_params,
772                                                  GType    *param_types)
773            {{
774              typedef void (*GMarshalFunc_VOID__PARAM) (gpointer data1,
775                                                        gpointer arg1,
776                                                        gpointer data2);
777              GCClosure *cc = (GCClosure *) closure;
778              gpointer data1, data2;
779              GMarshalFunc_VOID__PARAM callback;
780              gpointer arg0;
781              va_list args_copy;
782
783              G_VA_COPY (args_copy, args);
784              arg0 = (gpointer) va_arg (args_copy, gpointer);
785              if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
786                arg0 = g_param_spec_ref (arg0);
787              va_end (args_copy);
788
789
790              if (G_CCLOSURE_SWAP_DATA (closure))
791                {{
792                  data1 = closure->data;
793                  data2 = instance;
794                }}
795              else
796                {{
797                  data1 = instance;
798                  data2 = closure->data;
799                }}
800              callback = (GMarshalFunc_VOID__PARAM) (marshal_data ? marshal_data : cc->callback);
801
802              callback (data1,
803                        arg0,
804                        data2);
805              if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
806                g_param_spec_unref (arg0);
807            }}
808            """
809            )
810            .strip()
811            .format(**body_result.subs),
812            body_result.out.strip(),
813        )
814
815
816if __name__ == "__main__":
817    unittest.main(testRunner=taptestrunner.TAPTestRunner())
818