diff options
-rw-r--r-- | Makefile.am | 47 | ||||
-rwxr-xr-x | src/sbus/sbus_codegen | 506 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_meta.c | 64 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_meta.h | 82 | ||||
-rw-r--r-- | src/tests/sbus_codegen_tests.c | 166 | ||||
-rwxr-xr-x | src/tests/sbus_codegen_tests.xml | 45 | ||||
-rw-r--r-- | src/tests/sbus_codegen_tests_generated.c | 79 | ||||
-rw-r--r-- | src/tests/sbus_codegen_tests_generated.h | 24 |
8 files changed, 1012 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 07bd4e816..d7e624367 100644 --- a/Makefile.am +++ b/Makefile.am @@ -142,7 +142,8 @@ if HAVE_CHECK ipa_hbac-tests \ sss_idmap-tests \ responder_socket_access-tests \ - safe-format-tests + safe-format-tests \ + sbus_codegen_tests if BUILD_SSH non_interactive_check_based_tests += sysdb_ssh-tests @@ -449,6 +450,7 @@ dist_noinst_HEADERS = \ src/responder/ssh/sshsrv_private.h \ src/sbus/sbus_client.h \ src/sbus/sssd_dbus.h \ + src/sbus/sssd_dbus_meta.h \ src/sbus/sssd_dbus_private.h \ src/db/sysdb.h \ src/db/sysdb_sudo.h \ @@ -580,6 +582,7 @@ libsss_util_la_SOURCES = \ src/sbus/sbus_client.c \ src/sbus/sssd_dbus_common.c \ src/sbus/sssd_dbus_connection.c \ + src/sbus/sssd_dbus_meta.c \ src/sbus/sssd_dbus_server.c \ src/util/util.c \ src/util/memory.c \ @@ -658,6 +661,37 @@ include_HEADERS = \ src/sss_client/idmap/sss_nss_idmap.h #################### +# Sbus Codegen # +#################### + +# Yes, the goal here is that the generated files end up in $(srcdir) +# not $(builddir). Always use $(srcdir) here. +CODEGEN_XML = \ + $(srcdir)/src/tests/sbus_codegen_tests.xml + +SBUS_CODEGEN = src/sbus/sbus_codegen + +EXTRA_DIST += \ + $(SBUS_CODEGEN) \ + $(CODEGEN_XML) + +SUFFIXES = .xml _generated.h _generated.c + +.xml_generated.h: + $(srcdir)/$(SBUS_CODEGEN) --mode=header --output=$@ $< +.xml_generated.c: + $(srcdir)/$(SBUS_CODEGEN) --mode=source --output=$@ $< + +# Regenerate when codegen changes +CODEGEN_CODE = \ + $(CODEGEN_XML:.xml=_generated.c) \ + $(CODEGEN_XML:.xml=_generated.h) + +$(CODEGEN_CODE): $(SBUS_CODEGEN) + +BUILT_SOURCES = $(CODEGEN_CODE) + +#################### # Program Binaries # #################### sssd_SOURCES = \ @@ -1298,6 +1332,17 @@ krb5_child_test_LDADD = \ $(SSSD_INTERNAL_LTLIBS) \ libsss_test_common.la +sbus_codegen_tests_SOURCES = \ + src/tests/sbus_codegen_tests.c \ + src/tests/sbus_codegen_tests_generated.c \ + src/tests/sbus_codegen_tests_generated.h +sbus_codegen_tests_CFLAGS = \ + $(CHECK_CFLAGS) +sbus_codegen_tests_LDADD = \ + $(SSSD_INTERNAL_LTLIBS) \ + $(SSSD_LIBS) \ + $(CHECK_LIBS) + if HAVE_CMOCKA TEST_MOCK_RESP_OBJ = \ diff --git a/src/sbus/sbus_codegen b/src/sbus/sbus_codegen new file mode 100755 index 000000000..a1e2d02a0 --- /dev/null +++ b/src/sbus/sbus_codegen @@ -0,0 +1,506 @@ +#!/usr/bin/python + +# +# Authors: +# Stef Walter <stefw@redhat.com> +# +# Copyright (C) 2014 Red Hat +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# +# Some parser code from GLib +# +# Copyright (C) 2008-2011 Red Hat, Inc. +# +# 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 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., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307, USA. +# +# Portions by: David Zeuthen <davidz@redhat.com> +# + +# +# DBus interfaces are defined here: +# +# http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format +# +# The introspection data format has become the standard way to represent a +# DBus interface. For many examples see /usr/share/dbus-1/interfaces/ on a +# typical linux machine. +# +# A word about annotations. These are extra flags or values that can be +# assigned to anything. So far, the codegen supports this annotation: +# +# org.freedesktop.DBus.GLib.CSymbol +# - An annotation specified in the specification that tells us what C symbol +# to generate for a given interface or method. By default the codegen will +# build up a symbol name from the DBus name. +# + +import optparse +import os +import re +import StringIO +import sys +import xml.parsers.expat + +# ----------------------------------------------------------------------------- +# Objects + +class DBusXmlException(Exception): + line = 0 + file = None + + # Lets us print problems like a compiler would + def __str__(self): + message = Exception.__str__(self) + if self.file and self.line: + return "%s:%d: %s" % (self.file, self.line, message) + elif self.file: + return "%s: %s" % (self.file, message) + else: + return message + +class Base: + def __init__(self, name): + if not name: + raise DBusXmlException('No name on element') + self.name = name + self.annotations = { } + def c_name(self): + return self.annotations.get("org.freedesktop.DBus.GLib.CSymbol", self.name) + +class Arg(Base): + def __init__(self, method, name, signature): + Base.__init__(self, name) + self.method = method + self.signature = signature + +class Method(Base): + def __init__(self, iface, name): + Base.__init__(self, name) + self.iface = iface + self.in_args = [] + self.out_args = [] + def fq_c_name(self): + return "%s_%s" % (self.iface.c_name(), self.c_name()) + +class Signal(Base): + def __init__(self, iface, name): + Base.__init__(self, name) + self.iface = iface + self.args = [] + def fq_c_name(self): + return "%s_%s" % (self.iface.c_name(), self.c_name()) + +class Property(Base): + def __init__(self, iface, name, signature, access): + Base.__init__(self, name) + self.iface = iface + self.signature = signature + self.readable = False + self.writable = False + if access == 'readwrite': + self.readable = True + self.writable = True + elif access == 'read': + self.readable = True + elif access == 'write': + self.writable = True + else: + raise DBusXmlException('Invalid access type %s'%self.access) + def fq_c_name(self): + return "%s_%s" % (self.iface.c_name(), self.c_name()) + +class Interface(Base): + def __init__(self, name): + Base.__init__(self, name) + self.methods = [] + self.signals = [] + self.properties = [] + def c_name(self): + return self.annotations.get("org.freedesktop.DBus.GLib.CSymbol", + self.name.replace(".", "_")) + +# ----------------------------------------------------------------------------- +# Code Generation + +def out(format, *args): + str = format % args + sys.stdout.write(str) + sys.stdout.write("\n") + +def source_args(parent, args, suffix): + out("") + out("/* arguments for %s.%s */", parent.iface.name, parent.name) + out("const struct sbus_arg_meta %s%s[] = {", parent.fq_c_name(), suffix) + for arg in args: + out(" { \"%s\", \"%s\" },", arg.name, arg.signature) + out(" { NULL, }") + out("};") + +def source_methods(iface, methods): + for meth in methods: + if meth.in_args: + source_args(meth, meth.in_args, "__in") + if meth.out_args: + source_args(meth, meth.out_args, "__out") + + out("") + out("/* methods for %s */", iface.name) + out("const struct sbus_method_meta %s__methods[] = {", iface.c_name()) + for meth in methods: + out(" {") + out(" \"%s\", /* name */", meth.name) + if meth.in_args: + out(" %s__in,", meth.fq_c_name()) + else: + out(" NULL, /* no in_args */") + if meth.out_args: + out(" %s__out,", meth.fq_c_name()) + else: + out(" NULL, /* no out_args */") + out(" },") + out(" { NULL, }") + out("};") + +def source_signals(iface, signals): + for sig in iface.signals: + if sig.args: + source_args(sig, sig.args, "__args") + + out("") + out("/* signals for %s */", iface.name) + out("const struct sbus_signal_meta %s__signals[] = {", iface.c_name()) + for sig in signals: + out(" {") + out(" \"%s\", /* name */", sig.name) + if sig.args: + out(" %s__args", sig.fq_c_name()) + else: + out(" NULL, /* no args */") + out(" },") + out(" { NULL, }") + out("};") + +def source_properties(iface, properties): + out("") + out("/* property info for %s */", iface.name) + out("const struct sbus_property_meta %s__properties[] = {", iface.c_name()) + for prop in properties: + out(" {") + out(" \"%s\", /* name */", prop.name) + out(" \"%s\", /* signature */", prop.signature) + if prop.readable and prop.writable: + out(" SBUS_PROPERTY_READABLE | SBUS_PROPERTY_WRITABLE,") + elif prop.readable: + out(" SBUS_PROPERTY_READABLE,") + elif prop.writable: + out(" SBUS_PROPERTY_WRITABLE,") + else: + assert False, "should not be reached" + out(" },") + out(" { NULL, }") + out("};") + +def header_interface(iface): + out("") + out("/* interface info for %s */", iface.name) + out("extern const struct sbus_interface_meta %s_meta;", iface.c_name()) + +def source_interface(iface): + out("") + out("/* interface info for %s */", iface.name) + out("const struct sbus_interface_meta %s_meta = {", iface.c_name()) + out(" \"%s\", /* name */", iface.name) + if iface.methods: + out(" %s__methods,", iface.c_name()) + else: + out(" NULL, /* no methods */") + if iface.signals: + out(" %s__signals,", iface.c_name()) + else: + out(" NULL, /* no signals */") + if iface.properties: + out(" %s__properties", iface.c_name()) + else: + out(" NULL, /* no propetries */") + out("};") + +def generate_source(ifaces, filename): + basename = os.path.basename(filename) + + out("/* The following definitions are auto-generated from %s */", basename) + out("") + + out("#include \"util/util.h\"") + out("#include \"sbus/sssd_dbus.h\"") + out("#include \"sbus/sssd_dbus_meta.h\"") + + for iface in ifaces: + + # The methods + if iface.methods: + source_methods(iface, iface.methods) + + # The signals array + if iface.signals: + source_signals(iface, iface.signals) + + # The properties array + if iface.properties: + source_properties(iface, iface.properties) + + # The sbus_interface structure + source_interface(iface) + +def generate_header(ifaces, filename): + basename = os.path.basename(filename) + guard = "__%s__" % re.sub(r'([^_A-Z0-9])', "_", basename.upper()) + + out("/* The following declarations are auto-generated from %s */", basename) + out("") + out("#ifndef %s", guard) + out("#define %s", guard) + out("") + out("#include \"sbus/sssd_dbus.h\"") + + out("") + out("/* ------------------------------------------------------------------------") + out(" * DBus Interface Metadata") + out(" *") + out(" * These structure definitions are filled in with the information about") + out(" * the interfaces, methods, properties and so on.") + out(" *") + out(" * The actual definitions are found in the accompanying C file next") + out(" * to this header.") + out(" */") + + for iface in ifaces: + header_interface(iface) + + out("") + out("#endif /* %s */", guard) + +# ----------------------------------------------------------------------------- +# XML Interface Parsing + +STATE_TOP = 'top' +STATE_NODE = 'node' +STATE_INTERFACE = 'interface' +STATE_METHOD = 'method' +STATE_SIGNAL = 'signal' +STATE_PROPERTY = 'property' +STATE_ARG = 'arg' +STATE_ANNOTATION = 'annotation' +STATE_IGNORED = 'ignored' + +def expect_attr(attrs, name): + if name not in attrs: + raise DBusXmlException("Missing attribute '%s'" % name) + if attrs[name] == "": + raise DBusXmlException("Empty attribute '%s'" % name) + return attrs[name] + +class DBusXMLParser: + def __init__(self, filename): + parser = xml.parsers.expat.ParserCreate() + parser.CommentHandler = self.handle_comment + parser.CharacterDataHandler = self.handle_char_data + parser.StartElementHandler = self.handle_start_element + parser.EndElementHandler = self.handle_end_element + + self.parsed_interfaces = [] + self.cur_object = None + + self.state = STATE_TOP + self.state_stack = [] + self.cur_object = None + self.cur_object_stack = [] + self.arg_count = 0 + + try: + with open(filename, "r") as f: + parser.ParseFile(f) + except DBusXmlException, ex: + ex.line = parser.CurrentLineNumber + ex.file = filename + raise + except xml.parsers.expat.ExpatError, ex: + exc = DBusXmlException(str(ex)) + exc.line = ex.lineno + exc.file = filename + raise exc + + def handle_comment(self, data): + pass + + def handle_char_data(self, data): + pass + + def handle_start_element(self, name, attrs): + old_state = self.state + old_cur_object = self.cur_object + if self.state == STATE_IGNORED: + self.state = STATE_IGNORED + elif self.cur_object and name == STATE_ANNOTATION: + val = attrs.get('value', '') + self.cur_object.annotations[expect_attr(attrs, 'name')] = val + self.state = STATE_IGNORED + elif self.state == STATE_TOP: + if name == STATE_NODE: + self.state = STATE_NODE + else: + self.state = STATE_IGNORED + elif self.state == STATE_NODE: + if name == STATE_INTERFACE: + self.state = STATE_INTERFACE + iface = Interface(expect_attr(attrs, 'name')) + self.cur_object = iface + self.parsed_interfaces.append(iface) + else: + self.state = STATE_IGNORED + + elif self.state == STATE_INTERFACE: + if name == STATE_METHOD: + self.state = STATE_METHOD + method = Method(self.cur_object, expect_attr(attrs, 'name')) + self.cur_object.methods.append(method) + self.cur_object = method + self.arg_count = 0 + elif name == STATE_SIGNAL: + self.state = STATE_SIGNAL + signal = Signal(self.cur_object, expect_attr(attrs, 'name')) + self.cur_object.signals.append(signal) + self.cur_object = signal + self.arg_count = 0 + elif name == STATE_PROPERTY: + self.state = STATE_PROPERTY + prop = Property(self.cur_object, + expect_attr(attrs, 'name'), + expect_attr(attrs, 'type'), + expect_attr(attrs, 'access')) + self.cur_object.properties.append(prop) + self.cur_object = prop + else: + self.state = STATE_IGNORED + + elif self.state == STATE_METHOD: + if name == STATE_ARG: + self.state = STATE_ARG + arg = Arg(self.cur_object, + expect_attr(attrs, 'name'), + expect_attr(attrs, 'type')) + direction = attrs.get('direction', 'in') + if direction == 'in': + self.cur_object.in_args.append(arg) + elif direction == 'out': + self.cur_object.out_args.append(arg) + else: + raise DBusXmlException('Invalid direction "%s"' % direction) + self.cur_object = arg + else: + self.state = STATE_IGNORED + + elif self.state == STATE_SIGNAL: + if name == STATE_ARG: + self.state = STATE_ARG + arg = Arg(self.cur_object, + expect_attr(attrs, 'name'), + expect_attr(attrs, 'type')) + self.cur_object.args.append(arg) + self.cur_object = arg + else: + self.state = STATE_IGNORED + + elif self.state == STATE_PROPERTY: + self.state = STATE_IGNORED + + elif self.state == STATE_ARG: + self.state = STATE_IGNORED + + else: + assert False, 'Unhandled state "%s" while entering element with name "%s"' % (self.state, name) + + self.state_stack.append(old_state) + self.cur_object_stack.append(old_cur_object) + + def handle_end_element(self, name): + self.state = self.state_stack.pop() + self.cur_object = self.cur_object_stack.pop() + +def parse_options(): + parser = optparse.OptionParser("usage: %prog [options] introspect.xml ...") + parser.set_description("sbus_codegen generates sbus interface structures \ + from standard XML Introspect data.") + parser.add_option("--mode", + dest="mode", default="header", + help="'header' or 'source' (default: header)", + metavar="MODE") + parser.add_option("--output", + dest="output", default=None, + help="Set output file name (default: stdout)", + metavar="FILE") + (options, args) = parser.parse_args() + + if not args: + print >> sys.stderr, "sbus_codegen: no input file specified" + sys.exit(2) + + if options.mode not in ["header", "source"]: + print >> sys.stderr, "sbus_codegen: specify --mode=header or --mode=source" + + return options, args + +def main(): + options, args = parse_options() + + if options.output: + sys.stdout = buf = StringIO.StringIO() + + for filename in args: + parser = DBusXMLParser(filename) + + if options.mode == "header": + generate_header(parser.parsed_interfaces, filename) + elif options.mode == "source": + generate_source(parser.parsed_interfaces, filename) + else: + assert False, "should not be reached" + + # Write output at end to be nice to 'make' + if options.output: + output = open(options.output, "w") + output.write(buf.getvalue()) + output.close() + +if __name__ == "__main__": + try: + main() + except DBusXmlException, ex: + print >> sys.stderr, str(ex) + sys.exit(1) diff --git a/src/sbus/sssd_dbus_meta.c b/src/sbus/sssd_dbus_meta.c new file mode 100644 index 000000000..57123b743 --- /dev/null +++ b/src/sbus/sssd_dbus_meta.c @@ -0,0 +1,64 @@ +/* + Authors: + Stef Walter <stefw@redhat.com> + + Copyright (C) 2014 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "util/util.h" +#include "sbus/sssd_dbus_meta.h" + +const struct sbus_method_meta * +sbus_meta_find_method(const struct sbus_interface_meta *interface, + const char *method_name) +{ + const struct sbus_method_meta *method; + + for (method = interface->methods; method && method->name; method++) { + if (strcmp(method_name, method->name) == 0) + return method; + } + + return NULL; +} + +const struct sbus_signal_meta * +sbus_meta_find_signal(const struct sbus_interface_meta *interface, + const char *signal_name) +{ + const struct sbus_signal_meta *signal; + + for (signal = interface->signals; signal && signal->name; signal++) { + if (strcmp(signal_name, signal->name) == 0) + return signal; + } + + return NULL; +} + +const struct sbus_property_meta * +sbus_meta_find_property(const struct sbus_interface_meta *interface, + const char *property_name) +{ + const struct sbus_property_meta *property; + + for (property = interface->properties; property && property->name; property++) { + if (strcmp(property_name, property->name) == 0) + return property; + } + + return NULL; +} diff --git a/src/sbus/sssd_dbus_meta.h b/src/sbus/sssd_dbus_meta.h new file mode 100644 index 000000000..17ea7a507 --- /dev/null +++ b/src/sbus/sssd_dbus_meta.h @@ -0,0 +1,82 @@ +/* + Authors: + Stef Walter <stefw@redhat.com> + + Copyright (C) 2014 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SSSD_DBUS_META_H_ +#define _SSSD_DBUS_META_H_ + +/* + * Interface metadata + * + * For arrays, the last item in each array will have a + * NULL .name field + * + * Typically these structs will be generated by sbus_codegen + * from canonical XML interface data: + * + * http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format + */ + +struct sbus_arg_meta { + const char *name; + const char *type; +}; + +struct sbus_method_meta { + const char *name; + const struct sbus_arg_meta *in_args; + const struct sbus_arg_meta *out_args; +}; + +enum { + SBUS_PROPERTY_READABLE = 1 << 0, + SBUS_PROPERTY_WRITABLE = 1 << 1 +}; + +struct sbus_property_meta { + const char *name; + const char *type; + int flags; +}; + +struct sbus_signal_meta { + const char *name; + const struct sbus_arg_meta *args; +}; + +struct sbus_interface_meta { + const char *name; + const struct sbus_method_meta *methods; + const struct sbus_signal_meta *signals; + const struct sbus_property_meta *properties; +}; + +const struct sbus_method_meta * +sbus_meta_find_method (const struct sbus_interface_meta *interface, + const char *method_name); + +const struct sbus_signal_meta * +sbus_meta_find_signal (const struct sbus_interface_meta *interface, + const char *signal_name); + +const struct sbus_property_meta * +sbus_meta_find_property (const struct sbus_interface_meta *interface, + const char *property_name); + +#endif /* _SSSD_DBUS_META_H_ */ diff --git a/src/tests/sbus_codegen_tests.c b/src/tests/sbus_codegen_tests.c new file mode 100644 index 000000000..6b2abf8bc --- /dev/null +++ b/src/tests/sbus_codegen_tests.c @@ -0,0 +1,166 @@ +/* + SSSD + + sbus_codegen tests. + + Authors: + Stef Walter <stefw@redhat.com> + + Copyright (C) Red Hat, Inc 2014 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <check.h> +#include <talloc.h> +#include <tevent.h> +#include <popt.h> + +#include "sbus/sssd_dbus_meta.h" +#include "tests/sbus_codegen_tests_generated.h" + +static const struct sbus_arg_meta * +find_arg(const struct sbus_arg_meta *args, + const char *name) +{ + const struct sbus_arg_meta *arg; + for (arg = args; arg->name != NULL; arg++) { + if (strcmp (arg->name, name) == 0) + return arg; + } + + return NULL; +} + +START_TEST(test_interfaces) +{ + ck_assert_str_eq(com_planetexpress_Ship_meta.name, "com.planetexpress.Ship"); + ck_assert(com_planetexpress_Ship_meta.methods != NULL); + ck_assert(com_planetexpress_Ship_meta.signals != NULL); + ck_assert(com_planetexpress_Ship_meta.properties != NULL); + + /* Explicit C Symbol */ + ck_assert_str_eq(test_pilot_meta.name, "com.planetexpress.Pilot"); + ck_assert(test_pilot_meta.methods == NULL); /* no methods */ + ck_assert(test_pilot_meta.signals == NULL); /* no signals */ + ck_assert(test_pilot_meta.properties != NULL); + +} +END_TEST + +START_TEST(test_methods) +{ + const struct sbus_method_meta *method; + const struct sbus_arg_meta *arg; + + method = sbus_meta_find_method(&com_planetexpress_Ship_meta, "MoveUniverse"); + ck_assert(method != NULL); + ck_assert_str_eq(method->name, "MoveUniverse"); + ck_assert(method->in_args != NULL); + ck_assert(method->out_args != NULL); + + arg = find_arg(method->in_args, "smoothly"); + ck_assert(arg != NULL); + ck_assert_str_eq(arg->name, "smoothly"); + ck_assert_str_eq(arg->type, "b"); + + arg = find_arg(method->out_args, "where_we_crashed"); + ck_assert(arg != NULL); + ck_assert_str_eq(arg->name, "where_we_crashed"); + ck_assert_str_eq(arg->type, "s"); +} +END_TEST + +START_TEST(test_properties) +{ + const struct sbus_property_meta *prop; + + prop = sbus_meta_find_property(&com_planetexpress_Ship_meta, "Color"); + ck_assert(prop != NULL); + ck_assert_str_eq(prop->name, "Color"); + ck_assert_str_eq(prop->type, "s"); + ck_assert_int_eq(prop->flags, SBUS_PROPERTY_READABLE); +} +END_TEST + +START_TEST(test_signals) +{ + const struct sbus_signal_meta *signal; + const struct sbus_arg_meta *arg; + + signal = sbus_meta_find_signal(&com_planetexpress_Ship_meta, "BecameSentient"); + ck_assert(signal != NULL); + ck_assert_str_eq(signal->name, "BecameSentient"); + ck_assert(signal->args != NULL); + + arg = find_arg(signal->args, "gender"); + ck_assert(arg != NULL); + ck_assert_str_eq(arg->name, "gender"); + ck_assert_str_eq(arg->type, "s"); +} +END_TEST + +Suite *create_suite(void) +{ + Suite *s = suite_create("sbus_codegen"); + + TCase *tc = tcase_create("defs"); + + /* Do some testing */ + tcase_add_test(tc, test_interfaces); + tcase_add_test(tc, test_methods); + tcase_add_test(tc, test_properties); + tcase_add_test(tc, test_signals); + + /* Add all test cases to the test suite */ + suite_add_tcase(s, tc); + + return s; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + int failure_count; + Suite *suite; + SRunner *sr; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_TABLEEND + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + suite = create_suite(); + sr = srunner_create(suite); + srunner_set_fork_status(sr, CK_FORK); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + failure_count = srunner_ntests_failed(sr); + srunner_free(sr); + return (failure_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/src/tests/sbus_codegen_tests.xml b/src/tests/sbus_codegen_tests.xml new file mode 100755 index 000000000..0def3585a --- /dev/null +++ b/src/tests/sbus_codegen_tests.xml @@ -0,0 +1,45 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <!-- + This file should exercise as many aspects of the sbus_codegen as + possible. See sbus_codegen_test.c for verification. + --> + + <!-- + This is an interface, it will get a sbus_interface_meta struct. + Its name will be com_planetexpress_Ship__meta, since no c symbol + is specified. + --> + <interface name="com.planetexpress.Ship"> + <!-- A property --> + <property name="Color" type="s" access="read"/> + + <!-- A method with two in and one out argument --> + <method name="MoveUniverse"> + <!-- This is a boolean arg --> + <arg name="smoothly" type="b" direction="in"/> + <!-- This is an uint32 arg --> + <arg name="speed_factor" type="u" direction="in"/> + <!-- This is a string arg --> + <arg name="where_we_crashed" type="s" direction="out"/> + </method> + + <!-- A signal with one argument --> + <signal name="BecameSentient"> + <arg name="gender" type="s"/> + </signal> + </interface> + + <!-- + Another interface. It's C name will be test_pilot, since we've overridden + the c symbol name. + --> + <interface name="com.planetexpress.Pilot"> + <annotation value="test_pilot" name="org.freedesktop.DBus.GLib.CSymbol"/> + + <!-- A property --> + <property name="FullName" type="s" access="readwrite"/> + </interface> + +</node> diff --git a/src/tests/sbus_codegen_tests_generated.c b/src/tests/sbus_codegen_tests_generated.c new file mode 100644 index 000000000..556eea12c --- /dev/null +++ b/src/tests/sbus_codegen_tests_generated.c @@ -0,0 +1,79 @@ +/* The following definitions are auto-generated from sbus_codegen_tests.xml */ + +#include "util/util.h" +#include "sbus/sssd_dbus.h" +#include "sbus/sssd_dbus_meta.h" + +/* arguments for com.planetexpress.Ship.MoveUniverse */ +const struct sbus_arg_meta com_planetexpress_Ship_MoveUniverse__in[] = { + { "smoothly", "b" }, + { "speed_factor", "u" }, + { NULL, } +}; + +/* arguments for com.planetexpress.Ship.MoveUniverse */ +const struct sbus_arg_meta com_planetexpress_Ship_MoveUniverse__out[] = { + { "where_we_crashed", "s" }, + { NULL, } +}; + +/* methods for com.planetexpress.Ship */ +const struct sbus_method_meta com_planetexpress_Ship__methods[] = { + { + "MoveUniverse", /* name */ + com_planetexpress_Ship_MoveUniverse__in, + com_planetexpress_Ship_MoveUniverse__out, + }, + { NULL, } +}; + +/* arguments for com.planetexpress.Ship.BecameSentient */ +const struct sbus_arg_meta com_planetexpress_Ship_BecameSentient__args[] = { + { "gender", "s" }, + { NULL, } +}; + +/* signals for com.planetexpress.Ship */ +const struct sbus_signal_meta com_planetexpress_Ship__signals[] = { + { + "BecameSentient", /* name */ + com_planetexpress_Ship_BecameSentient__args + }, + { NULL, } +}; + +/* property info for com.planetexpress.Ship */ +const struct sbus_property_meta com_planetexpress_Ship__properties[] = { + { + "Color", /* name */ + "s", /* signature */ + SBUS_PROPERTY_READABLE, + }, + { NULL, } +}; + +/* interface info for com.planetexpress.Ship */ +const struct sbus_interface_meta com_planetexpress_Ship_meta = { + "com.planetexpress.Ship", /* name */ + com_planetexpress_Ship__methods, + com_planetexpress_Ship__signals, + com_planetexpress_Ship__properties +}; + +/* property info for com.planetexpress.Pilot */ +const struct sbus_property_meta test_pilot__properties[] = { + { + "FullName", /* name */ + "s", /* signature */ + SBUS_PROPERTY_READABLE | SBUS_PROPERTY_WRITABLE, + }, + { NULL, } +}; + +/* interface info for com.planetexpress.Pilot */ +const struct sbus_interface_meta test_pilot_meta = { + "com.planetexpress.Pilot", /* name */ + NULL, /* no methods */ + NULL, /* no signals */ + test_pilot__properties +}; diff --git a/src/tests/sbus_codegen_tests_generated.h b/src/tests/sbus_codegen_tests_generated.h new file mode 100644 index 000000000..c3a23c870 --- /dev/null +++ b/src/tests/sbus_codegen_tests_generated.h @@ -0,0 +1,24 @@ +/* The following declarations are auto-generated from sbus_codegen_tests.xml */ + +#ifndef __SBUS_CODEGEN_TESTS_XML__ +#define __SBUS_CODEGEN_TESTS_XML__ + +#include "sbus/sssd_dbus.h" + +/* ------------------------------------------------------------------------ + * DBus Interface Metadata + * + * These structure definitions are filled in with the information about + * the interfaces, methods, properties and so on. + * + * The actual definitions are found in the accompanying C file next + * to this header. + */ + +/* interface info for com.planetexpress.Ship */ +extern const struct sbus_interface_meta com_planetexpress_Ship_meta; + +/* interface info for com.planetexpress.Pilot */ +extern const struct sbus_interface_meta test_pilot_meta; + +#endif /* __SBUS_CODEGEN_TESTS_XML__ */ |