tests: use libnm via pygobject in tools/test-networkmanager-service.py
tools/test-networkmanager-service.py is our NetworkManager stub server. NetworkManager uses libnm(-core) heavily, for example to decide whether a connection verifies (nm_connection_verify()) and for normalizing connections (nm_connection_normalize()). If the stub server wants to mimic NetworkManager, it also must use these function. Luckily, we already can do so, by loading libnm using python GObject introspection. We already correctly set GI_TYPELIB_PATH search path, so that the correct libnm is loaded -- provided that we build with introspection enabled. We still need to gracefully fail, if starting the stub server fails. That requries some extra effort. If the stub server notices that something is missing, it shall exit with status 77. That will cause the tests to g_test_skip().
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include "nm-default.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "NetworkManager.h"
|
||||
#include "nm-dbus-compat.h"
|
||||
@@ -72,13 +73,60 @@ _libdbus_create_proxy_test (DBusGConnection *bus)
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
GMainLoop *mainloop;
|
||||
GDBusConnection *bus;
|
||||
int exit_code;
|
||||
bool exited:1;
|
||||
bool name_found:1;
|
||||
} ServiceInitWaitData;
|
||||
|
||||
static gboolean
|
||||
_service_init_wait_probe_name (gpointer user_data)
|
||||
{
|
||||
ServiceInitWaitData *data = user_data;
|
||||
|
||||
if (!name_exists (data->bus, "org.freedesktop.NetworkManager"))
|
||||
return G_SOURCE_CONTINUE;
|
||||
|
||||
data->name_found = TRUE;
|
||||
g_main_loop_quit (data->mainloop);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
_service_init_wait_child_wait (GPid pid,
|
||||
gint status,
|
||||
gpointer user_data)
|
||||
{
|
||||
ServiceInitWaitData *data = user_data;
|
||||
|
||||
data->exited = TRUE;
|
||||
data->exit_code = status;
|
||||
g_main_loop_quit (data->mainloop);
|
||||
}
|
||||
|
||||
NMTstcServiceInfo *
|
||||
nmtstc_service_available (NMTstcServiceInfo *info)
|
||||
{
|
||||
gs_free char *m = NULL;
|
||||
|
||||
if (info)
|
||||
return info;
|
||||
|
||||
/* This happens, when test-networkmanager-service.py exits with 77 status
|
||||
* code. */
|
||||
m = g_strdup_printf ("missing dependency for running NetworkManager stub service %s", TEST_NM_SERVICE);
|
||||
g_test_skip (m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NMTstcServiceInfo *
|
||||
nmtstc_service_init (void)
|
||||
{
|
||||
NMTstcServiceInfo *info;
|
||||
const char *args[] = { TEST_NM_PYTHON, TEST_NM_SERVICE, NULL };
|
||||
GError *error = NULL;
|
||||
int i;
|
||||
|
||||
info = g_malloc0 (sizeof (*info));
|
||||
|
||||
@@ -90,18 +138,55 @@ nmtstc_service_init (void)
|
||||
* make sure the service exits if the test program crashes.
|
||||
*/
|
||||
g_spawn_async_with_pipes (NULL, (char **) args, NULL,
|
||||
G_SPAWN_SEARCH_PATH,
|
||||
G_SPAWN_SEARCH_PATH
|
||||
| G_SPAWN_DO_NOT_REAP_CHILD,
|
||||
NULL, NULL,
|
||||
&info->pid, &info->keepalive_fd, NULL, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
/* Wait until the service is registered on the bus */
|
||||
for (i = 1000; i > 0; i--) {
|
||||
if (name_exists (info->bus, "org.freedesktop.NetworkManager"))
|
||||
break;
|
||||
g_usleep (G_USEC_PER_SEC / 50);
|
||||
{
|
||||
nm_auto_unref_gsource GSource *timeout_source = NULL;
|
||||
nm_auto_unref_gsource GSource *child_source = NULL;
|
||||
GMainContext *context = g_main_context_new ();
|
||||
ServiceInitWaitData data = {
|
||||
.bus = info->bus,
|
||||
.mainloop = g_main_loop_new (context, FALSE),
|
||||
};
|
||||
gboolean had_timeout;
|
||||
|
||||
timeout_source = g_timeout_source_new (50);
|
||||
g_source_set_callback (timeout_source, _service_init_wait_probe_name, &data, NULL);
|
||||
g_source_attach (timeout_source, context);
|
||||
|
||||
child_source = g_child_watch_source_new (info->pid);
|
||||
g_source_set_callback (child_source, (GSourceFunc)(void (*) (void)) _service_init_wait_child_wait, &data, NULL);
|
||||
g_source_attach (child_source, context);
|
||||
|
||||
had_timeout = !nmtst_main_loop_run (data.mainloop, 3000);
|
||||
|
||||
g_source_destroy (timeout_source);
|
||||
g_source_destroy (child_source);
|
||||
g_main_loop_unref (data.mainloop);
|
||||
g_main_context_unref (context);
|
||||
|
||||
if (had_timeout)
|
||||
g_error ("test service %s did not start in time", TEST_NM_SERVICE);
|
||||
if (!data.name_found) {
|
||||
g_assert (data.exited);
|
||||
info->pid = NM_PID_T_INVAL;
|
||||
nmtstc_service_cleanup (info);
|
||||
|
||||
if ( WIFEXITED (data.exit_code)
|
||||
&& WEXITSTATUS (data.exit_code) == 77) {
|
||||
/* If the stub service exited with status 77 it means that it decided
|
||||
* that it cannot conduct the tests and the test should be (gracefully)
|
||||
* skip. The likely reason for that, is that libnm is not available
|
||||
* via pygobject. */
|
||||
return NULL;
|
||||
}
|
||||
g_error ("test service %s exited with error code %d", TEST_NM_SERVICE, data.exit_code);
|
||||
}
|
||||
}
|
||||
g_assert (i > 0);
|
||||
|
||||
/* Grab a proxy to our fake NM service to trigger tests */
|
||||
info->proxy = g_dbus_proxy_new_sync (info->bus,
|
||||
@@ -126,26 +211,45 @@ nmtstc_service_init (void)
|
||||
void
|
||||
nmtstc_service_cleanup (NMTstcServiceInfo *info)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
gint64 t;
|
||||
int status;
|
||||
|
||||
g_object_unref (info->proxy);
|
||||
kill (info->pid, SIGTERM);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
/* Wait until the bus notices the service is gone */
|
||||
for (i = 100; i > 0; i--) {
|
||||
if (!name_exists (info->bus, "org.freedesktop.NetworkManager"))
|
||||
break;
|
||||
g_usleep (G_USEC_PER_SEC / 50);
|
||||
}
|
||||
g_assert (i > 0);
|
||||
nm_close (nm_steal_fd (&info->keepalive_fd));
|
||||
|
||||
g_object_unref (info->bus);
|
||||
nm_close (info->keepalive_fd);
|
||||
g_clear_object (&info->proxy);
|
||||
|
||||
#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_GLIB
|
||||
g_clear_pointer (&info->libdbus.bus, dbus_g_connection_unref);
|
||||
#endif
|
||||
|
||||
if (info->pid != NM_PID_T_INVAL) {
|
||||
kill (info->pid, SIGTERM);
|
||||
|
||||
t = g_get_monotonic_time ();
|
||||
again_wait:
|
||||
ret = waitpid (info->pid, &status, WNOHANG);
|
||||
if (ret == 0) {
|
||||
if (t + 2000000 < g_get_monotonic_time ()) {
|
||||
kill (info->pid, SIGKILL);
|
||||
g_error ("child process %lld did not exit within timeout", (long long) info->pid);
|
||||
}
|
||||
g_usleep (G_USEC_PER_SEC / 50);
|
||||
goto again_wait;
|
||||
}
|
||||
if (ret == -1 && errno == EINTR)
|
||||
goto again_wait;
|
||||
|
||||
g_assert (ret == info->pid);
|
||||
}
|
||||
|
||||
g_assert (!name_exists (info->bus, "org.freedesktop.NetworkManager"));
|
||||
|
||||
g_clear_object (&info->bus);
|
||||
|
||||
memset (info, 0, sizeof (*info));
|
||||
g_free (info);
|
||||
}
|
||||
|
Reference in New Issue
Block a user