2019-12-02 15:05:22 +01:00
|
|
|
#!/usr/bin/python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
|
|
# Copyright © 2018, 2019 Endless Mobile, Inc.
|
|
|
|
#
|
2022-06-01 13:44:23 +02:00
|
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
#
|
2019-12-02 15:05:22 +01:00
|
|
|
# This library is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
|
|
# License as published by the Free Software Foundation; either
|
|
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This library is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
# Lesser General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
|
|
# License along with this library; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
# MA 02110-1301 USA
|
|
|
|
|
|
|
|
"""Integration tests for gdbus-codegen utility."""
|
|
|
|
|
|
|
|
import collections
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
import unittest
|
2022-02-15 13:14:49 +01:00
|
|
|
import xml.etree.ElementTree as ET
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
import taptestrunner
|
|
|
|
|
2020-11-17 16:32:10 +01:00
|
|
|
# Disable line length warnings as wrapping the C code templates would be hard
|
|
|
|
# flake8: noqa: E501
|
|
|
|
|
|
|
|
|
2020-11-17 16:07:09 +01:00
|
|
|
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
def on_win32():
|
2022-05-12 02:29:44 +02:00
|
|
|
return sys.platform.find("win") != -1
|
2022-03-08 09:20:52 +01:00
|
|
|
|
|
|
|
|
2019-12-02 15:05:22 +01:00
|
|
|
class TestCodegen(unittest.TestCase):
|
|
|
|
"""Integration test for running gdbus-codegen.
|
|
|
|
|
|
|
|
This can be run when installed or uninstalled. When uninstalled, it
|
|
|
|
requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
|
|
|
|
|
|
|
|
The idea with this test harness is to test the gdbus-codegen utility, its
|
|
|
|
handling of command line arguments, its exit statuses, and its handling of
|
|
|
|
various C source codes. In future we could split out tests for the core
|
|
|
|
parsing and generation code of gdbus-codegen into separate unit tests, and
|
|
|
|
just test command line behaviour in this integration test.
|
|
|
|
"""
|
2020-11-17 16:07:09 +01:00
|
|
|
|
2019-12-02 15:05:22 +01:00
|
|
|
# Track the cwd, we want to back out to that to clean up our tempdir
|
2020-11-17 16:07:09 +01:00
|
|
|
cwd = ""
|
2019-12-02 15:05:22 +01:00
|
|
|
|
2023-06-30 16:25:01 +02:00
|
|
|
ARGUMENTS_TYPES = {
|
2023-06-30 02:52:11 +02:00
|
|
|
"b": {"value_type": "boolean"},
|
|
|
|
"y": {"value_type": "uchar"},
|
|
|
|
"n": {"value_type": "int"},
|
|
|
|
"q": {"value_type": "uint"},
|
|
|
|
"i": {"value_type": "int"},
|
|
|
|
"u": {"value_type": "uint"},
|
|
|
|
"x": {"value_type": "int64"},
|
|
|
|
"t": {"value_type": "uint64"},
|
|
|
|
"d": {"value_type": "double"},
|
|
|
|
"s": {"value_type": "string"},
|
|
|
|
"o": {"value_type": "string"},
|
|
|
|
"g": {"value_type": "string"},
|
|
|
|
"h": {"value_type": "variant"},
|
|
|
|
"ay": {"value_type": "string"},
|
|
|
|
"as": {"value_type": "boxed"},
|
|
|
|
"ao": {"value_type": "boxed"},
|
|
|
|
"aay": {"value_type": "boxed"},
|
|
|
|
"asv": {"value_type": "variant", "variant_type": "a{sv}"},
|
2023-06-30 16:25:01 +02:00
|
|
|
}
|
|
|
|
|
2019-12-02 15:05:22 +01:00
|
|
|
def setUp(self):
|
2022-11-02 18:40:51 +01:00
|
|
|
self.timeout_seconds = 6 # seconds per test
|
2019-12-02 15:05:22 +01:00
|
|
|
self.tmpdir = tempfile.TemporaryDirectory()
|
|
|
|
self.cwd = os.getcwd()
|
|
|
|
os.chdir(self.tmpdir.name)
|
2020-11-17 16:07:09 +01:00
|
|
|
print("tmpdir:", self.tmpdir.name)
|
|
|
|
if "G_TEST_BUILDDIR" in os.environ:
|
|
|
|
self.__codegen = os.path.join(
|
|
|
|
os.environ["G_TEST_BUILDDIR"],
|
|
|
|
"..",
|
|
|
|
"gdbus-2.0",
|
|
|
|
"codegen",
|
|
|
|
"gdbus-codegen",
|
|
|
|
)
|
2019-12-02 15:05:22 +01:00
|
|
|
else:
|
2020-11-17 16:07:09 +01:00
|
|
|
self.__codegen = shutil.which("gdbus-codegen")
|
|
|
|
print("codegen:", self.__codegen)
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
os.chdir(self.cwd)
|
|
|
|
self.tmpdir.cleanup()
|
|
|
|
|
|
|
|
def runCodegen(self, *args):
|
|
|
|
argv = [self.__codegen]
|
|
|
|
|
|
|
|
# shebang lines are not supported on native
|
|
|
|
# Windows consoles
|
2020-11-17 16:07:09 +01:00
|
|
|
if os.name == "nt":
|
2019-12-02 15:05:22 +01:00
|
|
|
argv.insert(0, sys.executable)
|
|
|
|
|
|
|
|
argv.extend(args)
|
2020-11-17 16:07:09 +01:00
|
|
|
print("Running:", argv)
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
env = os.environ.copy()
|
2020-11-17 16:07:09 +01:00
|
|
|
env["LC_ALL"] = "C.UTF-8"
|
|
|
|
print("Environment:", env)
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
# We want to ensure consistent line endings...
|
2020-11-17 16:07:09 +01:00
|
|
|
info = subprocess.run(
|
|
|
|
argv,
|
|
|
|
timeout=self.timeout_seconds,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE,
|
|
|
|
env=env,
|
|
|
|
universal_newlines=True,
|
|
|
|
)
|
2019-12-02 15:05:22 +01:00
|
|
|
info.check_returncode()
|
|
|
|
out = info.stdout.strip()
|
|
|
|
err = info.stderr.strip()
|
|
|
|
|
|
|
|
# Known substitutions for standard boilerplate
|
|
|
|
subs = {
|
2020-11-17 16:07:09 +01:00
|
|
|
"standard_top_comment": "/*\n"
|
|
|
|
" * This file is generated by gdbus-codegen, do not modify it.\n"
|
|
|
|
" *\n"
|
|
|
|
" * The license of this code is the same as for the D-Bus interface description\n"
|
|
|
|
" * it was derived from. Note that it links to GLib, so must comply with the\n"
|
|
|
|
" * LGPL linking clauses.\n"
|
|
|
|
" */",
|
|
|
|
"standard_config_h_include": "#ifdef HAVE_CONFIG_H\n"
|
|
|
|
'# include "config.h"\n'
|
|
|
|
"#endif",
|
|
|
|
"standard_header_includes": "#include <string.h>\n"
|
|
|
|
"#ifdef G_OS_UNIX\n"
|
|
|
|
"# include <gio/gunixfdlist.h>\n"
|
|
|
|
"#endif",
|
2023-06-30 19:24:03 +02:00
|
|
|
"private_gvalues_getters": """#ifdef G_ENABLE_DEBUG
|
|
|
|
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
|
|
|
|
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
|
|
|
|
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
|
|
|
|
#define g_marshal_value_peek_int(v) g_value_get_int (v)
|
|
|
|
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
|
|
|
|
#define g_marshal_value_peek_long(v) g_value_get_long (v)
|
|
|
|
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
|
|
|
|
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
|
|
|
|
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
|
|
|
|
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
|
|
|
|
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
|
|
|
|
#define g_marshal_value_peek_float(v) g_value_get_float (v)
|
|
|
|
#define g_marshal_value_peek_double(v) g_value_get_double (v)
|
|
|
|
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
|
|
|
|
#define g_marshal_value_peek_param(v) g_value_get_param (v)
|
|
|
|
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
|
|
|
|
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
|
|
|
|
#define g_marshal_value_peek_object(v) g_value_get_object (v)
|
|
|
|
#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
|
|
|
|
#else /* !G_ENABLE_DEBUG */
|
|
|
|
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
|
|
|
|
* Do not access GValues directly in your code. Instead, use the
|
|
|
|
* g_value_get_*() functions
|
|
|
|
*/
|
|
|
|
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
|
|
|
|
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
|
|
|
|
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
|
|
|
|
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
|
|
|
|
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
|
|
|
|
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
|
|
|
|
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
|
|
|
|
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
|
|
|
|
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
|
|
|
|
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
|
|
|
|
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
|
|
|
|
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
|
|
|
|
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
|
|
|
|
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
|
|
|
|
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
|
|
|
|
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
|
|
|
|
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
|
|
|
|
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
|
|
|
|
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
|
|
|
|
#endif /* !G_ENABLE_DEBUG */""",
|
2020-11-17 16:07:09 +01:00
|
|
|
"standard_typedefs_and_helpers": "typedef struct\n"
|
|
|
|
"{\n"
|
|
|
|
" GDBusArgInfo parent_struct;\n"
|
|
|
|
" gboolean use_gvariant;\n"
|
|
|
|
"} _ExtendedGDBusArgInfo;\n"
|
|
|
|
"\n"
|
|
|
|
"typedef struct\n"
|
|
|
|
"{\n"
|
|
|
|
" GDBusMethodInfo parent_struct;\n"
|
|
|
|
" const gchar *signal_name;\n"
|
|
|
|
" gboolean pass_fdlist;\n"
|
|
|
|
"} _ExtendedGDBusMethodInfo;\n"
|
|
|
|
"\n"
|
|
|
|
"typedef struct\n"
|
|
|
|
"{\n"
|
|
|
|
" GDBusSignalInfo parent_struct;\n"
|
|
|
|
" const gchar *signal_name;\n"
|
|
|
|
"} _ExtendedGDBusSignalInfo;\n"
|
|
|
|
"\n"
|
|
|
|
"typedef struct\n"
|
|
|
|
"{\n"
|
|
|
|
" GDBusPropertyInfo parent_struct;\n"
|
|
|
|
" const gchar *hyphen_name;\n"
|
|
|
|
" guint use_gvariant : 1;\n"
|
|
|
|
" guint emits_changed_signal : 1;\n"
|
|
|
|
"} _ExtendedGDBusPropertyInfo;\n"
|
|
|
|
"\n"
|
|
|
|
"typedef struct\n"
|
|
|
|
"{\n"
|
|
|
|
" GDBusInterfaceInfo parent_struct;\n"
|
|
|
|
" const gchar *hyphen_name;\n"
|
|
|
|
"} _ExtendedGDBusInterfaceInfo;\n"
|
|
|
|
"\n"
|
|
|
|
"typedef struct\n"
|
|
|
|
"{\n"
|
|
|
|
" const _ExtendedGDBusPropertyInfo *info;\n"
|
|
|
|
" guint prop_id;\n"
|
|
|
|
" GValue orig_value; /* the value before the change */\n"
|
|
|
|
"} ChangedProperty;\n"
|
|
|
|
"\n"
|
|
|
|
"static void\n"
|
|
|
|
"_changed_property_free (ChangedProperty *data)\n"
|
|
|
|
"{\n"
|
|
|
|
" g_value_unset (&data->orig_value);\n"
|
|
|
|
" g_free (data);\n"
|
|
|
|
"}\n"
|
|
|
|
"\n"
|
|
|
|
"static gboolean\n"
|
|
|
|
"_g_strv_equal0 (gchar **a, gchar **b)\n"
|
|
|
|
"{\n"
|
|
|
|
" gboolean ret = FALSE;\n"
|
|
|
|
" guint n;\n"
|
|
|
|
" if (a == NULL && b == NULL)\n"
|
|
|
|
" {\n"
|
|
|
|
" ret = TRUE;\n"
|
|
|
|
" goto out;\n"
|
|
|
|
" }\n"
|
|
|
|
" if (a == NULL || b == NULL)\n"
|
|
|
|
" goto out;\n"
|
|
|
|
" if (g_strv_length (a) != g_strv_length (b))\n"
|
|
|
|
" goto out;\n"
|
|
|
|
" for (n = 0; a[n] != NULL; n++)\n"
|
|
|
|
" if (g_strcmp0 (a[n], b[n]) != 0)\n"
|
|
|
|
" goto out;\n"
|
|
|
|
" ret = TRUE;\n"
|
|
|
|
"out:\n"
|
|
|
|
" return ret;\n"
|
|
|
|
"}\n"
|
|
|
|
"\n"
|
|
|
|
"static gboolean\n"
|
|
|
|
"_g_variant_equal0 (GVariant *a, GVariant *b)\n"
|
|
|
|
"{\n"
|
|
|
|
" gboolean ret = FALSE;\n"
|
|
|
|
" if (a == NULL && b == NULL)\n"
|
|
|
|
" {\n"
|
|
|
|
" ret = TRUE;\n"
|
|
|
|
" goto out;\n"
|
|
|
|
" }\n"
|
|
|
|
" if (a == NULL || b == NULL)\n"
|
|
|
|
" goto out;\n"
|
|
|
|
" ret = g_variant_equal (a, b);\n"
|
|
|
|
"out:\n"
|
|
|
|
" return ret;\n"
|
|
|
|
"}\n"
|
|
|
|
"\n"
|
|
|
|
"G_GNUC_UNUSED static gboolean\n"
|
|
|
|
"_g_value_equal (const GValue *a, const GValue *b)\n"
|
|
|
|
"{\n"
|
|
|
|
" gboolean ret = FALSE;\n"
|
|
|
|
" g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b));\n"
|
|
|
|
" switch (G_VALUE_TYPE (a))\n"
|
|
|
|
" {\n"
|
|
|
|
" case G_TYPE_BOOLEAN:\n"
|
|
|
|
" ret = (g_value_get_boolean (a) == g_value_get_boolean (b));\n"
|
|
|
|
" break;\n"
|
|
|
|
" case G_TYPE_UCHAR:\n"
|
|
|
|
" ret = (g_value_get_uchar (a) == g_value_get_uchar (b));\n"
|
|
|
|
" break;\n"
|
|
|
|
" case G_TYPE_INT:\n"
|
|
|
|
" ret = (g_value_get_int (a) == g_value_get_int (b));\n"
|
|
|
|
" break;\n"
|
|
|
|
" case G_TYPE_UINT:\n"
|
|
|
|
" ret = (g_value_get_uint (a) == g_value_get_uint (b));\n"
|
|
|
|
" break;\n"
|
|
|
|
" case G_TYPE_INT64:\n"
|
|
|
|
" ret = (g_value_get_int64 (a) == g_value_get_int64 (b));\n"
|
|
|
|
" break;\n"
|
|
|
|
" case G_TYPE_UINT64:\n"
|
|
|
|
" ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b));\n"
|
|
|
|
" break;\n"
|
|
|
|
" case G_TYPE_DOUBLE:\n"
|
|
|
|
" {\n"
|
|
|
|
" /* Avoid -Wfloat-equal warnings by doing a direct bit compare */\n"
|
|
|
|
" gdouble da = g_value_get_double (a);\n"
|
|
|
|
" gdouble db = g_value_get_double (b);\n"
|
|
|
|
" ret = memcmp (&da, &db, sizeof (gdouble)) == 0;\n"
|
|
|
|
" }\n"
|
|
|
|
" break;\n"
|
|
|
|
" case G_TYPE_STRING:\n"
|
|
|
|
" ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0);\n"
|
|
|
|
" break;\n"
|
|
|
|
" case G_TYPE_VARIANT:\n"
|
|
|
|
" ret = _g_variant_equal0 (g_value_get_variant (a), g_value_get_variant (b));\n"
|
|
|
|
" break;\n"
|
|
|
|
" default:\n"
|
|
|
|
" if (G_VALUE_TYPE (a) == G_TYPE_STRV)\n"
|
|
|
|
" ret = _g_strv_equal0 (g_value_get_boxed (a), g_value_get_boxed (b));\n"
|
|
|
|
" else\n"
|
|
|
|
' g_critical ("_g_value_equal() does not handle type %s", g_type_name (G_VALUE_TYPE (a)));\n'
|
|
|
|
" break;\n"
|
|
|
|
" }\n"
|
|
|
|
" return ret;\n"
|
|
|
|
"}",
|
2019-12-02 15:05:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
result = Result(info, out, err, subs)
|
|
|
|
|
2020-11-17 16:07:09 +01:00
|
|
|
print("Output:", result.out)
|
2019-12-02 15:05:22 +01:00
|
|
|
return result
|
|
|
|
|
|
|
|
def runCodegenWithInterface(self, interface_contents, *args):
|
2020-11-17 16:07:09 +01:00
|
|
|
with tempfile.NamedTemporaryFile(
|
|
|
|
dir=self.tmpdir.name, suffix=".xml", delete=False
|
|
|
|
) as interface_file:
|
2019-12-02 15:05:22 +01:00
|
|
|
# Write out the interface.
|
2020-11-17 16:07:09 +01:00
|
|
|
interface_file.write(interface_contents.encode("utf-8"))
|
|
|
|
print(interface_file.name + ":", interface_contents)
|
2019-12-02 15:05:22 +01:00
|
|
|
interface_file.flush()
|
|
|
|
|
|
|
|
return self.runCodegen(interface_file.name, *args)
|
|
|
|
|
|
|
|
def test_help(self):
|
|
|
|
"""Test the --help argument."""
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegen("--help")
|
|
|
|
self.assertIn("usage: gdbus-codegen", result.out)
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
def test_no_args(self):
|
|
|
|
"""Test running with no arguments at all."""
|
|
|
|
with self.assertRaises(subprocess.CalledProcessError):
|
|
|
|
self.runCodegen()
|
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2019-12-02 15:05:22 +01:00
|
|
|
def test_empty_interface_header(self):
|
|
|
|
"""Test generating a header with an empty interface file."""
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface("", "--output", "/dev/stdout", "--header")
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertEqual(
|
|
|
|
"""{standard_top_comment}
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
#ifndef __STDOUT__
|
|
|
|
#define __STDOUT__
|
|
|
|
|
|
|
|
#include <gio/gio.h>
|
|
|
|
|
|
|
|
G_BEGIN_DECLS
|
|
|
|
|
|
|
|
|
|
|
|
G_END_DECLS
|
|
|
|
|
2020-11-17 16:07:09 +01:00
|
|
|
#endif /* __STDOUT__ */""".format(
|
|
|
|
**result.subs
|
|
|
|
),
|
|
|
|
result.out.strip(),
|
|
|
|
)
|
2019-12-02 15:05:22 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2019-12-02 15:05:22 +01:00
|
|
|
def test_empty_interface_body(self):
|
|
|
|
"""Test generating a body with an empty interface file."""
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface("", "--output", "/dev/stdout", "--body")
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertEqual(
|
|
|
|
"""{standard_top_comment}
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
{standard_config_h_include}
|
|
|
|
|
|
|
|
#include "stdout.h"
|
|
|
|
|
|
|
|
{standard_header_includes}
|
|
|
|
|
2023-06-30 19:24:03 +02:00
|
|
|
{private_gvalues_getters}
|
|
|
|
|
2020-11-17 16:07:09 +01:00
|
|
|
{standard_typedefs_and_helpers}""".format(
|
|
|
|
**result.subs
|
|
|
|
),
|
|
|
|
result.out.strip(),
|
|
|
|
)
|
2019-12-02 15:05:22 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2019-12-02 15:05:22 +01:00
|
|
|
def test_reproducible(self):
|
|
|
|
"""Test builds are reproducible regardless of file ordering."""
|
2020-11-17 16:07:09 +01:00
|
|
|
xml_contents1 = """
|
2019-12-02 15:05:22 +01:00
|
|
|
<node>
|
|
|
|
<interface name="com.acme.Coyote">
|
|
|
|
<method name="Run"/>
|
|
|
|
<method name="Sleep"/>
|
|
|
|
<method name="Attack"/>
|
|
|
|
<signal name="Surprised"/>
|
|
|
|
<property name="Mood" type="s" access="read"/>
|
|
|
|
</interface>
|
|
|
|
</node>
|
2020-11-17 16:07:09 +01:00
|
|
|
"""
|
2019-12-02 15:05:22 +01:00
|
|
|
|
2020-11-17 16:07:09 +01:00
|
|
|
xml_contents2 = """
|
2019-12-02 15:05:22 +01:00
|
|
|
<node>
|
|
|
|
<interface name="org.project.Bar.Frobnicator">
|
|
|
|
<method name="RandomMethod"/>
|
|
|
|
</interface>
|
|
|
|
</node>
|
2020-11-17 16:07:09 +01:00
|
|
|
"""
|
2019-12-02 15:05:22 +01:00
|
|
|
|
2020-11-17 16:07:09 +01:00
|
|
|
with tempfile.NamedTemporaryFile(
|
|
|
|
dir=self.tmpdir.name, suffix="1.xml", delete=False
|
|
|
|
) as xml_file1, tempfile.NamedTemporaryFile(
|
|
|
|
dir=self.tmpdir.name, suffix="2.xml", delete=False
|
|
|
|
) as xml_file2:
|
2019-12-02 15:05:22 +01:00
|
|
|
# Write out the interfaces.
|
2020-11-17 16:07:09 +01:00
|
|
|
xml_file1.write(xml_contents1.encode("utf-8"))
|
|
|
|
xml_file2.write(xml_contents2.encode("utf-8"))
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
xml_file1.flush()
|
|
|
|
xml_file2.flush()
|
|
|
|
|
|
|
|
# Repeat this for headers and bodies.
|
2020-11-17 16:07:09 +01:00
|
|
|
for header_or_body in ["--header", "--body"]:
|
2019-12-02 15:05:22 +01:00
|
|
|
# Run gdbus-codegen with the interfaces in one order, and then
|
|
|
|
# again in another order.
|
2020-11-17 16:07:09 +01:00
|
|
|
result1 = self.runCodegen(
|
|
|
|
xml_file1.name,
|
|
|
|
xml_file2.name,
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
header_or_body,
|
|
|
|
)
|
|
|
|
self.assertEqual("", result1.err)
|
|
|
|
|
|
|
|
result2 = self.runCodegen(
|
|
|
|
xml_file2.name,
|
|
|
|
xml_file1.name,
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
header_or_body,
|
|
|
|
)
|
|
|
|
self.assertEqual("", result2.err)
|
2019-12-02 15:05:22 +01:00
|
|
|
|
|
|
|
# The output should be the same.
|
|
|
|
self.assertEqual(result1.out, result2.out)
|
|
|
|
|
2022-01-21 18:36:59 +01:00
|
|
|
def test_generate_docbook(self):
|
|
|
|
"""Test the basic functionality of the docbook generator."""
|
|
|
|
xml_contents = """
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.Bar.Frobnicator">
|
|
|
|
<method name="RandomMethod"/>
|
|
|
|
</interface>
|
|
|
|
</node>
|
|
|
|
"""
|
|
|
|
res = self.runCodegenWithInterface(
|
|
|
|
xml_contents,
|
|
|
|
"--generate-docbook",
|
|
|
|
"test",
|
|
|
|
)
|
|
|
|
self.assertEqual("", res.err)
|
|
|
|
self.assertEqual("", res.out)
|
|
|
|
with open("test-org.project.Bar.Frobnicator.xml", "r") as f:
|
|
|
|
xml_data = f.readlines()
|
|
|
|
self.assertTrue(len(xml_data) != 0)
|
|
|
|
|
2022-12-29 13:42:12 +01:00
|
|
|
def test_generate_md(self):
|
|
|
|
"""Test the basic functionality of the markdown generator."""
|
|
|
|
xml_contents = """
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.Bar.Frobnicator">
|
|
|
|
<method name="RandomMethod"/>
|
|
|
|
</interface>
|
|
|
|
</node>
|
|
|
|
"""
|
|
|
|
res = self.runCodegenWithInterface(
|
|
|
|
xml_contents,
|
|
|
|
"--generate-md",
|
|
|
|
"test",
|
|
|
|
)
|
|
|
|
self.assertEqual("", res.err)
|
|
|
|
self.assertEqual("", res.out)
|
|
|
|
with open("test-org.project.Bar.Frobnicator.md", "r") as f:
|
|
|
|
rst = f.readlines()
|
|
|
|
self.assertTrue(len(rst) != 0)
|
|
|
|
|
2022-01-21 18:33:21 +01:00
|
|
|
def test_generate_rst(self):
|
|
|
|
"""Test the basic functionality of the rst generator."""
|
|
|
|
xml_contents = """
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.Bar.Frobnicator">
|
|
|
|
<method name="RandomMethod"/>
|
|
|
|
</interface>
|
|
|
|
</node>
|
|
|
|
"""
|
|
|
|
res = self.runCodegenWithInterface(
|
|
|
|
xml_contents,
|
|
|
|
"--generate-rst",
|
|
|
|
"test",
|
|
|
|
)
|
|
|
|
self.assertEqual("", res.err)
|
|
|
|
self.assertEqual("", res.out)
|
|
|
|
with open("test-org.project.Bar.Frobnicator.rst", "r") as f:
|
|
|
|
rst = f.readlines()
|
|
|
|
self.assertTrue(len(rst) != 0)
|
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:11:46 +01:00
|
|
|
def test_glib_min_required_invalid(self):
|
|
|
|
"""Test running with an invalid --glib-min-required."""
|
2019-12-02 16:53:14 +01:00
|
|
|
with self.assertRaises(subprocess.CalledProcessError):
|
2020-11-17 16:07:09 +01:00
|
|
|
self.runCodegenWithInterface(
|
|
|
|
"",
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
"--body",
|
|
|
|
"--glib-min-required",
|
|
|
|
"hello mum",
|
|
|
|
)
|
2019-12-02 16:53:14 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:11:46 +01:00
|
|
|
def test_glib_min_required_too_low(self):
|
|
|
|
"""Test running with a --glib-min-required which is too low (and hence
|
2019-12-02 16:53:14 +01:00
|
|
|
probably a typo)."""
|
|
|
|
with self.assertRaises(subprocess.CalledProcessError):
|
2020-11-17 16:07:09 +01:00
|
|
|
self.runCodegenWithInterface(
|
|
|
|
"", "--output", "/dev/stdout", "--body", "--glib-min-required", "2.6"
|
|
|
|
)
|
2019-12-02 16:53:14 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:11:46 +01:00
|
|
|
def test_glib_min_required_major_only(self):
|
|
|
|
"""Test running with a --glib-min-required which contains only a major version."""
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
"",
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
"--header",
|
|
|
|
"--glib-min-required",
|
|
|
|
"3",
|
|
|
|
"--glib-max-allowed",
|
|
|
|
"3.2",
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertNotEqual("", result.out.strip())
|
2019-12-02 16:53:14 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:11:46 +01:00
|
|
|
def test_glib_min_required_with_micro(self):
|
|
|
|
"""Test running with a --glib-min-required which contains a micro version."""
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
"", "--output", "/dev/stdout", "--header", "--glib-min-required", "2.46.2"
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertNotEqual("", result.out.strip())
|
2019-12-02 16:53:14 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:36:03 +01:00
|
|
|
def test_glib_max_allowed_too_low(self):
|
|
|
|
"""Test running with a --glib-max-allowed which is too low (and hence
|
|
|
|
probably a typo)."""
|
|
|
|
with self.assertRaises(subprocess.CalledProcessError):
|
2020-11-17 16:07:09 +01:00
|
|
|
self.runCodegenWithInterface(
|
|
|
|
"", "--output", "/dev/stdout", "--body", "--glib-max-allowed", "2.6"
|
|
|
|
)
|
2020-01-27 11:36:03 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:36:03 +01:00
|
|
|
def test_glib_max_allowed_major_only(self):
|
|
|
|
"""Test running with a --glib-max-allowed which contains only a major version."""
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
"", "--output", "/dev/stdout", "--header", "--glib-max-allowed", "3"
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertNotEqual("", result.out.strip())
|
2020-01-27 11:36:03 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:36:03 +01:00
|
|
|
def test_glib_max_allowed_with_micro(self):
|
|
|
|
"""Test running with a --glib-max-allowed which contains a micro version."""
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
"", "--output", "/dev/stdout", "--header", "--glib-max-allowed", "2.46.2"
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertNotEqual("", result.out.strip())
|
2020-01-27 11:36:03 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:36:03 +01:00
|
|
|
def test_glib_max_allowed_unstable(self):
|
|
|
|
"""Test running with a --glib-max-allowed which is unstable. It should
|
|
|
|
be rounded up to the next stable version number, and hence should not
|
|
|
|
end up less than --glib-min-required."""
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
"",
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
"--header",
|
|
|
|
"--glib-max-allowed",
|
|
|
|
"2.63",
|
|
|
|
"--glib-min-required",
|
|
|
|
"2.64",
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertNotEqual("", result.out.strip())
|
2020-01-27 11:36:03 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2020-01-27 11:36:03 +01:00
|
|
|
def test_glib_max_allowed_less_than_min_required(self):
|
|
|
|
"""Test running with a --glib-max-allowed which is less than
|
|
|
|
--glib-min-required."""
|
|
|
|
with self.assertRaises(subprocess.CalledProcessError):
|
2020-11-17 16:07:09 +01:00
|
|
|
self.runCodegenWithInterface(
|
|
|
|
"",
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
"--body",
|
|
|
|
"--glib-max-allowed",
|
|
|
|
"2.62",
|
|
|
|
"--glib-min-required",
|
|
|
|
"2.64",
|
|
|
|
)
|
2020-01-27 11:36:03 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
2019-12-02 17:20:16 +01:00
|
|
|
def test_unix_fd_types_and_annotations(self):
|
|
|
|
"""Test an interface with `h` arguments, no annotation, and GLib < 2.64.
|
|
|
|
|
|
|
|
See issue #1726.
|
|
|
|
"""
|
2020-11-17 16:07:09 +01:00
|
|
|
interface_xml = """
|
2019-12-02 17:20:16 +01:00
|
|
|
<node>
|
|
|
|
<interface name="FDPassing">
|
|
|
|
<method name="HelloFD">
|
|
|
|
<annotation name="org.gtk.GDBus.C.UnixFD" value="1"/>
|
|
|
|
<arg name="greeting" direction="in" type="s"/>
|
|
|
|
<arg name="response" direction="out" type="s"/>
|
|
|
|
</method>
|
|
|
|
<method name="NoAnnotation">
|
|
|
|
<arg name="greeting" direction="in" type="h"/>
|
|
|
|
<arg name="greeting_locale" direction="in" type="s"/>
|
|
|
|
<arg name="response" direction="out" type="h"/>
|
|
|
|
<arg name="response_locale" direction="out" type="s"/>
|
|
|
|
</method>
|
|
|
|
<method name="NoAnnotationNested">
|
|
|
|
<arg name="files" type="a{sh}" direction="in"/>
|
|
|
|
</method>
|
|
|
|
</interface>
|
2020-11-17 16:07:09 +01:00
|
|
|
</node>"""
|
2019-12-02 17:20:16 +01:00
|
|
|
|
2020-01-27 11:11:46 +01:00
|
|
|
# Try without specifying --glib-min-required.
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--header"
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertEqual(result.out.strip().count("GUnixFDList"), 6)
|
2019-12-02 17:20:16 +01:00
|
|
|
|
2020-01-27 11:11:46 +01:00
|
|
|
# Specify an old --glib-min-required.
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml,
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
"--header",
|
|
|
|
"--glib-min-required",
|
|
|
|
"2.32",
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertEqual(result.out.strip().count("GUnixFDList"), 6)
|
2019-12-02 17:20:16 +01:00
|
|
|
|
2020-01-27 11:11:46 +01:00
|
|
|
# Specify a --glib-min-required ≥ 2.64. There should be more
|
2019-12-02 17:20:16 +01:00
|
|
|
# mentions of `GUnixFDList` now, since the annotation is not needed to
|
|
|
|
# trigger its use.
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml,
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
"--header",
|
|
|
|
"--glib-min-required",
|
|
|
|
"2.64",
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertEqual(result.out.strip().count("GUnixFDList"), 18)
|
2019-12-02 17:20:16 +01:00
|
|
|
|
2022-03-08 09:20:52 +01:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
gdbus-codegen: Add call_flags and timeout_msec args
Currently the code generated by gdbus-codegen uses
G_DBUS_CALL_FLAGS_NONE in its D-Bus calls, which occur for each method
defined by the input XML, and for proxy_set_property functions. This
means that if the daemon which implements the methods checks for
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION and only does interactive
authorization if that flag is present, users of the generated code have
no way to cause the daemon to use interactive authorization (e.g. polkit
dialogs).
If we simply changed the generated code to always use
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, its users would have no
way to disallow interactive authorization (except for manually calling
the D-Bus method themselves).
So instead, this commit adds a GDBusCallFlags argument to method call
functions. Since this is an API break which will require changes in
projects using gdbus-codegen code, the change is conditional on the
command line argument --glib-min-version having the value 2.64 or
higher.
The impetus for this change is that I'm changing accountsservice to
properly respect G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, and
libaccountsservice uses generated code for D-Bus method calls. So
these changes will allow libaccountsservice to continue allowing
interactive authorization, and avoid breaking any users of it which
expect that. See
https://gitlab.freedesktop.org/accountsservice/accountsservice/merge_requests/46
It might make sense to also let GDBusCallFlags be specified for property
set operations, but that is not needed in the case of accountsservice,
and would require significant work and breaking API in multiple places.
Similarly, the generated code currently hard codes -1 as the timeout
value when calling g_dbus_proxy_call*(). Add a timeout_msec argument so
the user of the generated code can specify the timeout as well.
Also, test this new API. In gio/tests/codegen.py we test that the new
arguments are generated if and only of --glib-min-version is used with a
value greater than or equal to 2.64, and in gio/tests/meson.build we
test that the generated code with the new API can be linked against.
The test_unix_fd_list() test also needed modification to continue
working now that we're using gdbus-test-codegen.c with code generated
with --glib-min-version=2.64 in one test.
Finally, update the docs for gdbus-codegen to explain the effect of
using --glib-min-version 2.64, both from this commit and from
"gdbus-codegen: Emit GUnixFDLists if an arg has type `h` w/
min-version".
2019-11-25 20:51:13 +01:00
|
|
|
def test_call_flags_and_timeout_method_args(self):
|
|
|
|
"""Test that generated method call functions have @call_flags and
|
|
|
|
@timeout_msec args if and only if GLib >= 2.64.
|
|
|
|
"""
|
2020-11-17 16:07:09 +01:00
|
|
|
interface_xml = """
|
gdbus-codegen: Add call_flags and timeout_msec args
Currently the code generated by gdbus-codegen uses
G_DBUS_CALL_FLAGS_NONE in its D-Bus calls, which occur for each method
defined by the input XML, and for proxy_set_property functions. This
means that if the daemon which implements the methods checks for
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION and only does interactive
authorization if that flag is present, users of the generated code have
no way to cause the daemon to use interactive authorization (e.g. polkit
dialogs).
If we simply changed the generated code to always use
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, its users would have no
way to disallow interactive authorization (except for manually calling
the D-Bus method themselves).
So instead, this commit adds a GDBusCallFlags argument to method call
functions. Since this is an API break which will require changes in
projects using gdbus-codegen code, the change is conditional on the
command line argument --glib-min-version having the value 2.64 or
higher.
The impetus for this change is that I'm changing accountsservice to
properly respect G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, and
libaccountsservice uses generated code for D-Bus method calls. So
these changes will allow libaccountsservice to continue allowing
interactive authorization, and avoid breaking any users of it which
expect that. See
https://gitlab.freedesktop.org/accountsservice/accountsservice/merge_requests/46
It might make sense to also let GDBusCallFlags be specified for property
set operations, but that is not needed in the case of accountsservice,
and would require significant work and breaking API in multiple places.
Similarly, the generated code currently hard codes -1 as the timeout
value when calling g_dbus_proxy_call*(). Add a timeout_msec argument so
the user of the generated code can specify the timeout as well.
Also, test this new API. In gio/tests/codegen.py we test that the new
arguments are generated if and only of --glib-min-version is used with a
value greater than or equal to 2.64, and in gio/tests/meson.build we
test that the generated code with the new API can be linked against.
The test_unix_fd_list() test also needed modification to continue
working now that we're using gdbus-test-codegen.c with code generated
with --glib-min-version=2.64 in one test.
Finally, update the docs for gdbus-codegen to explain the effect of
using --glib-min-version 2.64, both from this commit and from
"gdbus-codegen: Emit GUnixFDLists if an arg has type `h` w/
min-version".
2019-11-25 20:51:13 +01:00
|
|
|
<node>
|
|
|
|
<interface name="org.project.UsefulInterface">
|
|
|
|
<method name="UsefulMethod"/>
|
|
|
|
</interface>
|
2020-11-17 16:07:09 +01:00
|
|
|
</node>"""
|
gdbus-codegen: Add call_flags and timeout_msec args
Currently the code generated by gdbus-codegen uses
G_DBUS_CALL_FLAGS_NONE in its D-Bus calls, which occur for each method
defined by the input XML, and for proxy_set_property functions. This
means that if the daemon which implements the methods checks for
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION and only does interactive
authorization if that flag is present, users of the generated code have
no way to cause the daemon to use interactive authorization (e.g. polkit
dialogs).
If we simply changed the generated code to always use
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, its users would have no
way to disallow interactive authorization (except for manually calling
the D-Bus method themselves).
So instead, this commit adds a GDBusCallFlags argument to method call
functions. Since this is an API break which will require changes in
projects using gdbus-codegen code, the change is conditional on the
command line argument --glib-min-version having the value 2.64 or
higher.
The impetus for this change is that I'm changing accountsservice to
properly respect G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, and
libaccountsservice uses generated code for D-Bus method calls. So
these changes will allow libaccountsservice to continue allowing
interactive authorization, and avoid breaking any users of it which
expect that. See
https://gitlab.freedesktop.org/accountsservice/accountsservice/merge_requests/46
It might make sense to also let GDBusCallFlags be specified for property
set operations, but that is not needed in the case of accountsservice,
and would require significant work and breaking API in multiple places.
Similarly, the generated code currently hard codes -1 as the timeout
value when calling g_dbus_proxy_call*(). Add a timeout_msec argument so
the user of the generated code can specify the timeout as well.
Also, test this new API. In gio/tests/codegen.py we test that the new
arguments are generated if and only of --glib-min-version is used with a
value greater than or equal to 2.64, and in gio/tests/meson.build we
test that the generated code with the new API can be linked against.
The test_unix_fd_list() test also needed modification to continue
working now that we're using gdbus-test-codegen.c with code generated
with --glib-min-version=2.64 in one test.
Finally, update the docs for gdbus-codegen to explain the effect of
using --glib-min-version 2.64, both from this commit and from
"gdbus-codegen: Emit GUnixFDLists if an arg has type `h` w/
min-version".
2019-11-25 20:51:13 +01:00
|
|
|
|
2020-01-27 11:11:46 +01:00
|
|
|
# Try without specifying --glib-min-required.
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--header"
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 0)
|
|
|
|
self.assertEqual(result.out.strip().count("gint timeout_msec,"), 0)
|
gdbus-codegen: Add call_flags and timeout_msec args
Currently the code generated by gdbus-codegen uses
G_DBUS_CALL_FLAGS_NONE in its D-Bus calls, which occur for each method
defined by the input XML, and for proxy_set_property functions. This
means that if the daemon which implements the methods checks for
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION and only does interactive
authorization if that flag is present, users of the generated code have
no way to cause the daemon to use interactive authorization (e.g. polkit
dialogs).
If we simply changed the generated code to always use
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, its users would have no
way to disallow interactive authorization (except for manually calling
the D-Bus method themselves).
So instead, this commit adds a GDBusCallFlags argument to method call
functions. Since this is an API break which will require changes in
projects using gdbus-codegen code, the change is conditional on the
command line argument --glib-min-version having the value 2.64 or
higher.
The impetus for this change is that I'm changing accountsservice to
properly respect G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, and
libaccountsservice uses generated code for D-Bus method calls. So
these changes will allow libaccountsservice to continue allowing
interactive authorization, and avoid breaking any users of it which
expect that. See
https://gitlab.freedesktop.org/accountsservice/accountsservice/merge_requests/46
It might make sense to also let GDBusCallFlags be specified for property
set operations, but that is not needed in the case of accountsservice,
and would require significant work and breaking API in multiple places.
Similarly, the generated code currently hard codes -1 as the timeout
value when calling g_dbus_proxy_call*(). Add a timeout_msec argument so
the user of the generated code can specify the timeout as well.
Also, test this new API. In gio/tests/codegen.py we test that the new
arguments are generated if and only of --glib-min-version is used with a
value greater than or equal to 2.64, and in gio/tests/meson.build we
test that the generated code with the new API can be linked against.
The test_unix_fd_list() test also needed modification to continue
working now that we're using gdbus-test-codegen.c with code generated
with --glib-min-version=2.64 in one test.
Finally, update the docs for gdbus-codegen to explain the effect of
using --glib-min-version 2.64, both from this commit and from
"gdbus-codegen: Emit GUnixFDLists if an arg has type `h` w/
min-version".
2019-11-25 20:51:13 +01:00
|
|
|
|
2020-01-27 11:11:46 +01:00
|
|
|
# Specify an old --glib-min-required.
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml,
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
"--header",
|
|
|
|
"--glib-min-required",
|
|
|
|
"2.32",
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 0)
|
|
|
|
self.assertEqual(result.out.strip().count("gint timeout_msec,"), 0)
|
gdbus-codegen: Add call_flags and timeout_msec args
Currently the code generated by gdbus-codegen uses
G_DBUS_CALL_FLAGS_NONE in its D-Bus calls, which occur for each method
defined by the input XML, and for proxy_set_property functions. This
means that if the daemon which implements the methods checks for
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION and only does interactive
authorization if that flag is present, users of the generated code have
no way to cause the daemon to use interactive authorization (e.g. polkit
dialogs).
If we simply changed the generated code to always use
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, its users would have no
way to disallow interactive authorization (except for manually calling
the D-Bus method themselves).
So instead, this commit adds a GDBusCallFlags argument to method call
functions. Since this is an API break which will require changes in
projects using gdbus-codegen code, the change is conditional on the
command line argument --glib-min-version having the value 2.64 or
higher.
The impetus for this change is that I'm changing accountsservice to
properly respect G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, and
libaccountsservice uses generated code for D-Bus method calls. So
these changes will allow libaccountsservice to continue allowing
interactive authorization, and avoid breaking any users of it which
expect that. See
https://gitlab.freedesktop.org/accountsservice/accountsservice/merge_requests/46
It might make sense to also let GDBusCallFlags be specified for property
set operations, but that is not needed in the case of accountsservice,
and would require significant work and breaking API in multiple places.
Similarly, the generated code currently hard codes -1 as the timeout
value when calling g_dbus_proxy_call*(). Add a timeout_msec argument so
the user of the generated code can specify the timeout as well.
Also, test this new API. In gio/tests/codegen.py we test that the new
arguments are generated if and only of --glib-min-version is used with a
value greater than or equal to 2.64, and in gio/tests/meson.build we
test that the generated code with the new API can be linked against.
The test_unix_fd_list() test also needed modification to continue
working now that we're using gdbus-test-codegen.c with code generated
with --glib-min-version=2.64 in one test.
Finally, update the docs for gdbus-codegen to explain the effect of
using --glib-min-version 2.64, both from this commit and from
"gdbus-codegen: Emit GUnixFDLists if an arg has type `h` w/
min-version".
2019-11-25 20:51:13 +01:00
|
|
|
|
2020-01-27 11:11:46 +01:00
|
|
|
# Specify a --glib-min-required ≥ 2.64. The two arguments should be
|
gdbus-codegen: Add call_flags and timeout_msec args
Currently the code generated by gdbus-codegen uses
G_DBUS_CALL_FLAGS_NONE in its D-Bus calls, which occur for each method
defined by the input XML, and for proxy_set_property functions. This
means that if the daemon which implements the methods checks for
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION and only does interactive
authorization if that flag is present, users of the generated code have
no way to cause the daemon to use interactive authorization (e.g. polkit
dialogs).
If we simply changed the generated code to always use
G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, its users would have no
way to disallow interactive authorization (except for manually calling
the D-Bus method themselves).
So instead, this commit adds a GDBusCallFlags argument to method call
functions. Since this is an API break which will require changes in
projects using gdbus-codegen code, the change is conditional on the
command line argument --glib-min-version having the value 2.64 or
higher.
The impetus for this change is that I'm changing accountsservice to
properly respect G_DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, and
libaccountsservice uses generated code for D-Bus method calls. So
these changes will allow libaccountsservice to continue allowing
interactive authorization, and avoid breaking any users of it which
expect that. See
https://gitlab.freedesktop.org/accountsservice/accountsservice/merge_requests/46
It might make sense to also let GDBusCallFlags be specified for property
set operations, but that is not needed in the case of accountsservice,
and would require significant work and breaking API in multiple places.
Similarly, the generated code currently hard codes -1 as the timeout
value when calling g_dbus_proxy_call*(). Add a timeout_msec argument so
the user of the generated code can specify the timeout as well.
Also, test this new API. In gio/tests/codegen.py we test that the new
arguments are generated if and only of --glib-min-version is used with a
value greater than or equal to 2.64, and in gio/tests/meson.build we
test that the generated code with the new API can be linked against.
The test_unix_fd_list() test also needed modification to continue
working now that we're using gdbus-test-codegen.c with code generated
with --glib-min-version=2.64 in one test.
Finally, update the docs for gdbus-codegen to explain the effect of
using --glib-min-version 2.64, both from this commit and from
"gdbus-codegen: Emit GUnixFDLists if an arg has type `h` w/
min-version".
2019-11-25 20:51:13 +01:00
|
|
|
# present for both the async and sync method call functions.
|
2020-11-17 16:07:09 +01:00
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml,
|
|
|
|
"--output",
|
|
|
|
"/dev/stdout",
|
|
|
|
"--header",
|
|
|
|
"--glib-min-required",
|
|
|
|
"2.64",
|
|
|
|
)
|
|
|
|
self.assertEqual("", result.err)
|
|
|
|
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 2)
|
|
|
|
self.assertEqual(result.out.strip().count("gint timeout_msec,"), 2)
|
|
|
|
|
2023-06-30 16:25:01 +02:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_signal_id_simple_signal(self):
|
|
|
|
"""Test that signals IDs are used to emit signals"""
|
|
|
|
interface_xml = """
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.UsefulInterface">
|
|
|
|
<signal name="SimpleSignal"/>
|
|
|
|
</interface>
|
|
|
|
<interface name="org.project.OtherIface">
|
|
|
|
<signal name="SimpleSignal"/>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
|
|
|
|
|
|
|
|
for iface in ["USEFUL_INTERFACE", "OTHER_IFACE"]:
|
|
|
|
enum_name = f"_ORG_PROJECT_{iface}_SIGNALS"
|
|
|
|
enum_item = f"_ORG_PROJECT_{iface}_SIMPLE_SIGNAL"
|
|
|
|
self.assertIs(stripped_out.count(f"{enum_item},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{enum_name}[{enum_item}] ="), 1)
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count(
|
|
|
|
f" g_signal_emit (object, {enum_name}[{enum_item}], 0);"
|
|
|
|
),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_signal_id_multiple_signals_types(self):
|
|
|
|
"""Test that signals IDs are used to emit signals for all types"""
|
|
|
|
|
|
|
|
signal_template = "<signal name='{}'><arg name='{}' type='{}'/></signal>"
|
|
|
|
generated_signals = [
|
|
|
|
signal_template.format(
|
|
|
|
f"SingleArgSignal{t.upper()}", f"an_{t}", props.get("variant_type", t)
|
|
|
|
)
|
|
|
|
for t, props in self.ARGUMENTS_TYPES.items()
|
|
|
|
]
|
|
|
|
|
|
|
|
interface_xml = f"""
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.SignalingIface">
|
|
|
|
<signal name="NoArgSignal" />
|
|
|
|
{''.join(generated_signals)}
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
|
|
|
|
|
|
|
|
iface = "SIGNALING_IFACE"
|
|
|
|
for t in self.ARGUMENTS_TYPES.keys():
|
|
|
|
enum_name = f"_ORG_PROJECT_{iface}_SIGNALS"
|
|
|
|
enum_item = f"_ORG_PROJECT_{iface}_SINGLE_ARG_SIGNAL_{t.upper()}"
|
|
|
|
self.assertIs(stripped_out.count(f"{enum_item},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{enum_name}[{enum_item}] ="), 1)
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count(
|
|
|
|
f" g_signal_emit (object, {enum_name}[{enum_item}], 0, arg_an_{t});"
|
|
|
|
),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_signal_id_multiple_signal_args_types(self):
|
|
|
|
"""Test that signals IDs are used to emit signals for all types"""
|
|
|
|
|
|
|
|
generated_args = [
|
|
|
|
f"<arg name='an_{t}' type='{props.get('variant_type', t)}'/>\n"
|
|
|
|
for t, props in self.ARGUMENTS_TYPES.items()
|
|
|
|
]
|
|
|
|
|
|
|
|
interface_xml = f"""
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.SignalingIface">
|
|
|
|
<signal name="SignalWithManyArgs">
|
|
|
|
{''.join(generated_args)}
|
|
|
|
</signal>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
|
|
|
|
|
|
|
|
iface = "SIGNALING_IFACE"
|
|
|
|
enum_name = f"_ORG_PROJECT_{iface}_SIGNALS"
|
|
|
|
enum_item = f"_ORG_PROJECT_{iface}_SIGNAL_WITH_MANY_ARGS"
|
|
|
|
self.assertIs(stripped_out.count(f"{enum_item},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{enum_name}[{enum_item}] ="), 1)
|
|
|
|
|
|
|
|
args = ", ".join([f"arg_an_{t}" for t in self.ARGUMENTS_TYPES.keys()])
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count(
|
|
|
|
f" g_signal_emit (object, {enum_name}[{enum_item}], 0, {args});"
|
|
|
|
),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
|
2023-06-30 02:52:11 +02:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_signals_marshaller_simple_signal(self):
|
|
|
|
"""Test that signals marshaller is generated for simple signal"""
|
|
|
|
interface_xml = """
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.SignalingIface">
|
|
|
|
<signal name="SimpleSignal"/>
|
|
|
|
</interface>
|
|
|
|
<interface name="org.project.OtherSignalingIface">
|
|
|
|
<signal name="SimpleSignal"/>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = "org_project_signaling_iface_signal_marshal_simple_signal"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
|
|
|
func_name = "org_project_other_signaling_iface_signal_marshal_simple_signal"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_signals_marshaller_single_typed_args(self):
|
|
|
|
"""Test that signals marshaller is generated for each known type"""
|
|
|
|
for t, props in self.ARGUMENTS_TYPES.items():
|
|
|
|
camel_type = t[0].upper() + t[1:]
|
|
|
|
interface_xml = f"""
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.SignalingIface">
|
|
|
|
<signal name="SimpleSignal"/>
|
|
|
|
<signal name="SingleArgSignal{camel_type}">
|
|
|
|
<arg name="arg_{t}" type="{props.get("variant_type", t)}"/>
|
|
|
|
</signal>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertEqual(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = (
|
|
|
|
f"org_project_signaling_iface_signal_marshal_single_arg_signal_{t}"
|
|
|
|
)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count(
|
2023-06-30 19:24:03 +02:00
|
|
|
f"g_marshal_value_peek_{props['value_type']} (param_values + 1)"
|
2023-06-30 02:52:11 +02:00
|
|
|
),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_signals_marshallers_multiple_args(self):
|
|
|
|
"""Test that signals marshallers are generated"""
|
|
|
|
generated_args = [
|
|
|
|
f"<arg name='an_{t}' type='{props.get('variant_type', t)}'/>\n"
|
|
|
|
for t, props in self.ARGUMENTS_TYPES.items()
|
|
|
|
]
|
|
|
|
|
|
|
|
interface_xml = f"""
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.SignalingIface">
|
|
|
|
<signal name="SimpleSignal"/>
|
|
|
|
<signal name="SignalWithManyArgs">
|
|
|
|
{''.join(generated_args)}
|
|
|
|
</signal>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = f"org_project_signaling_iface_signal_marshal_simple_signal"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
|
|
|
func_name = f"org_project_signaling_iface_signal_marshal_signal_with_many_args"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
|
|
|
# Check access to MultipleArgsSignal arguments
|
|
|
|
index = 1
|
|
|
|
for props in self.ARGUMENTS_TYPES.values():
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count(
|
2023-06-30 19:24:03 +02:00
|
|
|
f"g_marshal_value_peek_{props['value_type']} (param_values + {index})"
|
2023-06-30 02:52:11 +02:00
|
|
|
),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
index += 1
|
|
|
|
|
2023-06-30 06:19:17 +02:00
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_methods_marshaller_simple_method(self):
|
|
|
|
"""Test that methods marshaller is generated for simple method"""
|
|
|
|
interface_xml = """
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.CallableIface">
|
|
|
|
<method name="SimpleMethod"/>
|
|
|
|
</interface>
|
|
|
|
<interface name="org.project.OtherCallableIface">
|
|
|
|
<method name="SimpleMethod"/>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = "org_project_callable_iface_method_marshal_simple_method"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
|
|
|
func_name = "org_project_other_callable_iface_method_marshal_simple_method"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
2023-06-30 19:24:03 +02:00
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count("g_marshal_value_peek_object (param_values + 1)"), 2
|
|
|
|
)
|
2023-06-30 06:19:17 +02:00
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 2
|
|
|
|
)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_methods_marshaller_single_typed_in_args(self):
|
|
|
|
"""Test that methods marshallers are generated for each known type"""
|
|
|
|
for t, props in self.ARGUMENTS_TYPES.items():
|
|
|
|
camel_type = t[0].upper() + t[1:]
|
|
|
|
interface_xml = f"""
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.UsefulInterface">
|
|
|
|
<method name="SingleArgMethod{camel_type}">
|
|
|
|
<arg name="arg_{t}" type="{props.get("variant_type", t)}"/>
|
|
|
|
</method>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertEqual(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = (
|
|
|
|
f"org_project_useful_interface_method_marshal_single_arg_method_{t}"
|
|
|
|
)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
self.assertIs(
|
2023-06-30 19:24:03 +02:00
|
|
|
stripped_out.count("g_marshal_value_peek_object (param_values + 1)"), 1
|
2023-06-30 06:19:17 +02:00
|
|
|
)
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
|
|
|
)
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count(
|
2023-06-30 19:24:03 +02:00
|
|
|
f"g_marshal_value_peek_{props['value_type']} (param_values + 2)"
|
2023-06-30 06:19:17 +02:00
|
|
|
),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_methods_marshaller_single_typed_out_args(self):
|
|
|
|
"""Test that methods marshallers are generated for each known type"""
|
|
|
|
for t, props in self.ARGUMENTS_TYPES.items():
|
|
|
|
camel_type = t[0].upper() + t[1:]
|
|
|
|
interface_xml = f"""
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.UsefulInterface">
|
|
|
|
<method name="SingleArgMethod{camel_type}">
|
|
|
|
<arg name="arg_{t}" type="{props.get("variant_type", t)}" direction="out"/>
|
|
|
|
</method>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertEqual(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = (
|
|
|
|
f"org_project_useful_interface_method_marshal_single_arg_method_{t}"
|
|
|
|
)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
self.assertIs(
|
2023-06-30 19:24:03 +02:00
|
|
|
stripped_out.count("g_marshal_value_peek_object (param_values + 1)"), 1
|
2023-06-30 06:19:17 +02:00
|
|
|
)
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
|
|
|
)
|
|
|
|
self.assertIs(stripped_out.count("(param_values + 2)"), 0)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_methods_marshallers_multiple_in_args(self):
|
|
|
|
"""Test that methods marshallers are generated"""
|
|
|
|
generated_args = [
|
|
|
|
f"<arg name='an_{t}' type='{props.get('variant_type', t)}'/>\n"
|
|
|
|
for t, props in self.ARGUMENTS_TYPES.items()
|
|
|
|
]
|
|
|
|
|
|
|
|
interface_xml = f"""
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.CallableIface">
|
|
|
|
<method name="MethodWithManyArgs">
|
|
|
|
{''.join(generated_args)}
|
|
|
|
</method>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = f"org_project_callable_iface_method_marshal_method_with_many_args"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
|
|
|
# Check access to MultipleArgsMethod arguments
|
|
|
|
index = 1
|
|
|
|
self.assertIs(
|
2023-06-30 19:24:03 +02:00
|
|
|
stripped_out.count(f"g_marshal_value_peek_object (param_values + {index})"),
|
|
|
|
1,
|
2023-06-30 06:19:17 +02:00
|
|
|
)
|
|
|
|
index += 1
|
|
|
|
|
|
|
|
for props in self.ARGUMENTS_TYPES.values():
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count(
|
2023-06-30 19:24:03 +02:00
|
|
|
f"g_marshal_value_peek_{props['value_type']} (param_values + {index})"
|
2023-06-30 06:19:17 +02:00
|
|
|
),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
index += 1
|
|
|
|
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
|
|
|
)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_methods_marshallers_multiple_out_args(self):
|
|
|
|
"""Test that methods marshallers are generated"""
|
|
|
|
generated_args = [
|
|
|
|
f"<arg name='an_{t}' type='{props.get('variant_type', t)}' direction='out'/>\n"
|
|
|
|
for t, props in self.ARGUMENTS_TYPES.items()
|
|
|
|
]
|
|
|
|
|
|
|
|
interface_xml = f"""
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.CallableIface">
|
|
|
|
<method name="MethodWithManyArgs">
|
|
|
|
{''.join(generated_args)}
|
|
|
|
</method>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = f"org_project_callable_iface_method_marshal_method_with_many_args"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
|
|
|
# Check access to MultipleArgsMethod arguments
|
|
|
|
index = 1
|
|
|
|
self.assertIs(
|
2023-06-30 19:24:03 +02:00
|
|
|
stripped_out.count(f"g_marshal_value_peek_object (param_values + {index})"),
|
|
|
|
1,
|
2023-06-30 06:19:17 +02:00
|
|
|
)
|
|
|
|
index += 1
|
|
|
|
|
|
|
|
for index in range(index, len(self.ARGUMENTS_TYPES)):
|
|
|
|
self.assertIs(stripped_out.count(f"(param_values + {index})"), 0)
|
|
|
|
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
|
|
|
)
|
|
|
|
|
|
|
|
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
|
|
|
def test_generate_methods_marshallers_with_unix_fds(self):
|
|
|
|
"""Test an interface with `h` arguments"""
|
|
|
|
interface_xml = """
|
|
|
|
<node>
|
|
|
|
<interface name="test.FDPassing">
|
|
|
|
<method name="HelloFD">
|
|
|
|
<annotation name="org.gtk.GDBus.C.UnixFD" value="1"/>
|
|
|
|
<arg name="greeting" direction="in" type="s"/>
|
|
|
|
<arg name="response" direction="out" type="s"/>
|
|
|
|
</method>
|
|
|
|
</interface>
|
|
|
|
</node>"""
|
|
|
|
|
|
|
|
result = self.runCodegenWithInterface(
|
|
|
|
interface_xml, "--output", "/dev/stdout", "--body"
|
|
|
|
)
|
|
|
|
stripped_out = result.out.strip()
|
|
|
|
self.assertFalse(result.err)
|
|
|
|
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
|
|
|
|
|
|
|
func_name = f"test_fdpassing_method_marshal_hello_fd"
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
|
|
|
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
|
|
|
|
|
|
|
index = 1
|
|
|
|
self.assertIs(
|
2023-06-30 19:24:03 +02:00
|
|
|
stripped_out.count(f"g_marshal_value_peek_object (param_values + {index})"),
|
|
|
|
1,
|
2023-06-30 06:19:17 +02:00
|
|
|
)
|
|
|
|
index += 1
|
|
|
|
|
|
|
|
self.assertIs(
|
2023-06-30 19:24:03 +02:00
|
|
|
stripped_out.count(f"g_marshal_value_peek_object (param_values + {index})"),
|
|
|
|
1,
|
2023-06-30 06:19:17 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
index += 1
|
|
|
|
self.assertIs(
|
2023-06-30 19:24:03 +02:00
|
|
|
stripped_out.count(f"g_marshal_value_peek_string (param_values + {index})"),
|
|
|
|
1,
|
2023-06-30 06:19:17 +02:00
|
|
|
)
|
|
|
|
index += 1
|
|
|
|
|
|
|
|
self.assertIs(
|
|
|
|
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
|
|
|
)
|
|
|
|
|
2022-02-15 13:14:49 +01:00
|
|
|
def test_generate_valid_docbook(self):
|
|
|
|
"""Test the basic functionality of the docbook generator."""
|
|
|
|
xml_contents = """
|
|
|
|
<node>
|
|
|
|
<interface name="org.project.Bar.Frobnicator">
|
|
|
|
<!-- Resize:
|
|
|
|
@size: New partition size in bytes, 0 for maximal size.
|
|
|
|
@options: Options.
|
|
|
|
@since 2.7.2
|
|
|
|
|
|
|
|
Resizes the partition.
|
|
|
|
|
|
|
|
The partition will not change its position but might be slightly bigger
|
|
|
|
than requested due to sector counts and alignment (e.g. 1MiB).
|
|
|
|
If the requested size can't be allocated it results in an error.
|
|
|
|
The maximal size can automatically be set by using 0 as size.
|
|
|
|
-->
|
|
|
|
<method name="Resize">
|
|
|
|
<arg name="size" direction="in" type="t"/>
|
|
|
|
<arg name="options" direction="in" type="a{sv}"/>
|
|
|
|
</method>
|
|
|
|
</interface>
|
|
|
|
</node>
|
|
|
|
"""
|
|
|
|
res = self.runCodegenWithInterface(
|
|
|
|
xml_contents,
|
|
|
|
"--generate-docbook",
|
|
|
|
"test",
|
|
|
|
)
|
|
|
|
self.assertEqual("", res.err)
|
|
|
|
self.assertEqual("", res.out)
|
|
|
|
with open("test-org.project.Bar.Frobnicator.xml", "r") as f:
|
|
|
|
self.assertTrue(ET.parse(f) is not None)
|
|
|
|
|
2020-11-17 16:07:09 +01:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2019-12-02 15:05:22 +01:00
|
|
|
unittest.main(testRunner=taptestrunner.TAPTestRunner())
|