about summary refs log tree commit diff
path: root/shared/nm-test-utils-impl.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/nm-test-utils-impl.c')
-rw-r--r--shared/nm-test-utils-impl.c207
1 files changed, 191 insertions, 16 deletions
diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c
index eca037ba..0805d0b9 100644
--- a/shared/nm-test-utils-impl.c
+++ b/shared/nm-test-utils-impl.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * 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 2010 - 2015 Red Hat, Inc.
- *
+ * Copyright (C) 2010 - 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -420,3 +406,192 @@ nmtstc_service_update_connection_variant (NMTstcServiceInfo *sinfo,
 	g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE ("()")));
 	g_variant_unref (result);
 }
+
+/*****************************************************************************/
+
+typedef struct {
+	GMainLoop *loop;
+	NMClient *client;
+} NMTstcClientNewData;
+
+static void
+_nmtstc_client_new_cb (GObject *source_object,
+                       GAsyncResult *res,
+                       gpointer user_data)
+{
+	NMTstcClientNewData *d = user_data;
+	gs_free_error GError *error = NULL;
+
+	g_assert (!d->client);
+
+	d->client = nm_client_new_finish (res,
+	                                  nmtst_get_rand_bool () ? &error : NULL);
+
+	nmtst_assert_success (NM_IS_CLIENT (d->client), error);
+
+	g_main_loop_quit (d->loop);
+}
+
+static NMClient *
+_nmtstc_client_new (gboolean sync)
+{
+	gs_free_error GError *error = NULL;
+	NMClient *client;
+
+	/* Create a NMClient instance synchronously, and arbitrarily use either
+	 * the sync or async constructor.
+	 *
+	 * Note that the sync and async construct differ in one important aspect:
+	 * the async constructor iterates the current g_main_context_get_thread_default(),
+	 * while the sync constructor does not! Aside from that, both should behave
+	 * pretty much the same way. */
+
+	if (sync) {
+		nm_auto_destroy_and_unref_gsource GSource *source = NULL;
+
+		if (nmtst_get_rand_bool ()) {
+			/* the current main context must not be iterated! */
+			source = g_idle_source_new ();
+			g_source_set_callback (source, nmtst_g_source_assert_not_called, NULL, NULL);
+			g_source_attach (source, g_main_context_get_thread_default ());
+		}
+
+		if (nmtst_get_rand_bool ()) {
+			gboolean success;
+
+			client = g_object_new (NM_TYPE_CLIENT, NULL);
+			g_assert (NM_IS_CLIENT (client));
+
+			success = g_initable_init (G_INITABLE (client),
+			                           NULL,
+			                           nmtst_get_rand_bool () ? &error : NULL);
+			nmtst_assert_success (success, error);
+		} else {
+			client = nm_client_new (NULL,
+			                        nmtst_get_rand_bool () ? &error : NULL);
+		}
+	} else {
+		nm_auto_unref_gmainloop GMainLoop *loop = NULL;
+		NMTstcClientNewData d = { .loop = NULL, };
+
+		loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
+
+		d.loop = loop;
+		nm_client_new_async (NULL,
+		                     _nmtstc_client_new_cb,
+		                     &d);
+		g_main_loop_run (loop);
+		g_assert (NM_IS_CLIENT (d.client));
+		client = d.client;
+	}
+
+	nmtst_assert_success (NM_IS_CLIENT (client), error);
+	return client;
+}
+
+typedef struct {
+	GMainLoop *loop;
+	NMClient *client;
+	bool sync;
+} NewSyncInsideDispatchedData;
+
+static gboolean
+_nmtstc_client_new_inside_loop_do (gpointer user_data)
+{
+	NewSyncInsideDispatchedData *d = user_data;
+
+	g_assert (d->loop);
+	g_assert (!d->client);
+
+	d->client = nmtstc_client_new (d->sync);
+	g_main_loop_quit (d->loop);
+	return G_SOURCE_CONTINUE;
+}
+
+static NMClient *
+_nmtstc_client_new_inside_loop (gboolean sync)
+{
+	GMainContext *context = g_main_context_get_thread_default ();
+	nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new (context, FALSE);
+	NewSyncInsideDispatchedData d = {
+		.sync = sync,
+		.loop = loop,
+	};
+	nm_auto_destroy_and_unref_gsource GSource *source = NULL;
+
+	source = g_idle_source_new ();
+	g_source_set_callback (source, _nmtstc_client_new_inside_loop_do, &d, NULL);
+	g_source_attach (source, context);
+
+	g_main_loop_run (loop);
+	g_assert (NM_IS_CLIENT (d.client));
+	return d.client;
+}
+
+static NMClient *
+_nmtstc_client_new_extra_context (void)
+{
+	GMainContext *inner_context;
+	NMClient *client;
+	GSource *source;
+	guint key_idx;
+
+	inner_context = g_main_context_new ();
+	g_main_context_push_thread_default (inner_context);
+
+	client = nmtstc_client_new (TRUE);
+
+	source = nm_utils_g_main_context_create_integrate_source (inner_context);
+
+	g_main_context_pop_thread_default (inner_context);
+	g_main_context_unref (inner_context);
+
+	g_source_attach (source, g_main_context_get_thread_default ());
+
+	for (key_idx = 0; TRUE; key_idx++) {
+		char s[100];
+
+		/* nmtstc_client_new() may call _nmtstc_client_new_extra_context() repeatedly. We
+		 * need to attach the source to a previously unused key. */
+		nm_sprintf_buf (s, "nm-test-extra-context-%u", key_idx);
+		if (!g_object_get_data (G_OBJECT (client), s)) {
+			g_object_set_data_full (G_OBJECT (client),
+			                        s,
+			                        source,
+			                        (GDestroyNotify) nm_g_source_destroy_and_unref);
+			break;
+		}
+	}
+
+	return client;
+}
+
+NMClient *
+nmtstc_client_new (gboolean allow_iterate_main_context)
+{
+	gboolean inside_loop;
+	gboolean sync;
+
+	if (nmtst_get_rand_uint32 () % 5 == 0)
+		return _nmtstc_client_new_extra_context ();
+
+	if (!allow_iterate_main_context) {
+		sync = TRUE;
+		inside_loop = FALSE;
+	} else {
+		/* The caller allows to iterate the main context. That that point,
+		 * we can both use the synchronous and the asynchronous initialization,
+		 * both should yield the same result. Choose one randomly. */
+		sync = nmtst_get_rand_bool ();
+		inside_loop = ((nmtst_get_rand_uint32 () % 3) == 0);
+	}
+
+	if (inside_loop) {
+		/* Create the client on an idle handler of the current context.
+		 * In practice, it should make no difference, which this check
+		 * tries to prove. */
+		return _nmtstc_client_new_inside_loop (sync);
+	}
+
+	return _nmtstc_client_new (sync);
+}