blob: 85d7abbb3aa34ee736c36d9b025f221d3c9c0d78 [file] [log] [blame]
/*
*
* Embedded Linux library
*
* Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <assert.h>
#include <stdbool.h>
#include <ell/ell.h>
#include "ell/dbus-private.h"
struct l_dbus_interface *interface;
static bool callback_called = false;
static char *dummy_data = "Foobar";
struct introspect_test {
const char *name;
const char *expected_xml;
};
static const struct introspect_test frobate_test = {
.name = "Frobate",
.expected_xml = "\t\t<method name=\"Frobate\">\n"
"\t\t\t<arg name=\"foo\" type=\"i\" direction=\"in\"/>\n"
"\t\t\t<arg name=\"bar\" type=\"s\" direction=\"out\"/>\n"
"\t\t\t<arg name=\"baz\" type=\"a{us}\" direction=\"out\"/>\n"
"\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" "
"value=\"true\"/>\n"
"\t\t</method>\n",
};
static const struct introspect_test bazify_test = {
.name = "Bazify",
.expected_xml = "\t\t<method name=\"Bazify\">\n"
"\t\t\t<arg name=\"bar\" type=\"(iiu)\" direction=\"in\"/>\n"
"\t\t\t<arg name=\"bar\" type=\"v\" direction=\"out\"/>\n"
"\t\t</method>\n",
};
static const struct introspect_test mogrify_test = {
.name = "Mogrify",
.expected_xml = "\t\t<method name=\"Mogrify\">\n"
"\t\t\t<arg name=\"bar\" type=\"(iiav)\" direction=\"in\"/>\n"
"\t\t</method>\n",
};
static const struct introspect_test changed_test = {
.name = "Changed",
.expected_xml = "\t\t<signal name=\"Changed\">\n"
"\t\t\t<arg name=\"new_value\" type=\"b\"/>\n"
"\t\t</signal>\n",
};
static const struct introspect_test bar_test = {
.name = "Bar",
.expected_xml = "\t\t<property name=\"Bar\" type=\"y\" "
"access=\"readwrite\"/>\n",
};
static const struct introspect_test interface_test = {
.name = "",
.expected_xml =
"\t<interface name=\"org.freedesktop.SampleInterface\">\n"
"\t\t<method name=\"Frobate\">\n"
"\t\t\t<arg name=\"foo\" type=\"i\" direction=\"in\"/>\n"
"\t\t\t<arg name=\"bar\" type=\"s\" direction=\"out\"/>\n"
"\t\t\t<arg name=\"baz\" type=\"a{us}\" direction=\"out\"/>\n"
"\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" "
"value=\"true\"/>\n"
"\t\t</method>\n"
"\t\t<method name=\"Bazify\">\n"
"\t\t\t<arg name=\"bar\" type=\"(iiu)\" direction=\"in\"/>\n"
"\t\t\t<arg name=\"bar\" type=\"v\" direction=\"out\"/>\n"
"\t\t</method>\n"
"\t\t<method name=\"Mogrify\">\n"
"\t\t\t<arg name=\"bar\" type=\"(iiav)\" direction=\"in\"/>\n"
"\t\t</method>\n"
"\t\t<signal name=\"Changed\">\n"
"\t\t\t<arg name=\"new_value\" type=\"b\"/>\n"
"\t\t</signal>\n"
"\t\t<property name=\"Bar\" type=\"y\" "
"access=\"readwrite\"/>\n"
"\t</interface>\n",
};
static void test_introspect_method(const void *test_data)
{
const struct introspect_test *test = test_data;
struct _dbus_method *method;
struct l_string *buf;
char *xml;
method = _dbus_interface_find_method(interface, test->name);
assert(method);
buf = l_string_new(0);
_dbus_method_introspection(method, buf);
xml = l_string_unwrap(buf);
assert(!strcmp(test->expected_xml, xml));
l_free(xml);
}
static void test_introspect_signal(const void *test_data)
{
const struct introspect_test *test = test_data;
struct _dbus_signal *signal;
struct l_string *buf;
char *xml;
signal = _dbus_interface_find_signal(interface, test->name);
assert(signal);
buf = l_string_new(0);
_dbus_signal_introspection(signal, buf);
xml = l_string_unwrap(buf);
assert(!strcmp(test->expected_xml, xml));
l_free(xml);
}
static void test_introspect_property(const void *test_data)
{
const struct introspect_test *test = test_data;
struct _dbus_property *property;
struct l_string *buf;
char *xml;
property = _dbus_interface_find_property(interface, test->name);
assert(property);
buf = l_string_new(0);
_dbus_property_introspection(property, buf);
xml = l_string_unwrap(buf);
assert(!strcmp(test->expected_xml, xml));
l_free(xml);
}
static void test_introspect_interface(const void *test_data)
{
const struct introspect_test *test = test_data;
struct l_string *buf;
char *xml;
buf = l_string_new(0);
_dbus_interface_introspection(interface, buf);
xml = l_string_unwrap(buf);
assert(!strcmp(test->expected_xml, xml));
l_free(xml);
}
static void test_dbus_object_tree_1(const void *test_data)
{
struct _dbus_object_tree *tree;
struct object_node *leaf1, *leaf2, *leaf3;
struct object_node *tmp;
tree = _dbus_object_tree_new();
assert(tree);
leaf1 = _dbus_object_tree_makepath(tree, "/foo/bar/baz");
leaf2 = _dbus_object_tree_makepath(tree, "/foo/bar/ble");
leaf3 = _dbus_object_tree_makepath(tree, "/foo/bee/boo");
tmp = _dbus_object_tree_lookup(tree, "/foo");
assert(tmp);
tmp = _dbus_object_tree_lookup(tree, "/foo/bar/baz");
assert(tmp);
assert(tmp == leaf1);
tmp = _dbus_object_tree_lookup(tree, "/foo/bar/ble");
assert(tmp);
assert(tmp == leaf2);
tmp = _dbus_object_tree_lookup(tree, "/foo/bee/boo");
assert(tmp);
assert(tmp == leaf3);
tmp = _dbus_object_tree_lookup(tree, "/foobar");
assert(!tmp);
tmp = _dbus_object_tree_lookup(tree, "/foo/bee");
assert(tmp);
_dbus_object_tree_prune_node(leaf3);
tmp = _dbus_object_tree_lookup(tree, "/foo/bee");
assert(!tmp);
tmp = _dbus_object_tree_lookup(tree, "/foo/bar");
assert(tmp);
_dbus_object_tree_prune_node(leaf2);
tmp = _dbus_object_tree_lookup(tree, "/foo/bar");
assert(tmp);
_dbus_object_tree_prune_node(leaf1);
tmp = _dbus_object_tree_lookup(tree, "/foo/bar");
assert(!tmp);
tmp = _dbus_object_tree_lookup(tree, "/foo");
assert(!tmp);
tmp = _dbus_object_tree_lookup(tree, "/");
assert(tmp);
_dbus_object_tree_free(tree);
}
static void setup_dummy_interface(struct l_dbus_interface *iface)
{
}
static void interface_destroy(void *data)
{
bool *destroyed = data;
*destroyed = true;
}
static void test_dbus_object_tree_2(const void *test_data)
{
struct _dbus_object_tree *tree;
bool destroyed[2] = { false, false };
tree = _dbus_object_tree_new();
assert(tree);
assert(_dbus_object_tree_new_object(tree, "/foo", NULL, NULL));
assert(_dbus_object_tree_new_object(tree, "/foo/bar/baz", NULL, NULL));
assert(_dbus_object_tree_lookup(tree, "/foo"));
assert(_dbus_object_tree_lookup(tree, "/foo/bar"));
assert(_dbus_object_tree_lookup(tree, "/foo/bar/baz"));
assert(_dbus_object_tree_register_interface(tree, "org.example",
setup_dummy_interface,
interface_destroy, false));
assert(_dbus_object_tree_add_interface(tree, "/foo",
"org.example", &destroyed[0]));
assert(!_dbus_object_tree_add_interface(tree, "/foo",
"org.other", NULL));
assert(_dbus_object_tree_add_interface(tree, "/foo/bar/baz",
"org.example", &destroyed[1]));
assert(!_dbus_object_tree_remove_interface(tree, "/foo", "org.other"));
assert(_dbus_object_tree_remove_interface(tree, "/foo", "org.example"));
assert(destroyed[0]);
assert(_dbus_object_tree_object_destroy(tree, "/foo"));
assert(!_dbus_object_tree_object_destroy(tree, "/foo/bar"));
assert(_dbus_object_tree_lookup(tree, "/foo"));
assert(_dbus_object_tree_lookup(tree, "/foo/bar"));
assert(_dbus_object_tree_lookup(tree, "/foo/bar/baz"));
destroyed[0] = false;
assert(_dbus_object_tree_add_interface(tree, "/foo",
"org.example", &destroyed[0]));
assert(_dbus_object_tree_object_destroy(tree, "/foo/bar/baz"));
assert(_dbus_object_tree_lookup(tree, "/foo"));
assert(!_dbus_object_tree_lookup(tree, "/foo/bar"));
assert(!_dbus_object_tree_lookup(tree, "/foo/bar/baz"));
assert(_dbus_object_tree_object_destroy(tree, "/foo"));
assert(destroyed[0] && destroyed[1]);
assert(!_dbus_object_tree_lookup(tree, "/foo"));
destroyed[0] = false;
assert(_dbus_object_tree_add_interface(tree, "/foo",
"org.example", &destroyed[0]));
assert(!_dbus_object_tree_unregister_interface(tree, "org.other"));
assert(_dbus_object_tree_unregister_interface(tree, "org.example"));
assert(destroyed[0]);
_dbus_object_tree_free(tree);
}
#define TEST_3_OBJ_COUNT 20000
static void test_dbus_object_tree_3(const void *test_data)
{
struct _dbus_object_tree *tree;
char path[50];
struct l_hashmap *paths;
unsigned int count = 0;
struct l_dbus_message *message, *reply;
struct l_dbus_message_iter objects, interfaces;
const char *obj_path;
tree = _dbus_object_tree_new();
assert(tree);
assert(_dbus_object_tree_register_interface(tree, "org.example",
setup_dummy_interface,
NULL, false));
srand48(50);
paths = l_hashmap_string_new();
while (count < TEST_3_OBJ_COUNT) {
unsigned int len = 0;
unsigned int subpath_len = 0;
bool repeat;
/* Generate a random legal path composed of a, b and / chars */
path[len++] = '/';
while (len < sizeof(path) - 1) {
const char *allowed;
char ch;
if (subpath_len)
allowed = "aabb/E";
else /* At least one letter required after a / */
allowed = "ab";
ch = allowed[lrand48() % strlen(allowed)];
if (ch == 'E')
break;
else if (ch == '/')
subpath_len = 0;
else
subpath_len++;
path[len++] = ch;
}
/* Last character can't be a / */
if (path[len - 1] == '/')
len--;
path[len] = '\0';
repeat = l_hashmap_lookup(paths, path) != NULL;
assert(_dbus_object_tree_add_interface(tree, path,
"org.example", NULL) == !repeat);
if (!repeat) {
l_hashmap_insert(paths, path, path);
count++;
}
}
assert(_dbus_object_tree_add_interface(tree, "/",
L_DBUS_INTERFACE_OBJECT_MANAGER, NULL));
l_hashmap_insert(paths, "/", path);
message = _dbus_message_new_method_call(1, "org.example", "/",
L_DBUS_INTERFACE_OBJECT_MANAGER,
"GetManagedObjects");
l_dbus_message_set_arguments(message, "");
_dbus_message_set_serial(message, 0);
reply = _dbus_object_tree_get_objects(tree, NULL, "/", message);
assert(reply);
assert(!l_dbus_message_get_error(reply, NULL, NULL));
assert(l_dbus_message_get_arguments(reply, "a{oa{sa{sv}}}", &objects));
while (l_dbus_message_iter_next_entry(&objects, &obj_path, &interfaces))
assert(l_hashmap_remove(paths, obj_path));
l_dbus_message_unref(message);
l_dbus_message_unref(reply);
assert(l_hashmap_isempty(paths));
l_hashmap_destroy(paths, NULL);
assert(_dbus_object_tree_remove_interface(tree, "/",
L_DBUS_INTERFACE_OBJECT_MANAGER));
assert(_dbus_object_tree_unregister_interface(tree, "org.example"));
_dbus_object_tree_free(tree);
}
static struct l_dbus_message *get_modems_callback(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
assert(user_data == dummy_data);
callback_called = true;
return NULL;
}
static void build_manager_interface(struct l_dbus_interface *iface)
{
l_dbus_interface_method(iface, "GetModems", 0, get_modems_callback,
"a(oa{sv})", "", "modems");
l_dbus_interface_signal(iface, "ModemAdded", 0,
"oa{sv}", "path", "properties");
l_dbus_interface_signal(iface, "ModemRemoved", 0,
"o", "path");
}
static const char *ofono_manager_introspection =
"<!DOCTYPE node PUBLIC \""
"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
"<node>\n"
"\t<interface name=\"org.freedesktop.DBus.Introspectable\">\n"
"\t\t<method name=\"Introspect\">\n"
"\t\t\t<arg name=\"xml\" type=\"s\" direction=\"out\"/>\n"
"\t\t</method>\n\t</interface>\n"
"\t<interface name=\"org.ofono.Manager\">\n"
"\t\t<method name=\"GetModems\">\n"
"\t\t\t<arg name=\"modems\" type=\"a(oa{sv})\" direction=\"out\"/>\n"
"\t\t</method>\n"
"\t\t<signal name=\"ModemAdded\">\n"
"\t\t\t<arg name=\"path\" type=\"o\"/>\n"
"\t\t\t<arg name=\"properties\" type=\"a{sv}\"/>\n"
"\t\t</signal>\n"
"\t\t<signal name=\"ModemRemoved\">\n" "\t\t\t<arg name=\"path\" type=\"o\"/>\n"
"\t\t</signal>\n\t</interface>\n"
"\t<node name=\"phonesim\"/>\n"
"</node>\n";
static void test_dbus_object_tree_introspection(const void *test_data)
{
struct _dbus_object_tree *tree;
struct l_string *buf;
char *xml;
tree = _dbus_object_tree_new();
_dbus_object_tree_register_interface(tree, "org.ofono.Manager",
build_manager_interface,
NULL, false);
_dbus_object_tree_add_interface(tree, "/", "org.ofono.Manager", NULL);
_dbus_object_tree_makepath(tree, "/phonesim");
buf = l_string_new(1024);
_dbus_object_tree_introspect(tree, "/", buf);
xml = l_string_unwrap(buf);
assert(!strcmp(ofono_manager_introspection, xml));
l_free(xml);
_dbus_object_tree_free(tree);
}
static void test_dbus_object_tree_dispatch(const void *test_data)
{
struct _dbus_object_tree *tree;
struct l_dbus_message *message;
tree = _dbus_object_tree_new();
_dbus_object_tree_register_interface(tree, "org.ofono.Manager",
build_manager_interface,
NULL, false);
_dbus_object_tree_add_interface(tree, "/", "org.ofono.Manager",
dummy_data);
message = _dbus_message_new_method_call(1, "org.ofono", "/",
"org.ofono.Manager",
"GetModems");
l_dbus_message_set_arguments(message, "");
_dbus_object_tree_dispatch(tree, NULL, message);
assert(callback_called);
l_dbus_message_unref(message);
_dbus_object_tree_free(tree);
}
static bool test_property_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *if_user_data)
{
return true;
}
static struct l_dbus_message *test_property_setter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_iter *new_value,
l_dbus_property_complete_cb_t complete,
void *user_data)
{
return NULL;
}
int main(int argc, char *argv[])
{
int ret;
interface = _dbus_interface_new("org.freedesktop.SampleInterface");
l_test_init(&argc, &argv);
l_dbus_interface_method(interface, "Frobate",
L_DBUS_METHOD_FLAG_DEPRECATED,
NULL, "sa{us}", "i", "bar", "baz", "foo");
l_dbus_interface_method(interface, "Bazify", 0, NULL, "v", "(iiu)",
"bar", "bar");
l_dbus_interface_method(interface, "Mogrify", 0, NULL, "",
"(iiav)", "bar");
l_dbus_interface_signal(interface, "Changed", 0, "b", "new_value");
l_dbus_interface_property(interface, "Bar", 0, "y",
test_property_getter,
test_property_setter);
l_test_add("Test Frobate Introspection", test_introspect_method,
&frobate_test);
l_test_add("Test Bazify Introspection", test_introspect_method,
&bazify_test);
l_test_add("Test Mogrify Introspection", test_introspect_method,
&mogrify_test);
l_test_add("Test Changed Introspection", test_introspect_signal,
&changed_test);
l_test_add("Test Bar Property Introspection", test_introspect_property,
&bar_test);
l_test_add("Test Interface Introspection", test_introspect_interface,
&interface_test);
l_test_add("_dbus_object_tree Sanity Tests 1",
test_dbus_object_tree_1, NULL);
l_test_add("_dbus_object_tree Sanity Tests 2",
test_dbus_object_tree_2, NULL);
l_test_add("_dbus_object_tree Sanity Tests 3",
test_dbus_object_tree_3, NULL);
l_test_add("_dbus_object_tree Introspection",
test_dbus_object_tree_introspection,
NULL);
l_test_add("_dbus_object_tree Dispatcher",
test_dbus_object_tree_dispatch,
NULL);
ret = l_test_run();
_dbus_interface_free(interface);
return ret;
}