diff options
Diffstat (limited to 'src/libnm-client-impl/tests/test-nm-client.c')
| -rw-r--r-- | src/libnm-client-impl/tests/test-nm-client.c | 406 |
1 files changed, 325 insertions, 81 deletions
diff --git a/src/libnm-client-impl/tests/test-nm-client.c b/src/libnm-client-impl/tests/test-nm-client.c index a43e98cd..216e3e42 100644 --- a/src/libnm-client-impl/tests/test-nm-client.c +++ b/src/libnm-client-impl/tests/test-nm-client.c @@ -8,7 +8,10 @@ #include <sys/types.h> #include <signal.h> +#include "libnm-client-impl/nm-dbus-helpers.h" + #include "libnm-client-test/nm-test-libnm-utils.h" +#include "libnm-glib-aux/nm-time-utils.h" static struct { GMainLoop *loop; @@ -495,7 +498,8 @@ nm_running_changed(GObject *client, GParamSpec *pspec, gpointer user_data) { int *running_changed = user_data; - (*running_changed)++; + if (running_changed) + (*running_changed)++; g_main_loop_quit(gl.loop); } @@ -788,50 +792,15 @@ activate_cb(GObject *object, GAsyncResult *result, gpointer user_data) g_main_loop_quit(info->loop); } -static void -_dev_eth0_1_state_changed_cb(NMDevice *device, - NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - int *p_count_call) +static NMClient * +_activate_virtual(NMTstcServiceInfo *sinfo) { - const GPtrArray *arr; - - g_assert(p_count_call); - g_assert_cmpint(*p_count_call, ==, 0); - - (*p_count_call)++; - - g_assert(NM_IS_DEVICE_VLAN(device)); - - g_assert_cmpint(old_state, >=, NM_DEVICE_STATE_PREPARE); - g_assert_cmpint(old_state, <=, NM_DEVICE_STATE_ACTIVATED); - g_assert_cmpint(new_state, ==, NM_DEVICE_STATE_UNKNOWN); - - arr = nm_device_get_available_connections(device); - g_assert(arr); - g_assert_cmpint(arr->len, ==, 0); - - g_assert(!nm_device_get_active_connection(device)); -} - -static void -test_activate_virtual(void) -{ - nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; - gs_unref_object NMClient *client = NULL; - NMConnection *conn; - NMSettingConnection *s_con; - NMSettingVlan *s_vlan; - TestACInfo info = {gl.loop, NULL, 0}; - TestConnectionInfo conn_info = {gl.loop, NULL}; - - if (nmtst_test_skip_slow()) - return; - - sinfo = nmtstc_service_init(); - if (!nmtstc_service_available(sinfo)) - return; + NMClient *client = NULL; + NMConnection *conn; + NMSettingConnection *s_con; + NMSettingVlan *s_vlan; + TestACInfo info = {gl.loop, NULL, 0}; + TestConnectionInfo conn_info = {gl.loop, NULL}; client = nmtstc_client_new(TRUE); @@ -878,55 +847,89 @@ test_activate_virtual(void) nm_clear_g_signal_handler(info.device, &info.ac_signal_id); } - if (nmtst_get_rand_bool()) { - /* OK, enough for this run. Let's see whether we can tear down - * successfully at this point. */ + return client; +} + +static void +test_activate_virtual(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + + if (nmtst_test_skip_slow()) return; - } - { - NMDevice *dev_eth0_1; - NMActiveConnection *ac; - const GPtrArray *arr; - gulong sig_id; - int call_count = 0; - gboolean take_ref = nmtst_get_rand_bool(); + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; - /* ensure we got all the necessary events in place. */ - nmtst_main_loop_run(gl.loop, 50); + g_object_unref(_activate_virtual(sinfo)); +} - dev_eth0_1 = nm_client_get_device_by_iface(client, "eth0.1"); - g_assert(NM_IS_DEVICE_VLAN(dev_eth0_1)); - if (take_ref) - g_object_ref(dev_eth0_1); +static void +_client_dev_removed(NMClient *client, NMDevice *device, int *p_count_call) +{ + (*p_count_call)++; +} - arr = nm_device_get_available_connections(dev_eth0_1); - g_assert(arr); - g_assert_cmpint(arr->len, ==, 1); +static void +test_activate_virtual_teardown(gconstpointer user_data) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + NMDevice *dev_eth0_1; + NMActiveConnection *ac; + const GPtrArray *arr; + int call_count = 0; + gboolean take_ref = nmtst_get_rand_bool(); + gboolean teardown_service = GPOINTER_TO_INT(user_data); - ac = nm_device_get_active_connection(dev_eth0_1); - g_assert(NM_IS_ACTIVE_CONNECTION(ac)); + if (nmtst_test_skip_slow()) + return; - sig_id = g_signal_connect(dev_eth0_1, - "state-changed", - G_CALLBACK(_dev_eth0_1_state_changed_cb), - &call_count); + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; - g_clear_object(&client); + client = _activate_virtual(sinfo); - g_assert_cmpint(call_count, ==, 1); + /* ensure we got all the necessary events in place. */ + nmtst_main_loop_run(gl.loop, 50); - if (take_ref) { - arr = nm_device_get_available_connections(dev_eth0_1); - g_assert(arr); - g_assert_cmpint(arr->len, ==, 0); + dev_eth0_1 = nm_client_get_device_by_iface(client, "eth0.1"); + g_assert(NM_IS_DEVICE_VLAN(dev_eth0_1)); + if (take_ref) + g_object_ref(dev_eth0_1); - g_assert(!nm_device_get_active_connection(dev_eth0_1)); + arr = nm_device_get_available_connections(dev_eth0_1); + g_assert(arr); + g_assert_cmpint(arr->len, ==, 1); - nm_clear_g_signal_handler(dev_eth0_1, &sig_id); + ac = nm_device_get_active_connection(dev_eth0_1); + g_assert(NM_IS_ACTIVE_CONNECTION(ac)); - g_object_unref(dev_eth0_1); - } + g_signal_connect(client, "device-removed", G_CALLBACK(_client_dev_removed), &call_count); + + if (teardown_service) { + g_signal_connect(client, + "notify::" NM_CLIENT_NM_RUNNING, + G_CALLBACK(nm_running_changed), + NULL); + nm_clear_pointer(&sinfo, nmtstc_service_cleanup); + nmtst_main_loop_run(gl.loop, 1000); + g_assert_cmpint(call_count, ==, 2); + } else { + g_clear_object(&client); + g_assert_cmpint(call_count, ==, 0); + } + + if (take_ref) { + arr = nm_device_get_available_connections(dev_eth0_1); + g_assert(arr); + g_assert_cmpint(arr->len, ==, 0); + + g_assert(!nm_device_get_active_connection(dev_eth0_1)); + + g_object_unref(dev_eth0_1); } } @@ -1349,6 +1352,240 @@ test_connection_invalid(void) /*****************************************************************************/ +typedef struct { + NMClient *nmc; + NMOptionBool completed; +} WaitShutdownNmcInitData; + +static void +_wait_shutdown_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + gs_free_error GError *error = NULL; + gboolean b; + guint *p_pending_ops = user_data; + + g_assert(!source); + + if (nmtst_get_rand_bool()) { + b = nm_client_wait_shutdown_finish(result, &error); + g_assert(b == !error); + } + + g_assert_cmpint(*p_pending_ops, >, 0); + (*p_pending_ops)--; +} + +static void +_wait_shutdown_nmc_init_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + WaitShutdownNmcInitData *data = user_data; + gs_free_error GError *error = NULL; + gboolean b; + + g_assert(data->nmc == NM_CLIENT(source)); + g_assert(data->completed == NM_OPTION_BOOL_DEFAULT); + + b = g_async_initable_init_finish(G_ASYNC_INITABLE(source), result, &error); + nmtst_assert_success(b, error); + g_assert(b == TRUE); + + g_assert(NM_FLAGS_HAS(nm_client_get_instance_flags(data->nmc), + NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD)); + + data->completed = TRUE; +} + +static void +test_client_wait_shutdown(void) +{ + gs_unref_ptrarray GPtrArray *contexts = + g_ptr_array_new_with_free_func((GDestroyNotify) g_main_context_unref); + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + int i_run; + const int N_RUN = 50; + gs_free_error GError *error = NULL; + gs_unref_object GDBusConnection *dbus_connection = NULL; + guint pending_ops = 0; + int i; + gint64 until_msec; + + /* The test creates NMClient instances in various ways (with different + * context of g_main_context_default()) and with sync/asnyc/no initialization. + * + * Then it calls nm_client_wait_shutdown() and checks that all callbacks + * got invoked. + * + * You can also manually bump N_RUN and convince yourself that there are no + * leaks. + */ + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + /* Have 5 contexts for creating clients (+ the default one). */ + g_ptr_array_add(contexts, g_main_context_ref(g_main_context_default())); + for (i = 0; i < 5; i++) + g_ptr_array_add(contexts, g_main_context_new()); + + dbus_connection = g_bus_get_sync(_nm_dbus_bus_type(), NULL, &error); + nmtst_assert_success(dbus_connection, error); + + for (i_run = 0; i_run < N_RUN; i_run++) { + gs_unref_object GCancellable *init_cancellable = g_cancellable_new(); + gs_unref_object NMClient *nmc = NULL; + nm_auto_pop_gmaincontext GMainContext *client_context = NULL; + gboolean b; + gboolean context_integrated = FALSE; + gs_unref_object GCancellable *cancellable_1 = NULL; + GMainContext *ctx; + + /* Choose a random context for the client. */ + ctx = contexts->pdata[nmtst_get_rand_uint32() % contexts->len]; + if (ctx == g_main_context_default()) { + /* don't use the main context explicitly. */ + } else if (!g_main_context_acquire(ctx)) { + /* the context is in use, we cannot use it. */ + } else { + g_main_context_release(ctx); + client_context = g_main_context_ref(ctx); + g_main_context_push_thread_default(client_context); + } + + nmc = g_object_new( + NM_TYPE_CLIENT, + NM_CLIENT_INSTANCE_FLAGS, + nmtst_get_rand_one_case_in(5) ? NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS : 0, + NM_CLIENT_DBUS_CONNECTION, + nmtst_get_rand_one_case_in(3) ? NULL : dbus_connection, + NULL); + + if (nmtst_get_rand_bool()) { + /* randomly already pop the context before initializing nmc. */ + nm_clear_pointer(&client_context, nm_g_main_context_pop_and_unref); + } + + if (nmtst_get_rand_bool()) { + nm_auto_pop_gmaincontext GMainContext *context = + nm_g_main_context_push_thread_default(NULL); + + /* It is also allowed to start waiting before initialization started. Randomly do that. */ + pending_ops++; + nm_client_wait_shutdown( + nmc, + nmtst_true_once(&context_integrated, nmtst_get_rand_bool()), + (cancellable_1 = nmtst_get_rand_bool() ? g_cancellable_new() : NULL), + _wait_shutdown_cb, + &pending_ops); + } + + /* randomly do some initialization... */ + switch (nmtst_get_rand_uint32() % 4) { + case 0: + /* Don't start initialization. */ + break; + case 1: + /* Do sync initialization. */ + b = g_initable_init(G_INITABLE(nmc), + nmtst_get_rand_bool() ? init_cancellable : NULL, + &error); + nmtst_assert_success(b, error); + break; + case 2: + /* Start async initialization in the background, but don't wait for it complete. */ + g_async_initable_init_async(G_ASYNC_INITABLE(nmc), + G_PRIORITY_DEFAULT, + nmtst_get_rand_bool() ? init_cancellable : NULL, + NULL, + NULL); + break; + default: + { + /* Do async initialization, and iterate the main context until done. */ + WaitShutdownNmcInitData data = { + .completed = NM_OPTION_BOOL_DEFAULT, + .nmc = nmc, + }; + + g_async_initable_init_async(G_ASYNC_INITABLE(nmc), + G_PRIORITY_DEFAULT, + nmtst_get_rand_bool() ? init_cancellable : NULL, + _wait_shutdown_nmc_init_cb, + &data); + + while (data.completed != TRUE) { + g_main_context_iteration(context_integrated ? g_main_context_default() + : nm_client_get_main_context(nmc), + TRUE); + } + break; + } + } + + if (!context_integrated && nmtst_get_rand_bool()) { + nm_clear_g_cancellable(&cancellable_1); + } + + if (nmtst_get_rand_bool()) + nm_clear_pointer(&client_context, nm_g_main_context_pop_and_unref); + + { + gs_unref_object GCancellable *cancellable = NULL; + + pending_ops++; + nm_client_wait_shutdown( + nmc, + nmtst_true_once(&context_integrated, nmtst_get_rand_bool()), + (cancellable = nmtst_get_rand_bool() ? g_cancellable_new() : NULL), + _wait_shutdown_cb, + &pending_ops); + } + + if (nmtst_get_rand_bool()) + g_clear_object(&nmc); + + for (i = 0; i < (int) contexts->len; i++) { + GMainContext *context = contexts->pdata[i]; + + if (nmtst_get_rand_bool()) { + /* iterate next time. */ + continue; + } + + if (!g_main_context_acquire(context)) { + /* It's integrated, skip. */ + continue; + } + g_main_context_release(context); + + while (g_main_context_iteration(context, FALSE)) { + /* pass */ + } + } + } + + until_msec = nm_utils_get_monotonic_timestamp_msec() + 10000; + while (pending_ops > 0 && nm_utils_get_monotonic_timestamp_msec() < until_msec) { + for (i = contexts->len; i > 0; i--) { + GMainContext *context = contexts->pdata[i - 1]; + + if (!g_main_context_acquire(context)) { + /* integrated, skip. */ + continue; + } + g_main_context_release(context); + + while (g_main_context_iteration(context, FALSE)) { + /* pass */ + } + } + } + + g_assert_cmpint(pending_ops, ==, 0); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -1366,9 +1603,16 @@ main(int argc, char **argv) g_test_add_func("/libnm/devices-array", test_devices_array); g_test_add_func("/libnm/client-nm-running", test_client_nm_running); g_test_add_func("/libnm/active-connections", test_active_connections); - g_test_add_func("/libnm/activate-virtual", test_activate_virtual); + g_test_add_func("/libnm/activate-virtual/without-teardown", test_activate_virtual); + g_test_add_data_func("/libnm/activate-virtual/with-teardown/service", + GINT_TO_POINTER(true), + test_activate_virtual_teardown); + g_test_add_data_func("/libnm/activate-virtual/with-teardown/client", + GINT_TO_POINTER(false), + test_activate_virtual_teardown); g_test_add_func("/libnm/device-connection-compatibility", test_device_connection_compatibility); g_test_add_func("/libnm/connection/invalid", test_connection_invalid); + g_test_add_func("/libnm/test_client_wait_shutdown", test_client_wait_shutdown); return g_test_run(); } |