diff --git a/Makefile.am b/Makefile.am index 638055c91d..7f5c328f20 100644 --- a/Makefile.am +++ b/Makefile.am @@ -948,8 +948,11 @@ EXTRA_DIST += \ libnm/libnm.pc.in \ libnm/libnm.ver +libnm_NM_1_0_typelib = if HAVE_INTROSPECTION +libnm_NM_1_0_typelib += libnm/NM-1.0.typelib + libnm/NM-1.0.gir: libnm/libnm.la libnm_NM_1_0_gir_INCLUDES = Gio-2.0 libnm_NM_1_0_gir_PACKAGES = gio-2.0 @@ -1059,12 +1062,20 @@ EXTRA_DIST += \ ############################################################################### libnm_tests_programs = \ - libnm/tests/test-general \ + libnm/tests/test-general + +check_programs += $(libnm_tests_programs) + +libnm_tests_programs_req_introspection = \ libnm/tests/test-nm-client \ libnm/tests/test-remote-settings-client \ libnm/tests/test-secret-agent -check_programs += $(libnm_tests_programs) +if HAVE_INTROSPECTION +check_programs += $(libnm_tests_programs_req_introspection) +else +check_programs_norun += $(libnm_tests_programs_req_introspection) +endif libnm_tests_cppflags = \ $(dflt_cppflags_libnm_core) \ @@ -1122,6 +1133,12 @@ $(libnm_tests_test_nm_client_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(libnm_tests_test_remote_settings_client_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(libnm_tests_test_secret_agent_OBJECTS): $(libnm_core_lib_h_pub_mkenums) +# tools/test-networkmanager-service.py uses libnm's typelib. Ensure it +# is built first. +$(libnm_tests_test_nm_client_OBJECTS): $(libnm_NM_1_0_typelib) +$(libnm_tests_test_remote_settings_client_OBJECTS): $(libnm_NM_1_0_typelib) +$(libnm_tests_test_secret_agent_OBJECTS): $(libnm_NM_1_0_typelib) + ############################################################################### # just test, that we can build "nm-vpn-plugin-utils.c" @@ -4431,10 +4448,16 @@ libnm_glib_tests_cppflags = \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) -check_programs += \ +libnm_glib_tests_programs_req_introspection = \ libnm-glib/tests/test-nm-client \ libnm-glib/tests/test-remote-settings-client +if HAVE_INTROSPECTION +check_programs += $(libnm_glib_tests_programs_req_introspection) +else +check_programs_norun += $(libnm_glib_tests_programs_req_introspection) +endif + libnm_glib_tests_test_nm_client_CPPFLAGS = $(libnm_glib_tests_cppflags) libnm_glib_tests_test_nm_client_SOURCES = \ @@ -4466,6 +4489,11 @@ libnm_glib_tests_test_remote_settings_client_LDADD = \ $(libnm_glib_tests_test_remote_settings_client_OBJECTS): $(libnm_core_lib_h_pub_mkenums) +# tools/test-networkmanager-service.py uses libnm's typelib. Ensure it +# is built first. +$(libnm_glib_tests_test_nm_client_OBJECTS): $(libnm_NM_1_0_typelib) +$(libnm_glib_tests_test_remote_settings_client_OBJECTS): $(libnm_NM_1_0_typelib) + endif EXTRA_DIST += \ diff --git a/libnm-glib/tests/test-nm-client.c b/libnm-glib/tests/test-nm-client.c index 9432520336..df85721538 100644 --- a/libnm-glib/tests/test-nm-client.c +++ b/libnm-glib/tests/test-nm-client.c @@ -155,6 +155,9 @@ test_device_added (void) DeviceAddedInfo info = { loop, FALSE, FALSE, 0, 0 }; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nmtstc_nm_client_new (); devices = nm_client_get_devices (client); @@ -312,6 +315,9 @@ test_wifi_ap_added_removed (void) char *expected_path = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nmtstc_nm_client_new (); /*************************************/ @@ -535,6 +541,9 @@ test_wimax_nsp_added_removed (void) char *expected_path = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nmtstc_nm_client_new (); /*************************************/ @@ -720,6 +729,9 @@ test_devices_array (void) GVariant *ret; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nmtstc_nm_client_new (); /*************************************/ @@ -820,7 +832,8 @@ manager_running_changed (GObject *client, static void test_client_manager_running (void) { - NMClient *client1, *client2; + gs_unref_object NMClient *client1 = NULL; + gs_unref_object NMClient *client2 = NULL; guint quit_id; int running_changed = 0; GError *error = NULL; @@ -842,6 +855,9 @@ test_client_manager_running (void) /* Now start the test service. */ sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client2 = nmtstc_nm_client_new (); /* client2 should know that NM is running, but the previously-created @@ -868,9 +884,6 @@ test_client_manager_running (void) g_assert_cmpint (running_changed, ==, 2); g_assert (!nm_client_get_manager_running (client1)); g_source_remove (quit_id); - - g_object_unref (client1); - g_object_unref (client2); } /*****************************************************************************/ diff --git a/libnm-glib/tests/test-remote-settings-client.c b/libnm-glib/tests/test-remote-settings-client.c index 0f3492981e..031e83f81c 100644 --- a/libnm-glib/tests/test-remote-settings-client.c +++ b/libnm-glib/tests/test-remote-settings-client.c @@ -70,6 +70,9 @@ test_add_connection (void) time_t start, now; gboolean done = FALSE; + if (!nmtstc_service_available (sinfo)) + return; + connection = nm_connection_new (); s_con = (NMSettingConnection *) nm_setting_connection_new (); @@ -145,6 +148,9 @@ test_make_invisible (void) gboolean done = FALSE, has_settings = FALSE; char *path; + if (!nmtstc_service_available (sinfo)) + return; + g_assert (remote != NULL); /* Listen for the remove event when the connection becomes invisible */ @@ -212,6 +218,9 @@ test_make_visible (void) char *path; NMRemoteConnection *new = NULL; + if (!nmtstc_service_available (sinfo)) + return; + g_assert (remote != NULL); /* Wait for the new-connection signal when the connection is visible again */ @@ -292,6 +301,9 @@ test_remove_connection (void) gboolean done = FALSE; char *path; + if (!nmtstc_service_available (sinfo)) + return; + /* Find a connection to delete */ list = nm_remote_settings_list_connections (settings); g_assert_cmpint (g_slist_length (list), >, 0); @@ -360,11 +372,14 @@ settings_service_running_changed (GObject *client, static void test_service_running (void) { - NMRemoteSettings *settings2; + gs_unref_object NMRemoteSettings *settings2 = NULL; guint quit_id; int running_changed = 0; gboolean running; + if (!nmtstc_service_available (sinfo)) + return; + loop = g_main_loop_new (NULL, FALSE); g_object_get (G_OBJECT (settings), @@ -403,6 +418,7 @@ test_service_running (void) /* Now restart it */ sinfo = nmtstc_service_init (); + g_assert (nmtstc_service_available (sinfo)); quit_id = g_timeout_add_seconds (5, loop_quit, loop); g_main_loop_run (loop); @@ -413,8 +429,6 @@ test_service_running (void) NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running, NULL); g_assert (running == TRUE); - - g_object_unref (settings2); } /*****************************************************************************/ diff --git a/libnm/tests/test-nm-client.c b/libnm/tests/test-nm-client.c index b40e67dd64..21272e4d4f 100644 --- a/libnm/tests/test-nm-client.c +++ b/libnm/tests/test-nm-client.c @@ -70,6 +70,9 @@ test_device_added (void) GError *error = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -163,6 +166,9 @@ test_device_added_signal_after_init (void) GError *error = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -311,6 +317,9 @@ test_wifi_ap_added_removed (void) char *expected_path = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -510,6 +519,9 @@ test_wimax_nsp_added_removed (void) char *expected_path = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -686,6 +698,8 @@ test_devices_array (void) GVariant *ret; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; /* Make sure that we test the async codepath in at least one test... */ nm_client_new_async (NULL, new_client_cb, &client); @@ -778,7 +792,8 @@ nm_running_changed (GObject *client, static void test_client_nm_running (void) { - NMClient *client1, *client2; + gs_unref_object NMClient *client1 = NULL; + gs_unref_object NMClient *client2 = NULL; guint quit_id; int running_changed = 0; GError *error = NULL; @@ -801,6 +816,9 @@ test_client_nm_running (void) /* Now start the test service. */ sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client2 = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -828,9 +846,6 @@ test_client_nm_running (void) g_assert_cmpint (running_changed, ==, 2); g_assert (!nm_client_get_nm_running (client1)); g_source_remove (quit_id); - - g_object_unref (client1); - g_object_unref (client2); } typedef struct { @@ -934,6 +949,9 @@ test_active_connections (void) GError *error = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -1063,6 +1081,9 @@ test_activate_virtual (void) GError *error = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -1138,6 +1159,9 @@ test_activate_failed (void) GError *error = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -1172,6 +1196,9 @@ test_device_connection_compatibility (void) const char *hw_addr2 = "52:54:00:ab:db:24"; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -1593,4 +1620,3 @@ main (int argc, char **argv) return g_test_run (); } - diff --git a/libnm/tests/test-remote-settings-client.c b/libnm/tests/test-remote-settings-client.c index f6c37e9196..45d60c069a 100644 --- a/libnm/tests/test-remote-settings-client.c +++ b/libnm/tests/test-remote-settings-client.c @@ -63,6 +63,9 @@ test_add_connection (void) time_t start, now; gboolean done = FALSE; + if (!nmtstc_service_available (sinfo)) + return; + connection = nmtst_create_minimal_connection (TEST_CON_ID, NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); nm_client_add_connection_async (client, @@ -137,6 +140,9 @@ test_make_invisible (void) gboolean has_settings = FALSE; char *path; + if (!nmtstc_service_available (sinfo)) + return; + g_assert (remote != NULL); /* Listen for the remove event when the connection becomes invisible */ @@ -215,6 +221,9 @@ test_make_visible (void) char *path; NMRemoteConnection *new = NULL; + if (!nmtstc_service_available (sinfo)) + return; + g_assert (remote != NULL); /* Wait for the new-connection signal when the connection is visible again */ @@ -304,6 +313,9 @@ test_remove_connection (void) gboolean done = FALSE; char *path; + if (!nmtstc_service_available (sinfo)) + return; + /* Find a connection to delete */ conns = nm_client_get_connections (client); g_assert_cmpint (conns->len, >, 0); @@ -384,6 +396,9 @@ test_add_remove_connection (void) time_t start, now; gboolean done = FALSE; + if (!nmtstc_service_available (sinfo)) + return; + /* This will cause the test server to immediately delete the connection * after creating it. */ @@ -437,6 +452,9 @@ test_add_bad_connection (void) time_t start, now; gboolean done = FALSE; + if (!nmtstc_service_available (sinfo)) + return; + /* The test daemon doesn't support bond connections */ connection = nmtst_create_minimal_connection ("bad connection test", NULL, NM_SETTING_BOND_SETTING_NAME, NULL); @@ -480,6 +498,9 @@ test_save_hostname (void) gboolean done = FALSE; GError *error = NULL; + if (!nmtstc_service_available (sinfo)) + return; + /* test-networkmanager-service.py requires the hostname to contain a '.' */ nm_client_save_hostname (client, "foo", NULL, &error); g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_HOSTNAME); diff --git a/libnm/tests/test-secret-agent.c b/libnm/tests/test-secret-agent.c index 911ef4488b..746a9e6437 100644 --- a/libnm/tests/test-secret-agent.c +++ b/libnm/tests/test-secret-agent.c @@ -247,6 +247,9 @@ test_setup (TestSecretAgentData *sadata, gconstpointer test_data) GError *error = NULL; sadata->sinfo = nmtstc_service_init (); + if (!sadata->sinfo) + return; + sadata->client = nm_client_new (NULL, &error); g_assert_no_error (error); @@ -311,6 +314,9 @@ test_cleanup (TestSecretAgentData *sadata, gconstpointer test_data) GVariant *ret; GError *error = NULL; + if (!sadata->sinfo) + return; + if (sadata->agent) { if (nm_secret_agent_old_get_registered (sadata->agent)) { nm_secret_agent_old_unregister (sadata->agent, NULL, &error); @@ -360,6 +366,9 @@ connection_activated_none_cb (GObject *c, static void test_secret_agent_none (TestSecretAgentData *sadata, gconstpointer test_data) { + if (!nmtstc_service_available (sadata->sinfo)) + return; + nm_client_activate_connection_async (sadata->client, sadata->connection, sadata->device, @@ -405,6 +414,9 @@ connection_activated_no_secrets_cb (GObject *c, static void test_secret_agent_no_secrets (TestSecretAgentData *sadata, gconstpointer test_data) { + if (!nmtstc_service_available (sadata->sinfo)) + return; + g_signal_connect (sadata->agent, "secret-requested", G_CALLBACK (secrets_requested_no_secrets_cb), sadata); @@ -456,6 +468,9 @@ secrets_requested_cancel_cb (TestSecretAgent *agent, static void test_secret_agent_cancel (TestSecretAgentData *sadata, gconstpointer test_data) { + if (!nmtstc_service_available (sadata->sinfo)) + return; + g_signal_connect (sadata->agent, "secret-requested", G_CALLBACK (secrets_requested_cancel_cb), sadata); @@ -510,6 +525,9 @@ secrets_requested_good_cb (TestSecretAgent *agent, static void test_secret_agent_good (TestSecretAgentData *sadata, gconstpointer test_data) { + if (!nmtstc_service_available (sadata->sinfo)) + return; + g_signal_connect (sadata->agent, "secret-requested", G_CALLBACK (secrets_requested_good_cb), sadata); @@ -582,6 +600,9 @@ test_secret_agent_auto_register (void) GError *error = NULL; sinfo = nmtstc_service_init (); + if (!nmtstc_service_available (sinfo)) + return; + loop = g_main_loop_new (NULL, FALSE); agent = test_secret_agent_new (); @@ -609,6 +630,8 @@ test_secret_agent_auto_register (void) /* Restart test service */ sinfo = nmtstc_service_init (); + g_assert (nmtstc_service_available (sinfo)); + g_main_loop_run (loop); g_assert (nm_secret_agent_old_get_registered (agent)); diff --git a/shared/nm-test-libnm-utils.h b/shared/nm-test-libnm-utils.h index c4731a520d..20a15e5f85 100644 --- a/shared/nm-test-libnm-utils.h +++ b/shared/nm-test-libnm-utils.h @@ -42,18 +42,22 @@ typedef struct { NMTstcServiceInfo *nmtstc_service_init (void); void nmtstc_service_cleanup (NMTstcServiceInfo *info); +NMTstcServiceInfo *nmtstc_service_available (NMTstcServiceInfo *info); static inline void _nmtstc_auto_service_cleanup (NMTstcServiceInfo **info) { - if (info && *info) { - nmtstc_service_cleanup (*info); - *info = NULL; - } + nmtstc_service_cleanup (g_steal_pointer (info)); } - #define NMTSTC_SERVICE_INFO_SETUP(sinfo) \ NM_PRAGMA_WARNING_DISABLE ("-Wunused-variable") \ - __attribute__ ((cleanup(_nmtstc_auto_service_cleanup))) NMTstcServiceInfo *sinfo = nmtstc_service_init (); \ + __attribute__ ((cleanup(_nmtstc_auto_service_cleanup))) NMTstcServiceInfo *sinfo = ({ \ + NMTstcServiceInfo *_sinfo; \ + \ + _sinfo = nmtstc_service_init (); \ + if (!nmtstc_service_available (_sinfo)) \ + return; \ + _sinfo; \ + }); \ NM_PRAGMA_WARNING_REENABLE /*****************************************************************************/ diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c index 998d792a2a..6d6e54c17f 100644 --- a/shared/nm-test-utils-impl.c +++ b/shared/nm-test-utils-impl.c @@ -21,6 +21,7 @@ #include "nm-default.h" #include +#include #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); } diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index 68097778ea..17bd7e28c0 100755 --- a/tools/test-networkmanager-service.py +++ b/tools/test-networkmanager-service.py @@ -3,8 +3,18 @@ from __future__ import print_function -from gi.repository import GLib import sys + +import gi +from gi.repository import GLib + +try: + gi.require_version('NM', '1.0') + from gi.repository import NM +except Exception as e: + print("Cannot load gi.NM: %s" % (str(e))) + sys.exit(77) + import dbus import dbus.service import dbus.mainloop.glib