diff --git a/.gitignore b/.gitignore index 033eaada1e..9807641988 100644 --- a/.gitignore +++ b/.gitignore @@ -91,6 +91,7 @@ libnm-util/tests/test-settings-defaults libnm-util/tests/test-general libnm-util/tests/test-need-secrets libnm-util/tests/test-setting-8021x +libnm-glib/tests/test-remote-settings-client src/tests/test-dhcp-options src/tests/test-policy-hosts diff --git a/configure.ac b/configure.ac index 3306c6da2b..bc9733036e 100644 --- a/configure.ac +++ b/configure.ac @@ -547,6 +547,7 @@ libnm-util/tests/certs/Makefile libnm-glib/libnm-glib.pc libnm-glib/libnm-glib-vpn.pc libnm-glib/Makefile +libnm-glib/tests/Makefile callouts/Makefile tools/Makefile system-settings/Makefile diff --git a/libnm-glib/Makefile.am b/libnm-glib/Makefile.am index 5a6045ebcf..571b6bf96c 100644 --- a/libnm-glib/Makefile.am +++ b/libnm-glib/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS=. tests + INCLUDES = \ -I$(top_srcdir)/include \ -I$(top_srcdir)/libnm-util \ @@ -135,6 +137,30 @@ libnm_glib_vpn_la_LIBADD = $(top_builddir)/libnm-util/libnm-util.la $(GLIB_LIBS) libnm_glib_vpn_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnm-glib-vpn.ver \ -version-info "1:0:0" +##################################################### +# Test libnm-glib stuff +##################################################### + +noinst_LTLIBRARIES = libnm-glib-test.la + +libnm_glib_test_la_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -DLIBNM_GLIB_TEST + +libnm_glib_test_la_SOURCES = \ + $(libnminclude_HEADERS) \ + $(libnm_glib_la_SOURCES) + +libnm_glib_test_la_LIBADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) \ + $(GUDEV_LIBS) + +##################################################### nm-client-bindings.h: $(top_srcdir)/introspection/nm-manager-client.xml $(AM_V_GEN) dbus-binding-tool --prefix=nm_client --mode=glib-client --output=$@ $< diff --git a/libnm-glib/nm-client.c b/libnm-glib/nm-client.c index e7770df64e..a2be1f795c 100644 --- a/libnm-glib/nm-client.c +++ b/libnm-glib/nm-client.c @@ -817,7 +817,11 @@ nm_client_new (void) DBusGConnection *connection; GError *err = NULL; +#ifdef LIBNM_GLIB_TEST + connection = dbus_g_bus_get (DBUS_BUS_SESSION, &err); +#else connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); +#endif if (!connection) { g_warning ("Couldn't connect to system bus: %s", err->message); g_error_free (err); diff --git a/libnm-glib/tests/Makefile.am b/libnm-glib/tests/Makefile.am new file mode 100644 index 0000000000..a22a2d35dc --- /dev/null +++ b/libnm-glib/tests/Makefile.am @@ -0,0 +1,34 @@ +INCLUDES = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/libnm-util \ + -I$(top_srcdir)/libnm-glib + +noinst_PROGRAMS = test-remote-settings-client + +####### remote settings client test ####### + +test_remote_settings_client_SOURCES = \ + test-remote-settings-client.c + +test_remote_settings_client_CPPFLAGS = \ + -DSERVICEDIR=\"$(builddir)\" \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) + +test_remote_settings_client_LDADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/libnm-glib/libnm-glib-test.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) + +########################################### + +EXTRA_DIST = test-remote-settings-service.py + +if WITH_TESTS + +check-local: test-remote-settings-client + $(abs_builddir)/test-remote-settings-client + +endif + diff --git a/libnm-glib/tests/test-remote-settings-client.c b/libnm-glib/tests/test-remote-settings-client.c new file mode 100644 index 0000000000..3285735735 --- /dev/null +++ b/libnm-glib/tests/test-remote-settings-client.c @@ -0,0 +1,258 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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 2, 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "nm-remote-settings.h" + +#define SERVICE_FILE "test-remote-settings-service.py" + +static GPid spid = 0; +static NMRemoteSettings *settings = NULL; + +/*******************************************************************/ + +static void +cleanup (void) +{ + if (settings) + g_object_unref (settings); + kill (spid, SIGTERM); +} + +#define test_assert(condition) \ +do { \ + if (!G_LIKELY (condition)) \ + cleanup (); \ + g_assert (condition); \ +} while (0) + +/*******************************************************************/ + +typedef struct { + gboolean done; + NMRemoteConnection *connection; +} AddInfo; + +static void +add_cb (NMRemoteSettings *s, + NMRemoteConnection *connection, + GError *error, + gpointer user_data) +{ + AddInfo *info = user_data; + + if (error) + g_warning ("Add error: %s", error->message); + + info->done = TRUE; + info->connection = connection; +} + +static void +test_add_connection (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + char *uuid; + gboolean success; + time_t start, now; + AddInfo info = { FALSE, NULL }; + + connection = nm_connection_new (); + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + uuid = nm_utils_uuid_generate (); + g_object_set (G_OBJECT (s_con), + NM_SETTING_CONNECTION_ID, "blahblahblah", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free (uuid); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + success = nm_remote_settings_add_connection (settings, + connection, + add_cb, + &info); + test_assert (success == TRUE); + + start = time (NULL); + do { + now = time (NULL); + g_main_context_iteration (NULL, FALSE); + } while ((info.done == FALSE) && (now - start < 5)); + test_assert (info.done == TRUE); + test_assert (info.connection != NULL); + + /* Make sure the connection is the same as what we added */ + test_assert (nm_connection_compare (connection, + NM_CONNECTION (info.connection), + NM_SETTING_COMPARE_FLAG_EXACT) == TRUE); +} + +/*******************************************************************/ + +static void +deleted_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GError *error = NULL; + gboolean success; + + success = dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + if (!success) + g_warning ("Failed to delete connection: %s", error->message); + test_assert (success == TRUE); + test_assert (error == NULL); +} + +static void +removed_cb (NMRemoteConnection *connection, gpointer user_data) +{ + gboolean *done = user_data; + + *done = TRUE; +} + +static void +test_remove_connection (DBusGConnection *bus) +{ + NMRemoteConnection *connection; + time_t start, now; + GSList *list, *iter; + DBusGProxy *proxy; + gboolean done = FALSE; + char *path; + + /* Find a connection to delete */ + list = nm_remote_settings_list_connections (settings); + test_assert (g_slist_length (list) > 0); + + connection = NM_REMOTE_CONNECTION (list->data); + path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection))); + g_signal_connect (connection, "removed", G_CALLBACK (removed_cb), &done); + + proxy = dbus_g_proxy_new_for_name (bus, + NM_DBUS_SERVICE, + path, + NM_DBUS_IFACE_SETTINGS_CONNECTION); + test_assert (proxy != NULL); + + /* Bypass the NMRemoteSettings object so we can test it independently */ + dbus_g_proxy_begin_call (proxy, "Delete", deleted_cb, NULL, NULL, G_TYPE_INVALID); + + start = time (NULL); + do { + now = time (NULL); + g_main_context_iteration (NULL, FALSE); + } while ((done == FALSE) && (now - start < 5)); + test_assert (done == TRUE); + + /* Ensure NMRemoteSettings no longer has the connection */ + list = nm_remote_settings_list_connections (settings); + for (iter = list; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + test_assert ((gpointer) connection != (gpointer) candidate); + test_assert (strcmp (path, nm_connection_get_path (candidate)) != 0); + } + + g_free (path); + g_object_unref (proxy); +} + +/*******************************************************************/ + +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFixtureFunc TCFunc; +#else +typedef void (*TCFunc)(void); +#endif + +#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) + +int main (int argc, char **argv) +{ + GTestSuite *suite; + char *service_argv[3] = { SERVICEDIR "/" SERVICE_FILE, SERVICE_FILE, NULL }; + int ret; + GError *error = NULL; + DBusGConnection *bus; + int i = 100; + + g_type_init (); + + g_test_init (&argc, &argv, NULL); + + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (!bus) { + g_warning ("Error connecting to D-Bus: %s", error->message); + g_assert (error == NULL); + } + + if (!g_spawn_async (SERVICEDIR, service_argv, NULL, 0, NULL, NULL, &spid, &error)) { + g_warning ("Error spawning " SERVICE_FILE ": %s", error->message); + g_assert (error == NULL); + } + + /* Wait until the service is registered on the bus */ + while (i > 0) { + g_usleep (G_USEC_PER_SEC / 50); + if (dbus_bus_name_has_owner (dbus_g_connection_get_connection (bus), + "org.freedesktop.NetworkManager", + NULL)) + break; + i--; + } + test_assert (i > 0); + + settings = nm_remote_settings_new (bus); + test_assert (settings != NULL); + + suite = g_test_get_root (); + + g_test_suite_add (suite, TESTCASE (test_add_connection, NULL)); + g_test_suite_add (suite, TESTCASE (test_remove_connection, bus)); + + ret = g_test_run (); + + cleanup (); + + return ret; +} + diff --git a/libnm-glib/tests/test-remote-settings-service.py b/libnm-glib/tests/test-remote-settings-service.py new file mode 100755 index 0000000000..1f4f9af072 --- /dev/null +++ b/libnm-glib/tests/test-remote-settings-service.py @@ -0,0 +1,116 @@ +#!/bin/env python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + +import glib +import gobject +import sys +import dbus +import dbus.service +import dbus.mainloop.glib + +IFACE_SETTINGS = 'org.freedesktop.NetworkManager.Settings' +IFACE_CONNECTION = 'org.freedesktop.NetworkManager.Settings.Connection' +IFACE_DBUS = 'org.freedesktop.DBus' + +class UnknownInterfaceException(dbus.DBusException): + _dbus_error_name = IFACE_DBUS + '.UnknownInterface' + +class UnknownPropertyException(dbus.DBusException): + _dbus_error_name = IFACE_DBUS + '.UnknownProperty' + +mainloop = gobject.MainLoop() + +class Connection(dbus.service.Object): + def __init__(self, bus, object_path, settings, remove_func): + dbus.service.Object.__init__(self, bus, object_path) + self.path = object_path + self.settings = settings + self.remove_func = remove_func + + @dbus.service.method(dbus_interface=IFACE_CONNECTION, in_signature='', out_signature='a{sa{sv}}') + def GetSettings(self): + return self.settings + + @dbus.service.method(dbus_interface=IFACE_CONNECTION, in_signature='', out_signature='') + def Delete(self): + self.remove_func(self) + self.Removed() + + @dbus.service.signal(IFACE_CONNECTION, signature='') + def Removed(self): + pass + +class Settings(dbus.service.Object): + def __init__(self, bus, object_path): + dbus.service.Object.__init__(self, bus, object_path) + self.connections = {} + self.bus = bus + self.counter = 1 + self.props = {} + self.props['Hostname'] = "foobar.baz" + self.props['CanModify'] = True + + @dbus.service.method(dbus_interface=IFACE_SETTINGS, in_signature='', out_signature='ao') + def ListConnections(self): + connections = [] + return self.connections.keys() + + @dbus.service.method(dbus_interface=IFACE_SETTINGS, in_signature='a{sa{sv}}', out_signature='o') + def AddConnection(self, settings): + path = "/org/freedesktop/NetworkManager/Settings/Connection/%d" % self.counter + self.counter = self.counter + 1 + self.connections[path] = Connection(self.bus, path, settings, self.delete_connection) + print "Added connection %s" % path + return path + + def delete_connection(self, connection): + del self.connections[connection.path] + + @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') + def GetAll(self, iface): + if iface != IFACE_SETTINGS: + raise UnknownInterfaceException() + return self.props + + @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') + def Get(self, iface, name): + if iface != IFACE_SETTINGS: + raise UnknownInterfaceException() + if not name in self.props.keys(): + raise UnknownPropertyException() + return self.props[name] + + @dbus.service.signal(IFACE_SETTINGS, signature='o') + def NewConnection(self, path): + pass + + @dbus.service.method(IFACE_SETTINGS, in_signature='', out_signature='') + def Quit(self): + mainloop.quit() + +def quit_cb(user_data): + mainloop.quit() + +def main(): + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SessionBus() + obj = Settings(bus, "/org/freedesktop/NetworkManager/Settings") + if not bus.request_name("org.freedesktop.NetworkManager"): + sys.exit(1) + + print "Service started" + + gobject.timeout_add_seconds(20, quit_cb, None) + + try: + mainloop.run() + except Exception, e: + pass + + print "Service stopped" + sys.exit(0) + +if __name__ == '__main__': + main() +