summary refs log tree commit diff
path: root/libnm/tests
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-12-18 18:29:24 +0100
committerMichael Biebl <biebl@debian.org>2019-12-18 18:29:24 +0100
commit28028b26b3371756811e95d894f709f4b1207c00 (patch)
tree6fe7316fd743b51042db47601a8ef8814b3134ac /libnm/tests
parente22609983008e1a669196ad64ba3a59ae8c76e0d (diff)
New upstream version 1.22.0 upstream/1.22.0
Diffstat (limited to 'libnm/tests')
-rw-r--r--libnm/tests/meson.build36
-rw-r--r--libnm/tests/test-libnm.c673
-rw-r--r--libnm/tests/test-nm-client.c480
-rw-r--r--libnm/tests/test-remote-settings-client.c225
-rw-r--r--libnm/tests/test-secret-agent.c23
5 files changed, 857 insertions, 580 deletions
diff --git a/libnm/tests/meson.build b/libnm/tests/meson.build
index 5b72a24f..76c3b287 100644
--- a/libnm/tests/meson.build
+++ b/libnm/tests/meson.build
@@ -1,11 +1,16 @@
 test_units = [
-  ['test-libnm',                  30],
-  ['test-nm-client',              90],
+  ['test-libnm', 30],
+  ['test-nm-client', 90],
   ['test-remote-settings-client', 90],
-  ['test-secret-agent',           90],
+  ['test-secret-agent', 90],
 ]
 
-cflags = [
+deps = [
+  libnm_core_dep,
+  libnm_nm_default_dep,
+]
+
+c_flags = [
   '-DNETWORKMANAGER_COMPILATION_TEST',
   '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM',
 ]
@@ -13,19 +18,10 @@ cflags = [
 foreach test_unit: test_units
   exe = executable(
     test_unit[0],
-    [
-      test_unit[0] + '.c',
-      shared_nm_test_utils_impl_c,
-    ],
-    dependencies: [
-      liblibnm_dep,
-      libnm_systemd_shared_no_logging_dep,
-    ],
-    c_args: cflags,
-    link_with: [
-      liblibnm,
-      libnm_core,
-    ],
+    [test_unit[0] + '.c', nm_test_utils_impl_source, libnm_enum_sources[1]],
+    dependencies: deps,
+    c_args: c_flags,
+    link_with: liblibnm,
   )
 
   test(
@@ -39,8 +35,8 @@ endforeach
 # just test, that we can build "nm-vpn-plugin-utils.c"
 libnm_vpn_plugin_utils_test = static_library(
   'nm-vpn-plugin-utils-test',
-  sources: shared_nm_utils_nm_vpn_plugin_utils_c + [libnm_enum[1]],
+  sources: nm_vpn_plugin_utils_source + [libnm_enum_sources[1]],
   include_directories: libnm_inc,
-  dependencies: libnm_core_dep,
-  c_args: cflags,
+  dependencies: libnm_nm_default_dep,
+  c_args: c_flags,
 )
diff --git a/libnm/tests/test-libnm.c b/libnm/tests/test-libnm.c
index 2a64fad8..345fbb07 100644
--- a/libnm/tests/test-libnm.c
+++ b/libnm/tests/test-libnm.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 SC 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 2017 Red Hat, Inc.
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -25,8 +11,16 @@
 
 #include <sys/mman.h>
 
+#include "NetworkManager.h"
+#include "nm-access-point.h"
+#include "nm-checkpoint.h"
+#include "nm-dhcp4-config.h"
+#include "nm-dhcp6-config.h"
+#include "nm-dns-manager.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
 #include "nm-libnm-utils.h"
-
+#include "nm-object.h"
 #include "nm-vpn-service-plugin.h"
 
 #include "nm-utils/nm-test-utils.h"
@@ -2430,6 +2424,648 @@ test_nm_vpn_service_plugin_read_vpn_details (void)
 
 /*****************************************************************************/
 
+static void
+test_types (void)
+{
+#define G(get_type_fcn) \
+	({ \
+		GType get_type_fcn (void); \
+		\
+		get_type_fcn; \
+	})
+	GType (*get_type_fcns[]) (void) = {
+		G (nm_802_11_ap_flags_get_type),
+		G (nm_802_11_ap_security_flags_get_type),
+		G (nm_802_11_mode_get_type),
+		G (nm_access_point_get_type),
+		G (nm_activation_state_flags_get_type),
+		G (nm_active_connection_get_type),
+		G (nm_active_connection_state_get_type),
+		G (nm_active_connection_state_reason_get_type),
+		G (nm_agent_manager_error_get_type),
+		G (nm_bluetooth_capabilities_get_type),
+		G (nm_bridge_vlan_get_type),
+		G (nm_capability_get_type),
+		G (nm_checkpoint_create_flags_get_type),
+		G (nm_checkpoint_get_type),
+		G (nm_client_error_get_type),
+		G (nm_client_get_type),
+		G (nm_client_permission_get_type),
+		G (nm_client_permission_result_get_type),
+		G (nm_connection_error_get_type),
+		G (nm_connection_get_type),
+		G (nm_connection_multi_connect_get_type),
+		G (nm_connection_serialization_flags_get_type),
+		G (nm_connectivity_state_get_type),
+		G (nm_crypto_error_get_type),
+		G (nm_device_6lowpan_get_type),
+		G (nm_device_adsl_get_type),
+		G (nm_device_bond_get_type),
+		G (nm_device_bridge_get_type),
+		G (nm_device_bt_get_type),
+		G (nm_device_capabilities_get_type),
+		G (nm_device_dummy_get_type),
+		G (nm_device_error_get_type),
+		G (nm_device_ethernet_get_type),
+		G (nm_device_generic_get_type),
+		G (nm_device_get_type),
+		G (nm_device_infiniband_get_type),
+		G (nm_device_ip_tunnel_get_type),
+		G (nm_device_macsec_get_type),
+		G (nm_device_macvlan_get_type),
+		G (nm_device_modem_capabilities_get_type),
+		G (nm_device_modem_get_type),
+		G (nm_device_olpc_mesh_get_type),
+		G (nm_device_ovs_bridge_get_type),
+		G (nm_device_ovs_interface_get_type),
+		G (nm_device_ovs_port_get_type),
+		G (nm_device_ppp_get_type),
+		G (nm_device_state_get_type),
+		G (nm_device_state_reason_get_type),
+		G (nm_device_team_get_type),
+		G (nm_device_tun_get_type),
+		G (nm_device_type_get_type),
+		G (nm_device_vlan_get_type),
+		G (nm_device_vxlan_get_type),
+		G (nm_device_wifi_capabilities_get_type),
+		G (nm_device_wifi_get_type),
+		G (nm_device_wifi_p2p_get_type),
+		G (nm_device_wimax_get_type),
+		G (nm_device_wireguard_get_type),
+		G (nm_device_wpan_get_type),
+		G (nm_dhcp4_config_get_type),
+		G (nm_dhcp6_config_get_type),
+		G (nm_dhcp_config_get_type),
+		G (nm_dns_entry_get_type),
+		G (nm_ip4_config_get_type),
+		G (nm_ip6_config_get_type),
+		G (nm_ip_address_get_type),
+		G (nm_ip_config_get_type),
+		G (nm_ip_route_get_type),
+		G (nm_ip_routing_rule_as_string_flags_get_type),
+		G (nm_ip_routing_rule_get_type),
+		G (nm_ip_tunnel_flags_get_type),
+		G (nm_ip_tunnel_mode_get_type),
+		G (nm_lldp_neighbor_get_type),
+		G (nm_manager_error_get_type),
+		G (nm_manager_reload_flags_get_type),
+		G (nm_metered_get_type),
+		G (nm_object_get_type),
+		G (nm_remote_connection_get_type),
+		G (nm_secret_agent_capabilities_get_type),
+		G (nm_secret_agent_error_get_type),
+		G (nm_secret_agent_get_secrets_flags_get_type),
+		G (nm_secret_agent_old_get_type),
+		G (nm_setting_6lowpan_get_type),
+		G (nm_setting_802_1x_auth_flags_get_type),
+		G (nm_setting_802_1x_ck_format_get_type),
+		G (nm_setting_802_1x_ck_scheme_get_type),
+		G (nm_setting_802_1x_get_type),
+		G (nm_setting_adsl_get_type),
+		G (nm_setting_bluetooth_get_type),
+		G (nm_setting_bond_get_type),
+		G (nm_setting_bridge_get_type),
+		G (nm_setting_bridge_port_get_type),
+		G (nm_setting_cdma_get_type),
+		G (nm_setting_compare_flags_get_type),
+		G (nm_setting_connection_autoconnect_slaves_get_type),
+		G (nm_setting_connection_get_type),
+		G (nm_setting_connection_lldp_get_type),
+		G (nm_setting_connection_llmnr_get_type),
+		G (nm_setting_connection_mdns_get_type),
+		G (nm_setting_dcb_flags_get_type),
+		G (nm_setting_dcb_get_type),
+		G (nm_setting_diff_result_get_type),
+		G (nm_setting_dummy_get_type),
+		G (nm_setting_ethtool_get_type),
+		G (nm_setting_generic_get_type),
+		G (nm_setting_get_type),
+		G (nm_setting_gsm_get_type),
+		G (nm_setting_infiniband_get_type),
+		G (nm_setting_ip4_config_get_type),
+		G (nm_setting_ip6_config_addr_gen_mode_get_type),
+		G (nm_setting_ip6_config_get_type),
+		G (nm_setting_ip6_config_privacy_get_type),
+		G (nm_setting_ip_config_get_type),
+		G (nm_setting_ip_tunnel_get_type),
+		G (nm_setting_mac_randomization_get_type),
+		G (nm_setting_macsec_get_type),
+		G (nm_setting_macsec_mode_get_type),
+		G (nm_setting_macsec_validation_get_type),
+		G (nm_setting_macvlan_get_type),
+		G (nm_setting_macvlan_mode_get_type),
+		G (nm_setting_match_get_type),
+		G (nm_setting_olpc_mesh_get_type),
+		G (nm_setting_ovs_bridge_get_type),
+		G (nm_setting_ovs_dpdk_get_type),
+		G (nm_setting_ovs_interface_get_type),
+		G (nm_setting_ovs_patch_get_type),
+		G (nm_setting_ovs_port_get_type),
+		G (nm_setting_ppp_get_type),
+		G (nm_setting_pppoe_get_type),
+		G (nm_setting_proxy_get_type),
+		G (nm_setting_proxy_method_get_type),
+		G (nm_settings_add_connection2_flags_get_type),
+		G (nm_settings_connection_flags_get_type),
+		G (nm_setting_secret_flags_get_type),
+		G (nm_setting_serial_get_type),
+		G (nm_setting_serial_parity_get_type),
+		G (nm_settings_error_get_type),
+		G (nm_setting_sriov_get_type),
+		G (nm_settings_update2_flags_get_type),
+		G (nm_setting_tc_config_get_type),
+		G (nm_setting_team_get_type),
+		G (nm_setting_team_port_get_type),
+		G (nm_setting_tun_get_type),
+		G (nm_setting_tun_mode_get_type),
+		G (nm_setting_user_get_type),
+		G (nm_setting_vlan_get_type),
+		G (nm_setting_vpn_get_type),
+		G (nm_setting_vxlan_get_type),
+		G (nm_setting_wifi_p2p_get_type),
+		G (nm_setting_wimax_get_type),
+		G (nm_setting_wired_get_type),
+		G (nm_setting_wired_wake_on_lan_get_type),
+		G (nm_setting_wireguard_get_type),
+		G (nm_setting_wireless_get_type),
+		G (nm_setting_wireless_powersave_get_type),
+		G (nm_setting_wireless_security_fils_get_type),
+		G (nm_setting_wireless_security_get_type),
+		G (nm_setting_wireless_security_pmf_get_type),
+		G (nm_setting_wireless_security_wps_method_get_type),
+		G (nm_setting_wireless_wake_on_wlan_get_type),
+		G (nm_setting_wpan_get_type),
+		G (nm_simple_connection_get_type),
+		G (nm_sriov_vf_get_type),
+		G (nm_sriov_vf_vlan_protocol_get_type),
+		G (nm_state_get_type),
+		G (nm_tc_action_get_type),
+		G (nm_tc_qdisc_get_type),
+		G (nm_tc_tfilter_get_type),
+		G (nm_team_link_watcher_arp_ping_flags_get_type),
+		G (nm_team_link_watcher_get_type),
+		G (nm_ternary_get_type),
+		G (nm_utils_security_type_get_type),
+		G (nm_vlan_flags_get_type),
+		G (nm_vlan_priority_map_get_type),
+		G (nm_vpn_connection_get_type),
+		G (nm_vpn_connection_state_get_type),
+		G (nm_vpn_connection_state_reason_get_type),
+		G (nm_vpn_editor_get_type),
+		G (nm_vpn_editor_plugin_capability_get_type),
+		G (nm_vpn_editor_plugin_get_type),
+		G (nm_vpn_plugin_error_get_type),
+		G (nm_vpn_plugin_failure_get_type),
+		G (nm_vpn_plugin_info_get_type),
+		G (nm_vpn_plugin_old_get_type),
+		G (nm_vpn_service_plugin_get_type),
+		G (nm_vpn_service_state_get_type),
+		G (nm_wep_key_type_get_type),
+		G (nm_wifi_p2p_peer_get_type),
+		G (nm_wimax_nsp_get_type),
+		G (nm_wimax_nsp_network_type_get_type),
+		G (nm_wireguard_peer_get_type),
+	};
+	guint i_type;
+
+	for (i_type = 0; i_type < G_N_ELEMENTS (get_type_fcns); i_type++) {
+		nm_auto_unref_gtypeclass GObjectClass *klass_unref = NULL;
+		GType gtype = (get_type_fcns[i_type]) ();
+		GObjectClass *klass;
+
+		g_assert (g_str_has_prefix (g_type_name (gtype), "NM"));
+
+		if (G_TYPE_IS_INTERFACE (gtype)) {
+			if (!NM_IN_STRSET (g_type_name (gtype), "NMConnection",
+			                                        "NMVpnEditor",
+			                                        "NMVpnEditorPlugin"))
+				g_error ("unexpected interface type %s", g_type_name (gtype));
+			continue;
+		}
+
+		if (g_type_is_a (gtype, G_TYPE_BOXED))
+			continue;
+
+		/* We only test parts of the types, and avoid initializing all the types.
+		 * That is so that other unit tests in this process randomly run with either
+		 * the class instance already initialized or not. */
+		if ((nmtst_get_rand_uint () % 5) == 0) {
+			klass = (klass_unref = g_type_class_ref (gtype));
+			g_assert (klass);
+		} else {
+			klass = g_type_class_peek (gtype);
+			if (!klass)
+				continue;
+		}
+
+		if (g_type_is_a (gtype, G_TYPE_ENUM))
+			continue;
+
+		if (g_type_is_a (gtype, G_TYPE_FLAGS))
+			continue;
+
+		g_assert (g_type_is_a (gtype, G_TYPE_OBJECT));
+		g_assert (G_IS_OBJECT_CLASS (klass));
+	}
+}
+
+/*****************************************************************************/
+
+static void
+test_nml_dbus_meta (void)
+{
+	const NMLDBusMetaIface *meta_iface;
+	const NMLDBusMetaProperty *meta_property;
+	guint prop_idx;
+	gsize i, j;
+	guint l, m;
+
+	for (i = 0; i < G_N_ELEMENTS (_nml_dbus_meta_ifaces); i++) {
+		const NMLDBusMetaIface *mif = _nml_dbus_meta_ifaces[i];
+		nm_auto_unref_gtypeclass GObjectClass *klass_unref = NULL;
+		GObjectClass *klass;
+		GType gtype;
+
+#define COMMON_PREFIX "org.freedesktop.NetworkManager"
+
+		g_assert (mif);
+		g_assert (mif->dbus_iface_name);
+		g_assert (   g_str_has_prefix (mif->dbus_iface_name, COMMON_PREFIX)
+		          && !g_str_has_suffix (mif->dbus_iface_name, ".")
+		          && NM_IN_SET (mif->dbus_iface_name[NM_STRLEN (COMMON_PREFIX)], '\0', '.'));
+		for (j = i + 1; j < G_N_ELEMENTS (_nml_dbus_meta_ifaces); j++)
+			g_assert (mif != _nml_dbus_meta_ifaces[j]);
+		if (i > 0) {
+			if (strcmp (_nml_dbus_meta_ifaces[i - 1]->dbus_iface_name, mif->dbus_iface_name) >= 0) {
+				g_error ("meta-ifaces are not properly sorted: [%zu] \"%s\" should be after [%zu] \"%s\"",
+				         i - 1, _nml_dbus_meta_ifaces[i - 1]->dbus_iface_name, i, mif->dbus_iface_name);
+			}
+		}
+
+		g_assert ((mif->n_dbus_properties > 0) == (!!mif->dbus_properties));
+
+		if (mif->interface_prio == NML_DBUS_META_INTERFACE_PRIO_NONE) {
+			g_assert (!mif->get_type_fcn);
+			g_assert (!mif->obj_properties);
+			g_assert (mif->n_obj_properties == 0);
+			g_assert (!mif->obj_properties_reverse_idx);
+			if (!NM_IN_STRSET (mif->dbus_iface_name, NM_DBUS_INTERFACE_AGENT_MANAGER,
+			                                         NM_DBUS_INTERFACE_DEVICE_STATISTICS,
+			                                         NM_DBUS_INTERFACE_DEVICE_VETH))
+				g_error ("D-Bus interface \"%s\" is unexpectedly empty", mif->dbus_iface_name);
+			if (mif->n_dbus_properties == 0)
+				continue;
+			gtype = G_TYPE_NONE;
+			klass = NULL;
+			goto check_dbus_properties;
+		}
+
+		g_assert (NM_IN_SET ((NMLDBusMetaInteracePrio) mif->interface_prio, NML_DBUS_META_INTERFACE_PRIO_NMCLIENT,
+		                                                                    NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE,
+		                                                                    NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_LOW,
+		                                                                    NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH));
+
+		g_assert (mif->get_type_fcn);
+		gtype = mif->get_type_fcn ();
+		g_assert (g_type_is_a (gtype, G_TYPE_OBJECT));
+
+		if (mif->interface_prio == NML_DBUS_META_INTERFACE_PRIO_NMCLIENT)
+			g_assert (gtype == NM_TYPE_CLIENT);
+		else
+			g_assert (g_type_is_a (gtype, NM_TYPE_OBJECT));
+
+		/* We only test parts of the types, and avoid initializing all the types.
+		 * That is so that other unit tests in this process randomly run with either
+		 * the class instance already initialized or not. */
+		if ((nmtst_get_rand_uint () % 5) == 0) {
+			klass = (klass_unref = g_type_class_ref (gtype));
+			g_assert (klass);
+		} else
+			klass = g_type_class_peek (gtype);
+
+		if (klass) {
+			if (NM_IS_OBJECT_CLASS (klass)) {
+				NMObjectClass *nm_object_class = NM_OBJECT_CLASS (klass);
+				const _NMObjectClassFieldInfo *p_prev;
+				const _NMObjectClassFieldInfo *p;
+
+				p_prev = NULL;
+				for (p = nm_object_class->property_o_info; p; p_prev = p, p = p->parent) {
+					g_assert (p->num > 0);
+					g_assert (NM_IS_OBJECT_CLASS (p->klass));
+					g_assert (g_type_is_a (gtype, G_TYPE_FROM_CLASS (p->klass)));
+					g_assert (p->klass->property_o_info == p);
+					if (p_prev) {
+						g_assert (g_type_is_a (G_TYPE_FROM_CLASS (p_prev->klass), G_TYPE_FROM_CLASS (p->klass)));
+						g_assert (p_prev->klass != p->klass);
+					}
+				}
+			} else
+				g_assert (NM_IS_CLIENT_CLASS (klass));
+		}
+
+		if (!mif->obj_properties) {
+			g_assert_cmpint (mif->n_obj_properties, ==, 0);
+			g_assert (!mif->obj_properties_reverse_idx);
+		} else {
+			g_assert (mif->obj_properties);
+			g_assert (mif->obj_properties[0] == 0);
+			g_assert_cmpint (mif->n_obj_properties, >, 1);
+			if (klass) {
+				for (l = 1; l < mif->n_obj_properties; l++) {
+					const GParamSpec *sp = mif->obj_properties[l];
+
+					g_assert (sp);
+					g_assert (sp->name);
+					g_assert (strlen (sp->name) > 0);
+				}
+			}
+
+			g_assert (mif->obj_properties_reverse_idx);
+			if (klass) {
+				g_assert (mif->obj_properties_reverse_idx[0] == 0xFFu);
+				for (l = 0; l < mif->n_obj_properties; l++) {
+					guint8 ridx = mif->obj_properties_reverse_idx[l];
+
+					if (ridx != 0xFFu) {
+						g_assert_cmpint (ridx, <=, mif->n_dbus_properties);
+						for (m = l + 1; m < mif->n_obj_properties; m++)
+							g_assert_cmpint (ridx, !=, mif->obj_properties_reverse_idx[m]);
+					}
+				}
+			}
+		}
+
+check_dbus_properties:
+		for (l = 0; l < mif->n_dbus_properties; l++) {
+			const NMLDBusMetaProperty *mpr = &mif->dbus_properties[l];
+			gs_free char *obj_property_name = NULL;
+			const struct {
+				const char *dbus_type;
+				GType default_gtype;
+			} *p_expected_type, *p_expected_type_2, expected_types[] = {
+				{ "b",         G_TYPE_BOOLEAN },
+				{ "q",         G_TYPE_UINT },
+				{ "y",         G_TYPE_UCHAR },
+				{ "i",         G_TYPE_INT },
+				{ "u",         G_TYPE_UINT },
+				{ "x",         G_TYPE_INT64 },
+				{ "t",         G_TYPE_UINT64 },
+				{ "s",         G_TYPE_STRING },
+				{ "o",         G_TYPE_STRING },
+				{ "ay",        G_TYPE_BYTES },
+				{ "as",        G_TYPE_STRV },
+				{ "ao",        G_TYPE_PTR_ARRAY },
+				{ "a{sv}",     G_TYPE_HASH_TABLE },
+				{ "aa{sv}",    G_TYPE_PTR_ARRAY },
+
+				{ "(uu)",      G_TYPE_NONE },
+				{ "aau",       G_TYPE_NONE },
+				{ "au",        G_TYPE_NONE },
+				{ "a(ayuay)",  G_TYPE_NONE },
+				{ "aay",       G_TYPE_NONE },
+				{ "a(ayuayu)", G_TYPE_NONE },
+
+				{ "u",         G_TYPE_FLAGS },
+				{ "u",         G_TYPE_ENUM },
+				{ "o",         NM_TYPE_OBJECT },
+			};
+			const GParamSpec *pspec = NULL;
+
+			g_assert (mpr->dbus_property_name);
+			g_assert (g_variant_type_string_is_valid ((const char *) mpr->dbus_type));
+			if (l > 0) {
+				if (strcmp (mif->dbus_properties[l - 1].dbus_property_name, mpr->dbus_property_name) >= 0) {
+					g_error ("meta-ifaces[%s] must have property #%u \"%s\" after #%u \"%s\"",
+					         mif->dbus_iface_name, l - 1, mif->dbus_properties[l - 1].dbus_property_name, l, mpr->dbus_property_name);
+				}
+			}
+
+			obj_property_name = nm_utils_wincaps_to_dash (mpr->dbus_property_name);
+			g_assert (obj_property_name);
+
+			for (p_expected_type = &expected_types[0]; TRUE; ) {
+				if (nm_streq ((const char *) mpr->dbus_type, p_expected_type->dbus_type))
+					break;
+				p_expected_type++;
+				if (p_expected_type >= &expected_types[G_N_ELEMENTS (expected_types)]) {
+					g_error ("D-Bus type \"%s\" is not implemented (in property %s.%s)",
+					         (const char *) mpr->dbus_type,
+					         mif->dbus_iface_name,
+					         mpr->dbus_property_name);
+				}
+			}
+
+			if (   klass
+			    && mpr->obj_properties_idx > 0) {
+				g_assert_cmpint (mpr->obj_properties_idx, <, mif->n_obj_properties);
+				if (!mpr->obj_property_no_reverse_idx)
+					g_assert_cmpint (mif->obj_properties_reverse_idx[mpr->obj_properties_idx], ==, l);
+				else {
+					g_assert_cmpint (mif->obj_properties_reverse_idx[mpr->obj_properties_idx], !=, l);
+					g_assert_cmpint (mif->obj_properties_reverse_idx[mpr->obj_properties_idx], !=, 0xFFu);
+				}
+				pspec = mif->obj_properties[mpr->obj_properties_idx];
+			}
+
+			if (mpr->use_notify_update_prop) {
+				g_assert (mpr->notify_update_prop);
+			} else {
+				if (klass)
+					g_assert (pspec);
+			}
+
+			if (pspec) {
+				const char *expected_property_name;
+
+				if (   mif == &_nml_dbus_meta_iface_nm_connection_active
+				    && nm_streq (pspec->name, NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT_PATH)) {
+					g_assert_cmpstr (obj_property_name, ==, "specific-object");
+					expected_property_name = NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT_PATH;
+				} else if (   mif == &_nml_dbus_meta_iface_nm_accesspoint
+				           && nm_streq (pspec->name, NM_ACCESS_POINT_BSSID)) {
+					g_assert_cmpstr (obj_property_name, ==, "hw-address");
+					expected_property_name = NM_ACCESS_POINT_BSSID;
+				} else if (   mif == &_nml_dbus_meta_iface_nm_device_wireguard
+				           && nm_streq (pspec->name, NM_DEVICE_WIREGUARD_FWMARK)) {
+					g_assert_cmpstr (obj_property_name, ==, "fw-mark");
+					expected_property_name = NM_DEVICE_WIREGUARD_FWMARK;
+				} else if (   NM_IN_SET (mif, &_nml_dbus_meta_iface_nm_ip4config,
+				                              &_nml_dbus_meta_iface_nm_ip6config)
+				           && nm_streq (pspec->name, NM_IP_CONFIG_ADDRESSES)) {
+					g_assert (NM_IN_STRSET (obj_property_name, "addresses", "address-data"));
+					expected_property_name = NM_IP_CONFIG_ADDRESSES;
+				} else if (   NM_IN_SET (mif, &_nml_dbus_meta_iface_nm_ip4config,
+				                              &_nml_dbus_meta_iface_nm_ip6config)
+				           && nm_streq (pspec->name, NM_IP_CONFIG_ROUTES)) {
+					g_assert (NM_IN_STRSET (obj_property_name, "routes", "route-data"));
+					expected_property_name = NM_IP_CONFIG_ROUTES;
+				} else if (   NM_IN_SET (mif, &_nml_dbus_meta_iface_nm_ip4config,
+				                              &_nml_dbus_meta_iface_nm_ip6config)
+				           && nm_streq (pspec->name, NM_IP_CONFIG_NAMESERVERS)) {
+					g_assert (NM_IN_STRSET (obj_property_name, "nameservers", "nameserver-data"));
+					expected_property_name = NM_IP_CONFIG_NAMESERVERS;
+				} else if (   mif == &_nml_dbus_meta_iface_nm_ip4config
+				           && nm_streq (pspec->name, NM_IP_CONFIG_WINS_SERVERS)) {
+					g_assert (NM_IN_STRSET (obj_property_name, "wins-servers", "wins-server-data"));
+					expected_property_name = NM_IP_CONFIG_WINS_SERVERS;
+				} else if (   mif == &_nml_dbus_meta_iface_nm_dnsmanager
+				           && nm_streq (pspec->name, NM_CLIENT_DNS_CONFIGURATION)) {
+					g_assert_cmpstr (obj_property_name, ==, "configuration");
+					expected_property_name = NM_CLIENT_DNS_CONFIGURATION;
+				} else if (   mif == &_nml_dbus_meta_iface_nm_dnsmanager
+				           && nm_streq (pspec->name, NM_CLIENT_DNS_MODE)) {
+					g_assert_cmpstr (obj_property_name, ==, "mode");
+					expected_property_name = NM_CLIENT_DNS_MODE;
+				} else if (   mif == &_nml_dbus_meta_iface_nm_dnsmanager
+				           && nm_streq (pspec->name, NM_CLIENT_DNS_RC_MANAGER)) {
+					g_assert_cmpstr (obj_property_name, ==, "rc-manager");
+					expected_property_name = NM_CLIENT_DNS_RC_MANAGER;
+				} else
+					expected_property_name = obj_property_name;
+
+				g_assert_cmpstr (expected_property_name, ==, pspec->name);
+
+				if (!mpr->use_notify_update_prop) {
+					for (p_expected_type_2 = &expected_types[0]; p_expected_type_2 < &expected_types[G_N_ELEMENTS (expected_types)]; p_expected_type_2++) {
+						if (!nm_streq ((const char *) mpr->dbus_type, p_expected_type_2->dbus_type))
+							continue;
+						if (   pspec->value_type == p_expected_type_2->default_gtype
+						    || (   p_expected_type_2->default_gtype == G_TYPE_ENUM
+						        && g_type_is_a (pspec->value_type, G_TYPE_ENUM))
+						    || (   p_expected_type_2->default_gtype == G_TYPE_FLAGS
+						        && g_type_is_a (pspec->value_type, G_TYPE_FLAGS))
+						    || (   p_expected_type_2->default_gtype == NM_TYPE_OBJECT
+						        && nm_streq ((const char *) mpr->dbus_type, "o")
+						        && g_type_is_a (pspec->value_type, NM_TYPE_OBJECT)))
+							break;
+					}
+					if (p_expected_type_2 >= &expected_types[G_N_ELEMENTS (expected_types)]) {
+						g_error ("D-Bus property \"%s.%s\" (type \"%s\") maps to property \"%s\", but that has an unexpected property type %s (expected %s)",
+						         mif->dbus_iface_name,
+						         mpr->dbus_property_name,
+						         (const char *) mpr->dbus_type,
+						         pspec->name,
+						         g_type_name (pspec->value_type),
+						         g_type_name (p_expected_type->default_gtype));
+					}
+				}
+
+				if (!nm_utils_g_param_spec_is_default (pspec)) {
+					/* We expect our properties to have a default value of zero/NULL.
+					 * Except those whitelisted here: */
+					if (   (   mif == &_nml_dbus_meta_iface_nm_accesspoint
+					        && nm_streq (pspec->name, NM_ACCESS_POINT_LAST_SEEN))
+					    || (   mif == &_nml_dbus_meta_iface_nm_device_vxlan
+					        && nm_streq (pspec->name, NM_DEVICE_VXLAN_LEARNING))
+					    || (   mif == &_nml_dbus_meta_iface_nm_device_wireless
+					        && nm_streq (pspec->name, NM_DEVICE_WIFI_LAST_SCAN))
+					    || (   mif == &_nml_dbus_meta_iface_nm_wifip2ppeer
+					        && nm_streq (pspec->name, NM_WIFI_P2P_PEER_LAST_SEEN))
+					    || (   mif == &_nml_dbus_meta_iface_nm_device_tun
+					        && NM_IN_STRSET (pspec->name, NM_DEVICE_TUN_GROUP,
+					                                      NM_DEVICE_TUN_OWNER))) {
+						/* pass */
+					} else {
+						g_error ("property %s.%s (%s.%s) does not have a default value of zero",
+						         mif->dbus_iface_name,
+						         mpr->dbus_property_name,
+						         g_type_name (gtype),
+						         pspec->name);
+					}
+				}
+			}
+		}
+
+		if (klass) {
+			for (l = 0; l < mif->n_obj_properties; l++) {
+				guint8 ridx = mif->obj_properties_reverse_idx[l];
+
+				if (ridx != 0xFFu)
+					g_assert_cmpint (mif->dbus_properties[ridx].obj_properties_idx, ==, l);
+			}
+		}
+
+		g_assert (mif == nml_dbus_meta_iface_get (mif->dbus_iface_name));
+	}
+
+	meta_iface = nml_dbus_meta_iface_get (NM_DBUS_INTERFACE);
+	g_assert (meta_iface);
+	g_assert (meta_iface == &_nml_dbus_meta_iface_nm);
+	g_assert_cmpstr (meta_iface->dbus_iface_name, ==, NM_DBUS_INTERFACE);
+
+	meta_property = nml_dbus_meta_property_get (meta_iface, "Version", &prop_idx);
+	g_assert (meta_property);
+	g_assert_cmpstr (meta_property->dbus_property_name, ==, "Version");
+	g_assert (&meta_iface->dbus_properties[prop_idx] == meta_property);
+}
+
+/*****************************************************************************/
+
+static void
+test_dbus_meta_types (void)
+{
+	struct list_data {
+		const char *dbus_iface_name;
+		GType gtype;
+		NMLDBusMetaInteracePrio interface_prio;
+	} list[] = {
+		{ NM_DBUS_INTERFACE,                      NM_TYPE_CLIENT,               NML_DBUS_META_INTERFACE_PRIO_NMCLIENT,         },
+		{ NM_DBUS_INTERFACE_ACCESS_POINT,         NM_TYPE_ACCESS_POINT,         NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_ACTIVE_CONNECTION,    NM_TYPE_ACTIVE_CONNECTION,    NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_LOW,  }, /* otherwise, NM_TYPE_VPN_CONNECTION. */
+		{ NM_DBUS_INTERFACE_DEVICE_6LOWPAN,       NM_TYPE_DEVICE_6LOWPAN,       NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_ADSL,          NM_TYPE_DEVICE_ADSL,          NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_BOND,          NM_TYPE_DEVICE_BOND,          NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_BRIDGE,        NM_TYPE_DEVICE_BRIDGE,        NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_BLUETOOTH,     NM_TYPE_DEVICE_BT,            NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_DUMMY,         NM_TYPE_DEVICE_DUMMY,         NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_WIRED,         NM_TYPE_DEVICE_ETHERNET,      NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_GENERIC,       NM_TYPE_DEVICE_GENERIC,       NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_INFINIBAND,    NM_TYPE_DEVICE_INFINIBAND,    NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL,     NM_TYPE_DEVICE_IP_TUNNEL,     NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_MACSEC,        NM_TYPE_DEVICE_MACSEC,        NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_MACVLAN,       NM_TYPE_DEVICE_MACVLAN,       NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_MODEM,         NM_TYPE_DEVICE_MODEM,         NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_OLPC_MESH,     NM_TYPE_DEVICE_OLPC_MESH,     NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE, NM_TYPE_DEVICE_OVS_INTERFACE, NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_OVS_PORT,      NM_TYPE_DEVICE_OVS_PORT,      NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE,    NM_TYPE_DEVICE_OVS_BRIDGE,    NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_WIFI_P2P,      NM_TYPE_DEVICE_WIFI_P2P,      NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_PPP,           NM_TYPE_DEVICE_PPP,           NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_TEAM,          NM_TYPE_DEVICE_TEAM,          NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_TUN,           NM_TYPE_DEVICE_TUN,           NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_VLAN,          NM_TYPE_DEVICE_VLAN,          NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_WPAN,          NM_TYPE_DEVICE_WPAN,          NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_VXLAN,         NM_TYPE_DEVICE_VXLAN,         NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_WIRELESS,      NM_TYPE_DEVICE_WIFI,          NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DEVICE_WIREGUARD,     NM_TYPE_DEVICE_WIREGUARD,     NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DHCP4_CONFIG,         NM_TYPE_DHCP4_CONFIG,         NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_DHCP6_CONFIG,         NM_TYPE_DHCP6_CONFIG,         NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_IP4_CONFIG,           NM_TYPE_IP4_CONFIG,           NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_IP6_CONFIG,           NM_TYPE_IP6_CONFIG,           NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_WIFI_P2P_PEER,        NM_TYPE_WIFI_P2P_PEER,        NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_SETTINGS_CONNECTION,  NM_TYPE_REMOTE_CONNECTION,    NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_SETTINGS,             NM_TYPE_CLIENT,               NML_DBUS_META_INTERFACE_PRIO_NMCLIENT,         },
+		{ NM_DBUS_INTERFACE_DNS_MANAGER,          NM_TYPE_CLIENT,               NML_DBUS_META_INTERFACE_PRIO_NMCLIENT,         },
+		{ NM_DBUS_INTERFACE_VPN_CONNECTION,       NM_TYPE_VPN_CONNECTION,       NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+		{ NM_DBUS_INTERFACE_CHECKPOINT,           NM_TYPE_CHECKPOINT,           NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_HIGH, },
+	};
+	guint i;
+
+	/* These iface<->gtype associations are copied from "nm-client.c"'s obj_nm_for_gdbus_object().
+	 * This is redundant to the meta-data, still check that the meta data matches. */
+	for (i = 0; i < G_N_ELEMENTS (list); i++) {
+		const struct list_data *d = &list[i];
+		const NMLDBusMetaIface *meta_iface;
+
+		meta_iface = nml_dbus_meta_iface_get (d->dbus_iface_name);
+		g_assert (meta_iface);
+		g_assert_cmpint (meta_iface->interface_prio, ==, d->interface_prio);
+		g_assert (meta_iface->get_type_fcn() == d->gtype);
+	}
+}
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
@@ -2439,6 +3075,9 @@ int main (int argc, char **argv)
 	g_test_add_func ("/libnm/general/fixup_product_string", test_fixup_product_string);
 	g_test_add_func ("/libnm/general/fixup_vendor_string", test_fixup_vendor_string);
 	g_test_add_func ("/libnm/general/nm_vpn_service_plugin_read_vpn_details", test_nm_vpn_service_plugin_read_vpn_details);
+	g_test_add_func ("/libnm/general/test_types", test_types);
+	g_test_add_func ("/libnm/general/test_nml_dbus_meta", test_nml_dbus_meta);
+	g_test_add_func ("/libnm/general/test_dbus_meta_types", test_dbus_meta_types);
 
 	return g_test_run ();
 }
diff --git a/libnm/tests/test-nm-client.c b/libnm/tests/test-nm-client.c
index 44cd5b3b..afcc4962 100644
--- a/libnm/tests/test-nm-client.c
+++ b/libnm/tests/test-nm-client.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 - 2014 Red Hat, Inc.
- *
+ * Copyright (C) 2010 - 2014 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -24,8 +10,9 @@
 
 #include "nm-test-libnm-utils.h"
 
-static GMainLoop *loop = NULL;
-static NMTstcServiceInfo *sinfo;
+static struct {
+	GMainLoop *loop;
+} gl = { };
 
 /*****************************************************************************/
 
@@ -61,7 +48,8 @@ devices_notify_cb (NMClient *c,
 static void
 test_device_added (void)
 {
-	NMClient *client;
+	nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL;
+	gs_unref_object NMClient *client = NULL;
 	const GPtrArray *devices;
 	NMDevice *device;
 	gboolean notified = FALSE;
@@ -71,8 +59,7 @@ test_device_added (void)
 	if (!nmtstc_service_available (sinfo))
 		return;
 
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client = nmtstc_client_new (TRUE);
 
 	devices = nm_client_get_devices (client);
 	g_assert (devices->len == 0);
@@ -103,9 +90,6 @@ test_device_added (void)
 	nm_device_delete (device, NULL, &error);
 	g_assert_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_SOFTWARE);
 	g_clear_error (&error);
-
-	g_object_unref (client);
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
 }
 
 /*****************************************************************************/
@@ -142,6 +126,8 @@ devices_sai_notify_cb (NMClient *c,
 	const GPtrArray *devices;
 	NMDevice *device;
 
+	g_assert_cmpstr (pspec->name, ==, "devices");
+
 	devices = nm_client_get_devices (c);
 	g_assert (devices);
 	g_assert_cmpint (devices->len, ==, 1);
@@ -157,18 +143,17 @@ devices_sai_notify_cb (NMClient *c,
 static void
 test_device_added_signal_after_init (void)
 {
-	NMClient *client;
+	nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL;
+	gs_unref_object NMClient *client = NULL;
 	const GPtrArray *devices;
 	NMDevice *device;
 	guint result = 0;
-	GError *error = NULL;
 
 	sinfo = nmtstc_service_init ();
 	if (!nmtstc_service_available (sinfo))
 		return;
 
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client = nmtstc_client_new (TRUE);
 
 	devices = nm_client_get_devices (client);
 	g_assert (devices->len == 0);
@@ -195,8 +180,8 @@ test_device_added_signal_after_init (void)
 	g_signal_handlers_disconnect_by_func (client, device_sai_added_cb, &result);
 	g_signal_handlers_disconnect_by_func (client, devices_sai_notify_cb, &result);
 
-	g_assert ((result & SIGNAL_MASK) == SIGNAL_FIRST);
-	g_assert ((result & NOTIFY_MASK) == NOTIFY_SECOND);
+	g_assert ((result & SIGNAL_MASK) == SIGNAL_SECOND);
+	g_assert ((result & NOTIFY_MASK) == NOTIFY_FIRST);
 
 	devices = nm_client_get_devices (client);
 	g_assert (devices);
@@ -205,9 +190,6 @@ test_device_added_signal_after_init (void)
 	device = g_ptr_array_index (devices, 0);
 	g_assert (device);
 	g_assert_cmpstr (nm_device_get_iface (device), ==, "eth0");
-
-	g_object_unref (client);
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
 }
 
 /*****************************************************************************/
@@ -307,19 +289,19 @@ wifi_ap_remove_notify_cb (NMDeviceWifi *w,
 static void
 test_wifi_ap_added_removed (void)
 {
-	NMClient *client;
+	nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL;
+	gs_unref_object NMClient *client = NULL;
 	NMDeviceWifi *wifi;
-	WifiApInfo info = { loop, FALSE, FALSE, 0, 0 };
+	WifiApInfo info = { gl.loop, FALSE, FALSE, 0, 0 };
 	GVariant *ret;
 	GError *error = NULL;
-	char *expected_path = NULL;
+	gs_free 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);
+	client = nmtstc_client_new (TRUE);
 
 	/*************************************/
 	/* Add the wifi device */
@@ -358,8 +340,8 @@ test_wifi_ap_added_removed (void)
 	info.quit_count++;
 
 	/* Wait for libnm to find the AP */
-	info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
-	g_main_loop_run (loop);
+	info.quit_id = g_timeout_add_seconds (5, loop_quit, gl.loop);
+	g_main_loop_run (gl.loop);
 
 	g_assert (info.signaled);
 	g_assert (info.notified);
@@ -397,8 +379,8 @@ test_wifi_ap_added_removed (void)
 	info.quit_count++;
 
 	/* Wait for libnm to find the AP */
-	info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
-	g_main_loop_run (loop);
+	info.quit_id = g_timeout_add_seconds (5, loop_quit, gl.loop);
+	g_main_loop_run (gl.loop);
 
 	g_assert (info.signaled);
 	g_assert (info.notified);
@@ -406,212 +388,6 @@ test_wifi_ap_added_removed (void)
 	g_signal_handlers_disconnect_by_func (wifi, wifi_ap_remove_notify_cb, &info);
 
 	g_free (info.ap_path);
-	g_free (expected_path);
-
-	g_object_unref (client);
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
-}
-
-/*****************************************************************************/
-
-static const char *expected_nsp_name = "Clear";
-
-typedef struct {
-	GMainLoop *loop;
-	gboolean found;
-	char *nsp_path;
-	gboolean signaled;
-	gboolean notified;
-	guint quit_id;
-	guint quit_count;
-} WimaxNspInfo;
-
-static void
-wimax_check_quit (WimaxNspInfo *info)
-{
-	info->quit_count--;
-	if (info->quit_count == 0) {
-		g_source_remove (info->quit_id);
-		info->quit_id = 0;
-		g_main_loop_quit (info->loop);
-	}
-}
-
-static void
-got_nsp_path (WimaxNspInfo *info, const char *path)
-{
-	if (info->nsp_path)
-		g_assert_cmpstr (info->nsp_path, ==, path);
-	else
-		info->nsp_path = g_strdup (path);
-}
-
-static void
-wimax_nsp_added_cb (NMDeviceWimax *w,
-                    NMWimaxNsp *nsp,
-                    WimaxNspInfo *info)
-{
-	g_assert (nsp);
-	g_assert_cmpstr (nm_wimax_nsp_get_name (nsp), ==, expected_nsp_name);
-	got_nsp_path (info, nm_object_get_path (NM_OBJECT (nsp)));
-
-	info->signaled = TRUE;
-	wimax_check_quit (info);
-}
-
-static void
-wimax_nsp_add_notify_cb (NMDeviceWimax *w,
-                         GParamSpec *pspec,
-                         WimaxNspInfo *info)
-{
-	const GPtrArray *nsps;
-	NMWimaxNsp *nsp;
-
-	nsps = nm_device_wimax_get_nsps (w);
-	g_assert (nsps);
-	g_assert_cmpint (nsps->len, ==, 1);
-
-	nsp = g_ptr_array_index (nsps, 0);
-	g_assert (nsp);
-	g_assert_cmpstr (nm_wimax_nsp_get_name (nsp), ==, expected_nsp_name);
-	got_nsp_path (info, nm_object_get_path (NM_OBJECT (nsp)));
-
-	info->notified = TRUE;
-	wimax_check_quit (info);
-}
-
-static void
-wimax_nsp_removed_cb (NMDeviceWimax *w,
-                      NMWimaxNsp *nsp,
-                      WimaxNspInfo *info)
-{
-	g_assert (nsp);
-	g_assert_cmpstr (info->nsp_path, ==, nm_object_get_path (NM_OBJECT (nsp)));
-
-	info->signaled = TRUE;
-	wimax_check_quit (info);
-}
-
-static void
-wimax_nsp_remove_notify_cb (NMDeviceWimax *w,
-                            GParamSpec *pspec,
-                            WimaxNspInfo *info)
-{
-	const GPtrArray *nsps;
-
-	nsps = nm_device_wimax_get_nsps (w);
-	g_assert (nsps->len == 0);
-
-	info->notified = TRUE;
-	wimax_check_quit (info);
-}
-
-static void
-test_wimax_nsp_added_removed (void)
-{
-	NMClient *client;
-	NMDeviceWimax *wimax;
-	WimaxNspInfo info = { loop, FALSE, FALSE, 0, 0 };
-	GVariant *ret;
-	GError *error = NULL;
-	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);
-
-	/*************************************/
-	/* Add the wimax device */
-	wimax = (NMDeviceWimax *) nmtstc_service_add_device (sinfo, client, "AddWimaxDevice", "wmx0");
-	g_assert (NM_IS_DEVICE_WIMAX (wimax));
-
-	/*************************************/
-	/* Add the wimax NSP */
-	info.signaled =  FALSE;
-	info.notified = FALSE;
-	info.quit_id = 0;
-
-	ret = g_dbus_proxy_call_sync (sinfo->proxy,
-	                              "AddWimaxNsp",
-	                              g_variant_new ("(ss)", "wmx0", expected_nsp_name),
-	                              G_DBUS_CALL_FLAGS_NO_AUTO_START,
-	                              3000,
-	                              NULL,
-	                              &error);
-	g_assert_no_error (error);
-	g_assert (ret);
-	g_assert_cmpstr (g_variant_get_type_string (ret), ==, "(o)");
-	g_variant_get (ret, "(o)", &expected_path);
-	g_variant_unref (ret);
-
-	g_signal_connect (wimax,
-	                  "nsp-added",
-	                  (GCallback) wimax_nsp_added_cb,
-	                  &info);
-	info.quit_count = 1;
-
-	g_signal_connect (wimax,
-	                  "notify::nsps",
-	                  (GCallback) wimax_nsp_add_notify_cb,
-	                  &info);
-	info.quit_count++;
-
-	/* Wait for libnm to find the AP */
-	info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
-	g_main_loop_run (loop);
-
-	g_assert (info.signaled);
-	g_assert (info.notified);
-	g_assert (info.nsp_path);
-	g_assert_cmpstr (info.nsp_path, ==, expected_path);
-	g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_added_cb, &info);
-	g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_add_notify_cb, &info);
-
-	/*************************************/
-	/* Remove the wimax NSP */
-	info.signaled =  FALSE;
-	info.notified = FALSE;
-	info.quit_id = 0;
-
-	ret = g_dbus_proxy_call_sync (sinfo->proxy,
-	                              "RemoveWimaxNsp",
-	                              g_variant_new ("(so)", "wmx0", expected_path),
-	                              G_DBUS_CALL_FLAGS_NO_AUTO_START,
-	                              3000,
-	                              NULL,
-	                              &error);
-	g_assert_no_error (error);
-	g_clear_pointer (&ret, g_variant_unref);
-
-	g_signal_connect (wimax,
-	                  "nsp-removed",
-	                  (GCallback) wimax_nsp_removed_cb,
-	                  &info);
-	info.quit_count = 1;
-
-	g_signal_connect (wimax,
-	                  "notify::nsps",
-	                  (GCallback) wimax_nsp_remove_notify_cb,
-	                  &info);
-	info.quit_count++;
-
-	/* Wait for libnm to find the AP */
-	info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
-	g_main_loop_run (loop);
-
-	g_assert (info.signaled);
-	g_assert (info.notified);
-	g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_removed_cb, &info);
-	g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_remove_notify_cb, &info);
-
-	g_free (info.nsp_path);
-	g_free (expected_path);
-
-	g_object_unref (client);
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
 }
 
 /*****************************************************************************/
@@ -671,25 +447,11 @@ da_devices_notify_cb (NMClient *c,
 }
 
 static void
-new_client_cb (GObject *object,
-               GAsyncResult *result,
-               gpointer user_data)
-{
-	NMClient **out_client = user_data;
-	GError *error = NULL;
-
-	*out_client = nm_client_new_finish (result, &error);
-	g_assert_no_error (error);
-	g_assert (*out_client != NULL);
-
-	g_main_loop_quit (loop);
-}
-
-static void
 test_devices_array (void)
 {
-	NMClient *client = NULL;
-	DaInfo info = { loop };
+	nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL;
+	gs_unref_object NMClient *client = NULL;
+	DaInfo info = { gl.loop };
 	NMDevice *wlan0, *eth0, *eth1, *device;
 	const GPtrArray *devices;
 	GError *error = NULL;
@@ -699,10 +461,7 @@ test_devices_array (void)
 	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);
-	g_main_loop_run (loop);
-	g_assert (client != NULL);
+	client = nmtstc_client_new (TRUE);
 
 	/*************************************/
 	/* Add some devices */
@@ -752,8 +511,8 @@ test_devices_array (void)
 	info.quit_count = 2;
 
 	/* Wait for libnm to notice the changes */
-	info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
-	g_main_loop_run (loop);
+	info.quit_id = g_timeout_add_seconds (5, loop_quit, gl.loop);
+	g_main_loop_run (gl.loop);
 
 	g_assert_cmpint (info.quit_count, ==, 0);
 	g_signal_handlers_disconnect_by_func (client, da_device_removed_cb, &info);
@@ -771,9 +530,6 @@ test_devices_array (void)
 	device = nm_client_get_device_by_iface (client, "eth1");
 	g_assert (NM_IS_DEVICE_ETHERNET (device));
 	g_assert (device == eth1);
-
-	g_object_unref (client);
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
 }
 
 static void
@@ -784,20 +540,20 @@ nm_running_changed (GObject *client,
 	int *running_changed = user_data;
 
 	(*running_changed)++;
-	g_main_loop_quit (loop);
+	g_main_loop_quit (gl.loop);
 }
 
 static void
 test_client_nm_running (void)
 {
+	nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL;
 	gs_unref_object NMClient *client1 = NULL;
 	gs_unref_object NMClient *client2 = NULL;
 	guint quit_id;
 	int running_changed = 0;
 	GError *error = NULL;
 
-	client1 = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client1 = nmtstc_client_new (TRUE);
 
 	g_assert (!nm_client_get_nm_running (client1));
 	g_assert_cmpstr (nm_client_get_version (client1), ==, NULL);
@@ -817,8 +573,7 @@ test_client_nm_running (void)
 	if (!nmtstc_service_available (sinfo))
 		return;
 
-	client2 = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client2 = nmtstc_client_new (FALSE);
 
 	/* client2 should know that NM is running, but the previously-created
 	 * client1 hasn't gotten the news yet.
@@ -828,8 +583,8 @@ test_client_nm_running (void)
 
 	g_signal_connect (client1, "notify::" NM_CLIENT_NM_RUNNING,
 	                  G_CALLBACK (nm_running_changed), &running_changed);
-	quit_id = g_timeout_add_seconds (5, loop_quit, loop);
-	g_main_loop_run (loop);
+	quit_id = g_timeout_add_seconds (5, loop_quit, gl.loop);
+	g_main_loop_run (gl.loop);
 	g_assert_cmpint (running_changed, ==, 1);
 	g_assert (nm_client_get_nm_running (client1));
 	g_source_remove (quit_id);
@@ -839,8 +594,8 @@ test_client_nm_running (void)
 
 	g_assert (nm_client_get_nm_running (client1));
 
-	quit_id = g_timeout_add_seconds (5, loop_quit, loop);
-	g_main_loop_run (loop);
+	quit_id = g_timeout_add_seconds (5, loop_quit, gl.loop);
+	g_main_loop_run (gl.loop);
 	g_assert_cmpint (running_changed, ==, 2);
 	g_assert (!nm_client_get_nm_running (client1));
 	g_source_remove (quit_id);
@@ -851,6 +606,9 @@ typedef struct {
 	NMActiveConnection *ac;
 
 	int remaining;
+
+	NMDevice *device;
+	gulong ac_signal_id;
 } TestACInfo;
 
 static void
@@ -877,13 +635,18 @@ assert_ac_and_device (NMClient *client)
 	device = devices->pdata[0];
 	if (device != ac_device && devices->len > 1)
 		device = devices->pdata[1];
-	device_ac = nm_device_get_active_connection (device);
-	g_assert (device_ac != NULL);
 
 	g_assert_cmpstr (nm_object_get_path (NM_OBJECT (device)), ==, nm_object_get_path (NM_OBJECT (ac_device)));
 	g_assert (device == ac_device);
-	g_assert_cmpstr (nm_object_get_path (NM_OBJECT (ac)), ==, nm_object_get_path (NM_OBJECT (device_ac)));
-	g_assert (ac == device_ac);
+
+	device_ac = nm_device_get_active_connection (device);
+	if (!device_ac) {
+		/* the stub NetworkManager service starts activating in an idle handler (delayed). That means, the
+		 * device may not yet refer to the active connection at this point. */
+	} else {
+		g_assert_cmpstr (nm_object_get_path (NM_OBJECT (ac)), ==, nm_object_get_path (NM_OBJECT (device_ac)));
+		g_assert (ac == device_ac);
+	}
 }
 
 static void
@@ -940,18 +703,17 @@ device_ac_changed_cb (GObject *device,
 static void
 test_active_connections (void)
 {
-	NMClient *client;
+	nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL;
+	gs_unref_object NMClient *client = NULL;
 	NMDevice *device;
 	NMConnection *conn;
-	TestACInfo info = { loop, NULL, 0 };
-	GError *error = NULL;
+	TestACInfo info = { gl.loop, NULL, 0 };
 
 	sinfo = nmtstc_service_init ();
 	if (!nmtstc_service_available (sinfo))
 		return;
 
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client = nmtstc_client_new (TRUE);
 
 	/* Tell the test service to add a new device */
 	device = nmtstc_service_add_device (sinfo, client, "AddWiredDevice", "eth0");
@@ -968,30 +730,25 @@ test_active_connections (void)
 
 	/* Two signals plus activate_cb */
 	info.remaining = 3;
-	g_main_loop_run (loop);
+	g_main_loop_run (gl.loop);
 	g_signal_handlers_disconnect_by_func (client, client_acs_changed_cb, &info);
 	g_signal_handlers_disconnect_by_func (device, device_ac_changed_cb, &info);
 
 	g_assert (info.ac != NULL);
 
 	g_object_unref (info.ac);
-	g_object_unref (client);
+	g_clear_object (&client);
 
 	/* Ensure that we can correctly resolve the recursive property link between the
 	 * AC and the Device in a newly-created client.
 	 */
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client = nmtstc_client_new (TRUE);
 	assert_ac_and_device (client);
-	g_object_unref (client);
+	g_clear_object (&client);
 
-	client = NULL;
-	nm_client_new_async (NULL, new_client_cb, &client);
-	g_main_loop_run (loop);
+	client = nmtstc_client_new (TRUE);
 	assert_ac_and_device (client);
-	g_object_unref (client);
-
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
+	g_clear_object (&client);
 }
 
 static void
@@ -1019,9 +776,12 @@ client_devices_changed_cb (GObject *client,
 	g_assert_cmpstr (nm_device_get_iface (device), ==, "eth0.1");
 
 	if (!nm_device_get_active_connection (device)) {
+		g_assert (info->ac_signal_id == 0);
 		info->remaining++;
-		g_signal_connect (device, "notify::" NM_DEVICE_ACTIVE_CONNECTION,
-		                  G_CALLBACK (device_ac_changed_cb), info);
+		info->device = device;
+		g_object_add_weak_pointer (G_OBJECT (device), (gpointer *) &info->device);
+		info->ac_signal_id = g_signal_connect (device, "notify::" NM_DEVICE_ACTIVE_CONNECTION,
+		                                       G_CALLBACK (device_ac_changed_cb), info);
 	}
 
 	info->remaining--;
@@ -1070,20 +830,19 @@ activate_cb (GObject *object,
 static void
 test_activate_virtual (void)
 {
-	NMClient *client;
+	nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL;
+	gs_unref_object NMClient *client = NULL;
 	NMConnection *conn;
 	NMSettingConnection *s_con;
 	NMSettingVlan *s_vlan;
-	TestACInfo info = { loop, NULL, 0 };
-	TestConnectionInfo conn_info = { loop, NULL };
-	GError *error = NULL;
+	TestACInfo info = { gl.loop, NULL, 0 };
+	TestConnectionInfo conn_info = { gl.loop, NULL };
 
 	sinfo = nmtstc_service_init ();
 	if (!nmtstc_service_available (sinfo))
 		return;
 
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client = nmtstc_client_new (TRUE);
 
 	nmtstc_service_add_device (sinfo, client, "AddWiredDevice", "eth0");
 
@@ -1099,7 +858,7 @@ test_activate_virtual (void)
 
 	nm_client_add_connection_async (client, conn, TRUE,
 	                                NULL, add_connection_cb, &conn_info);
-	g_main_loop_run (loop);
+	g_main_loop_run (gl.loop);
 	g_object_unref (conn);
 	conn = NM_CONNECTION (conn_info.remote);
 
@@ -1119,72 +878,27 @@ test_activate_virtual (void)
 	 */
 	info.remaining = 3;
 
-	g_main_loop_run (loop);
+	g_main_loop_run (gl.loop);
 	g_signal_handlers_disconnect_by_func (client, client_acs_changed_cb, &info);
 	g_signal_handlers_disconnect_by_func (client, client_devices_changed_cb, &info);
 
 	g_assert (info.ac != NULL);
+	g_clear_object (&info.ac);
 
-	g_object_unref (info.ac);
-	g_object_unref (client);
-
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
-}
-
-static void
-activate_failed_cb (GObject *object,
-                    GAsyncResult *result,
-                    gpointer user_data)
-{
-	NMClient *client = NM_CLIENT (object);
-	NMActiveConnection *ac;
-	GError *error = NULL;
-
-	ac = nm_client_activate_connection_finish (client, result, &error);
-	g_assert (ac == NULL);
-	g_assert_error (error, NM_CLIENT_ERROR, NM_CLIENT_ERROR_OBJECT_CREATION_FAILED);
-	g_clear_error (&error);
-
-	g_main_loop_quit (loop);
-}
-
-static void
-test_activate_failed (void)
-{
-	NMClient *client;
-	NMDevice *device;
-	NMConnection *conn;
-	GError *error = NULL;
-
-	sinfo = nmtstc_service_init ();
-	if (!nmtstc_service_available (sinfo))
-		return;
-
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
-
-	device = nmtstc_service_add_device (sinfo, client, "AddWiredDevice", "eth0");
-
-	/* Note that test-networkmanager-service.py checks for this exact name */
-	conn = nmtst_create_minimal_connection ("object-creation-failed-test", NULL,
-	                                        NM_SETTING_WIRED_SETTING_NAME, NULL);
-
-	nm_client_add_and_activate_connection_async (client, conn, device, NULL,
-	                                             NULL, activate_failed_cb, NULL);
-	g_main_loop_run (loop);
-
-	g_object_unref (conn);
-	g_object_unref (client);
-
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
+	if (info.device) {
+		g_object_remove_weak_pointer (G_OBJECT (info.device), (gpointer *) &info.device);
+		nm_clear_g_signal_handler (info.device, &info.ac_signal_id);
+	}
 }
 
 static void
 test_device_connection_compatibility (void)
 {
-	NMClient *client;
-	NMDevice *device1, *device2;
-	NMConnection *conn;
+	nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL;
+	gs_unref_object NMClient *client = NULL;
+	gs_unref_object NMConnection *conn = NULL;
+	NMDevice *device1;
+	NMDevice *device2;
 	NMSettingWired *s_wired;
 	GError *error = NULL;
 	const char *subchannels[] = { "0.0.8000", "0.0.8001", "0.0.8002", NULL };
@@ -1197,8 +911,7 @@ test_device_connection_compatibility (void)
 	if (!nmtstc_service_available (sinfo))
 		return;
 
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client = nmtstc_client_new (TRUE);
 
 	/* Create two devices */
 	device1 = nmtstc_service_add_wired_device (sinfo, client, "eth0", hw_addr1, subchannels);
@@ -1246,11 +959,6 @@ test_device_connection_compatibility (void)
 	nm_device_connection_compatible (device1, conn, &error);
 	g_assert_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION);
 	g_clear_error (&error);
-
-	g_object_unref (conn);
-	g_object_unref (client);
-
-	g_clear_pointer (&sinfo, nmtstc_service_cleanup);
 }
 
 /*****************************************************************************/
@@ -1283,7 +991,6 @@ test_connection_invalid (void)
 	NMSettingConnection *s_con;
 	gs_unref_object NMClient *client = NULL;
 	const GPtrArray *connections;
-	gs_free_error GError *error = NULL;
 	gs_free char *path0 = NULL;
 	gs_free char *path1 = NULL;
 	gs_free char *path2 = NULL;
@@ -1293,6 +1000,8 @@ test_connection_invalid (void)
 	gssize idx[4];
 	gs_unref_variant GVariant *variant = NULL;
 
+	g_assert (g_main_loop_get_context (gl.loop) == (g_main_context_get_thread_default () ?: g_main_context_default ()));
+
 	/**************************************************************************
 	 * Add three connections before starting libnm. One valid, two invalid.
 	 *************************************************************************/
@@ -1334,8 +1043,7 @@ test_connection_invalid (void)
 	                                       FALSE,
 	                                       &path2);
 
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	client = nmtstc_client_new (TRUE);
 
 	connections = nm_client_get_connections (client);
 	g_assert (connections);
@@ -1370,7 +1078,7 @@ test_connection_invalid (void)
 	                               FALSE,
 	                               &path3);
 
-	nmtst_main_loop_run (loop, 1000);
+	nmtst_main_loop_run (gl.loop, 1000);
 
 	connections = nm_client_get_connections (client);
 	g_assert (connections);
@@ -1406,7 +1114,7 @@ test_connection_invalid (void)
 	                                          variant,
 	                                          FALSE);
 
-	nmtst_main_loop_run (loop, 100);
+	nmtst_main_loop_run (gl.loop, 100);
 
 	connections = nm_client_get_connections (client);
 	g_assert (connections);
@@ -1444,7 +1152,7 @@ test_connection_invalid (void)
 	                                          variant,
 	                                          FALSE);
 
-	nmtst_main_loop_run (loop, 100);
+	nmtst_main_loop_run (gl.loop, 100);
 
 	connections = nm_client_get_connections (client);
 	g_assert (connections);
@@ -1479,7 +1187,7 @@ test_connection_invalid (void)
 	                                  connection,
 	                                  FALSE);
 
-	nmtst_main_loop_run (loop, 100);
+	nmtst_main_loop_run (gl.loop, 100);
 
 	connections = nm_client_get_connections (client);
 	g_assert (connections);
@@ -1521,7 +1229,7 @@ test_connection_invalid (void)
 	                                  connection,
 	                                  FALSE);
 
-	nmtst_main_loop_run (loop, 100);
+	nmtst_main_loop_run (gl.loop, 100);
 
 	connections = nm_client_get_connections (client);
 	g_assert (connections);
@@ -1563,7 +1271,7 @@ test_connection_invalid (void)
 	                                  connection,
 	                                  FALSE);
 
-	nmtst_main_loop_run (loop, 100);
+	nmtst_main_loop_run (gl.loop, 100);
 
 	connections = nm_client_get_connections (client);
 	g_assert (connections);
@@ -1602,17 +1310,15 @@ main (int argc, char **argv)
 
 	nmtst_init (&argc, &argv, TRUE);
 
-	loop = g_main_loop_new (NULL, FALSE);
+	gl.loop = g_main_loop_new (NULL, FALSE);
 
 	g_test_add_func ("/libnm/device-added", test_device_added);
 	g_test_add_func ("/libnm/device-added-signal-after-init", test_device_added_signal_after_init);
 	g_test_add_func ("/libnm/wifi-ap-added-removed", test_wifi_ap_added_removed);
-	g_test_add_func ("/libnm/wimax-nsp-added-removed", test_wimax_nsp_added_removed);
 	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-failed", test_activate_failed);
 	g_test_add_func ("/libnm/device-connection-compatibility", test_device_connection_compatibility);
 	g_test_add_func ("/libnm/connection/invalid", test_connection_invalid);
 
diff --git a/libnm/tests/test-remote-settings-client.c b/libnm/tests/test-remote-settings-client.c
index 45bf97c8..8483eca8 100644
--- a/libnm/tests/test-remote-settings-client.c
+++ b/libnm/tests/test-remote-settings-client.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 - 2011 Red Hat, Inc.
- *
+ * Copyright (C) 2010 - 2011 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -22,12 +8,16 @@
 #include <sys/types.h>
 #include <signal.h>
 
+#include "nm-glib-aux/nm-time-utils.h"
+
 #include "nm-test-libnm-utils.h"
 
-static NMTstcServiceInfo *sinfo;
-static NMClient *client = NULL;
-GDBusConnection *bus = NULL;
-NMRemoteConnection *remote = NULL;
+static struct {
+	NMTstcServiceInfo *sinfo;
+	NMClient *client;
+	GDBusConnection *bus;
+	NMRemoteConnection *remote;
+} gl = { };
 
 /*****************************************************************************/
 
@@ -39,17 +29,17 @@ add_cb (GObject *s,
 	gboolean *done = user_data;
 	GError *error = NULL;
 
-	remote = nm_client_add_connection_finish (client, result, &error);
+	gl.remote = nm_client_add_connection_finish (gl.client, result, &error);
 	g_assert_no_error (error);
 
 	*done = TRUE;
-	g_object_add_weak_pointer (G_OBJECT (remote), (void **) &remote);
+	g_object_add_weak_pointer (G_OBJECT (gl.remote), (void **) &gl.remote);
 
 	/* nm_client_add_connection_finish() adds a ref to @remote, but we
 	 * want the weak pointer to be cleared as soon as @client drops its own ref.
 	 * So drop ours.
 	 */
-	g_object_unref (remote);
+	g_object_unref (gl.remote);
 }
 
 #define TEST_CON_ID "blahblahblah"
@@ -58,32 +48,27 @@ static void
 test_add_connection (void)
 {
 	NMConnection *connection;
-	time_t start, now;
 	gboolean done = FALSE;
 
-	if (!nmtstc_service_available (sinfo))
+	if (!nmtstc_service_available (gl.sinfo))
 		return;
 
 	connection = nmtst_create_minimal_connection (TEST_CON_ID, NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
 
-	nm_client_add_connection_async (client,
+	nm_client_add_connection_async (gl.client,
 	                                connection,
 	                                TRUE,
 	                                NULL,
 	                                add_cb,
 	                                &done);
 
-	start = time (NULL);
-	do {
-		now = time (NULL);
-		g_main_context_iteration (NULL, FALSE);
-	} while ((done == FALSE) && (now - start < 5));
-	g_assert (done == TRUE);
-	g_assert (remote != NULL);
+	nmtst_main_context_iterate_until (NULL, 5000, done);
+
+	g_assert (gl.remote != NULL);
 
 	/* Make sure the connection is the same as what we added */
 	g_assert (nm_connection_compare (connection,
-	                                 NM_CONNECTION (remote),
+	                                 NM_CONNECTION (gl.remote),
 	                                 NM_SETTING_COMPARE_FLAG_EXACT) == TRUE);
 	g_object_unref (connection);
 }
@@ -113,7 +98,7 @@ visible_changed_cb (GObject *object, GParamSpec *pspec, gboolean *done)
 static void
 connection_removed_cb (NMClient *s, NMRemoteConnection *connection, gboolean *done)
 {
-	if (connection == remote)
+	if (connection == gl.remote)
 		*done = TRUE;
 }
 
@@ -130,7 +115,6 @@ invis_has_settings_cb (NMSetting *setting,
 static void
 test_make_invisible (void)
 {
-	time_t start, now;
 	const GPtrArray *conns;
 	int i;
 	GDBusProxy *proxy;
@@ -138,17 +122,17 @@ test_make_invisible (void)
 	gboolean has_settings = FALSE;
 	char *path;
 
-	if (!nmtstc_service_available (sinfo))
+	if (!nmtstc_service_available (gl.sinfo))
 		return;
 
-	g_assert (remote != NULL);
+	g_assert (gl.remote != NULL);
 
 	/* Listen for the remove event when the connection becomes invisible */
-	g_signal_connect (remote, "notify::" NM_REMOTE_CONNECTION_VISIBLE, G_CALLBACK (visible_changed_cb), &visible_changed);
-	g_signal_connect (client, "connection-removed", G_CALLBACK (connection_removed_cb), &connection_removed);
+	g_signal_connect (gl.remote, "notify::" NM_REMOTE_CONNECTION_VISIBLE, G_CALLBACK (visible_changed_cb), &visible_changed);
+	g_signal_connect (gl.client, "connection-removed", G_CALLBACK (connection_removed_cb), &connection_removed);
 
-	path = g_strdup (nm_connection_get_path (NM_CONNECTION (remote)));
-	proxy = g_dbus_proxy_new_sync (bus,
+	path = g_strdup (nm_connection_get_path (NM_CONNECTION (gl.remote)));
+	proxy = g_dbus_proxy_new_sync (gl.bus,
 	                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
 	                               NULL,
 	                               NM_DBUS_SERVICE,
@@ -167,29 +151,23 @@ test_make_invisible (void)
 	                   set_visible_cb, NULL);
 
 	/* Wait for the connection to be removed */
-	start = time (NULL);
-	do {
-		now = time (NULL);
-		g_main_context_iteration (NULL, FALSE);
-	} while ((!visible_changed || !connection_removed) && (now - start < 5));
-	g_assert (visible_changed == TRUE);
-	g_assert (connection_removed == TRUE);
+	nmtst_main_context_iterate_until (NULL, 5000, visible_changed && connection_removed);
 
-	g_signal_handlers_disconnect_by_func (remote, G_CALLBACK (visible_changed_cb), &visible_changed);
-	g_signal_handlers_disconnect_by_func (client, G_CALLBACK (connection_removed_cb), &connection_removed);
+	g_signal_handlers_disconnect_by_func (gl.remote, G_CALLBACK (visible_changed_cb), &visible_changed);
+	g_signal_handlers_disconnect_by_func (gl.client, G_CALLBACK (connection_removed_cb), &connection_removed);
 
 	/* Ensure NMClient no longer has the connection */
-	conns = nm_client_get_connections (client);
+	conns = nm_client_get_connections (gl.client);
 	for (i = 0; i < conns->len; i++) {
 		NMConnection *candidate = NM_CONNECTION (conns->pdata[i]);
 
-		g_assert ((gpointer) remote != (gpointer) candidate);
+		g_assert ((gpointer) gl.remote != (gpointer) candidate);
 		g_assert (strcmp (path, nm_connection_get_path (candidate)) != 0);
 	}
 
 	/* And ensure the invisible connection no longer has any settings */
-	g_assert (remote);
-	nm_connection_for_each_setting_value (NM_CONNECTION (remote),
+	g_assert (gl.remote);
+	nm_connection_for_each_setting_value (NM_CONNECTION (gl.remote),
 	                                      invis_has_settings_cb,
 	                                      &has_settings);
 	g_assert (has_settings == FALSE);
@@ -211,7 +189,6 @@ vis_new_connection_cb (NMClient *foo,
 static void
 test_make_visible (void)
 {
-	time_t start, now;
 	const GPtrArray *conns;
 	int i;
 	GDBusProxy *proxy;
@@ -219,17 +196,17 @@ test_make_visible (void)
 	char *path;
 	NMRemoteConnection *new = NULL;
 
-	if (!nmtstc_service_available (sinfo))
+	if (!nmtstc_service_available (gl.sinfo))
 		return;
 
-	g_assert (remote != NULL);
+	g_assert (gl.remote != NULL);
 
 	/* Wait for the new-connection signal when the connection is visible again */
-	g_signal_connect (client, NM_CLIENT_CONNECTION_ADDED,
+	g_signal_connect (gl.client, NM_CLIENT_CONNECTION_ADDED,
 	                  G_CALLBACK (vis_new_connection_cb), &new);
 
-	path = g_strdup (nm_connection_get_path (NM_CONNECTION (remote)));
-	proxy = g_dbus_proxy_new_sync (bus,
+	path = g_strdup (nm_connection_get_path (NM_CONNECTION (gl.remote)));
+	proxy = g_dbus_proxy_new_sync (gl.bus,
 	                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
 	                               NULL,
 	                               NM_DBUS_SERVICE,
@@ -248,24 +225,19 @@ test_make_visible (void)
 	                   set_visible_cb, NULL);
 
 	/* Wait for the settings service to announce the connection again */
-	start = time (NULL);
-	do {
-		now = time (NULL);
-		g_main_context_iteration (NULL, FALSE);
-	} while ((new == NULL) && (now - start < 5));
+	nmtst_main_context_iterate_until (NULL, 5000, new);
 
 	/* Ensure the new connection is the same as the one we made visible again */
-	g_assert (new);
-	g_assert (new == remote);
+	g_assert (new == gl.remote);
 
-	g_signal_handlers_disconnect_by_func (client, G_CALLBACK (vis_new_connection_cb), &new);
+	g_signal_handlers_disconnect_by_func (gl.client, G_CALLBACK (vis_new_connection_cb), &new);
 
 	/* Ensure NMClient has the connection */
-	conns = nm_client_get_connections (client);
+	conns = nm_client_get_connections (gl.client);
 	for (i = 0; i < conns->len; i++) {
 		NMConnection *candidate = NM_CONNECTION (conns->pdata[i]);
 
-		if ((gpointer) remote == (gpointer) candidate) {
+		if ((gpointer) gl.remote == (gpointer) candidate) {
 			g_assert_cmpstr (path, ==, nm_connection_get_path (candidate));
 			g_assert_cmpstr (TEST_CON_ID, ==, nm_connection_get_id (candidate));
 			found = TRUE;
@@ -296,7 +268,7 @@ deleted_cb (GObject *proxy,
 static void
 removed_cb (NMClient *s, NMRemoteConnection *connection, gboolean *done)
 {
-	if (connection == remote)
+	if (connection == gl.remote)
 		*done = TRUE;
 }
 
@@ -304,27 +276,26 @@ static void
 test_remove_connection (void)
 {
 	NMRemoteConnection *connection;
-	time_t start, now;
 	const GPtrArray *conns;
 	int i;
 	GDBusProxy *proxy;
 	gboolean done = FALSE;
 	char *path;
 
-	if (!nmtstc_service_available (sinfo))
+	if (!nmtstc_service_available (gl.sinfo))
 		return;
 
 	/* Find a connection to delete */
-	conns = nm_client_get_connections (client);
+	conns = nm_client_get_connections (gl.client);
 	g_assert_cmpint (conns->len, >, 0);
 
 	connection = NM_REMOTE_CONNECTION (conns->pdata[0]);
 	g_assert (connection);
-	g_assert (remote == connection);
+	g_assert (gl.remote == connection);
 	path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection)));
-	g_signal_connect (client, "connection-removed", G_CALLBACK (removed_cb), &done);
+	g_signal_connect (gl.client, "connection-removed", G_CALLBACK (removed_cb), &done);
 
-	proxy = g_dbus_proxy_new_sync (bus,
+	proxy = g_dbus_proxy_new_sync (gl.bus,
 	                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
 	                               NULL,
 	                               NM_DBUS_SERVICE,
@@ -342,18 +313,10 @@ test_remove_connection (void)
 	                   NULL,
 	                   deleted_cb, NULL);
 
-	start = time (NULL);
-	do {
-		now = time (NULL);
-		g_main_context_iteration (NULL, FALSE);
-		if (done && !remote)
-			break;
-	} while (now - start < 5);
-	g_assert (done == TRUE);
-	g_assert (!remote);
+	nmtst_main_context_iterate_until (NULL, 5000, done && !gl.remote);
 
 	/* Ensure NMClient no longer has the connection */
-	conns = nm_client_get_connections (client);
+	conns = nm_client_get_connections (gl.client);
 	for (i = 0; i < conns->len; i++) {
 		NMConnection *candidate = NM_CONNECTION (conns->pdata[i]);
 
@@ -378,7 +341,7 @@ add_remove_cb (GObject *s,
 	gboolean *done = user_data;
 	gs_free_error GError *error = NULL;
 
-	connection = nm_client_add_connection_finish (client, result, &error);
+	connection = nm_client_add_connection_finish (gl.client, result, &error);
 	g_assert_error (error, NM_CLIENT_ERROR, NM_CLIENT_ERROR_OBJECT_CREATION_FAILED);
 	g_assert (connection == NULL);
 
@@ -388,43 +351,34 @@ add_remove_cb (GObject *s,
 static void
 test_add_remove_connection (void)
 {
-	GVariant *ret;
+	gs_unref_variant GVariant *ret = NULL;
 	GError *error = NULL;
-	NMConnection *connection;
-	time_t start, now;
+	gs_unref_object NMConnection *connection = NULL;
 	gboolean done = FALSE;
 
-	if (!nmtstc_service_available (sinfo))
+	if (!nmtstc_service_available (gl.sinfo))
 		return;
 
 	/* This will cause the test server to immediately delete the connection
 	 * after creating it.
 	 */
-	ret = g_dbus_proxy_call_sync (sinfo->proxy,
+	ret = g_dbus_proxy_call_sync (gl.sinfo->proxy,
 	                              "AutoRemoveNextConnection",
 	                              NULL,
 	                              G_DBUS_CALL_FLAGS_NONE, -1,
 	                              NULL,
 	                              &error);
-	g_assert_no_error (error);
-	g_variant_unref (ret);
+	nmtst_assert_success (ret, error);
 
 	connection = nmtst_create_minimal_connection (TEST_ADD_REMOVE_ID, NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
-	nm_client_add_connection_async (client,
+	nm_client_add_connection_async (gl.client,
 	                                connection,
 	                                TRUE,
 	                                NULL,
 	                                add_remove_cb,
 	                                &done);
 
-	start = time (NULL);
-	do {
-		now = time (NULL);
-		g_main_context_iteration (NULL, FALSE);
-	} while ((done == FALSE) && (now - start < 5));
-	g_assert (done == TRUE);
-
-	g_object_unref (connection);
+	nmtst_main_context_iterate_until (NULL, 5000, done);
 }
 
 /*****************************************************************************/
@@ -437,7 +391,7 @@ add_bad_cb (GObject *s,
 	gboolean *done = user_data;
 	gs_free_error GError *error = NULL;
 
-	remote = nm_client_add_connection_finish (client, result, &error);
+	gl.remote = nm_client_add_connection_finish (gl.client, result, &error);
 	g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
 
 	*done = TRUE;
@@ -446,31 +400,25 @@ add_bad_cb (GObject *s,
 static void
 test_add_bad_connection (void)
 {
-	NMConnection *connection;
-	time_t start, now;
+	gs_unref_object NMConnection *connection = NULL;
 	gboolean done = FALSE;
 
-	if (!nmtstc_service_available (sinfo))
+	if (!nmtstc_service_available (gl.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);
 
-	nm_client_add_connection_async (client,
+	nm_client_add_connection_async (gl.client,
 	                                connection,
 	                                TRUE,
 	                                NULL,
 	                                add_bad_cb,
 	                                &done);
-	g_object_unref (connection);
+	g_clear_object (&connection);
 
-	start = time (NULL);
-	do {
-		now = time (NULL);
-		g_main_context_iteration (NULL, FALSE);
-	} while ((done == FALSE) && (now - start < 5));
-	g_assert (done == TRUE);
-	g_assert (remote == NULL);
+	nmtst_main_context_iterate_until (NULL, 5000, done);
+	g_assert (gl.remote == NULL);
 }
 
 /*****************************************************************************/
@@ -483,7 +431,7 @@ save_hostname_cb (GObject *s,
 	gboolean *done = user_data;
 	gs_free_error GError *error = NULL;
 
-	nm_client_save_hostname_finish (client, result, &error);
+	nm_client_save_hostname_finish (gl.client, result, &error);
 	g_assert_no_error (error);
 
 	*done = TRUE;
@@ -492,27 +440,30 @@ save_hostname_cb (GObject *s,
 static void
 test_save_hostname (void)
 {
-	time_t start, now;
+	gint64 until_ts;
 	gboolean done = FALSE;
 	GError *error = NULL;
 
-	if (!nmtstc_service_available (sinfo))
+	if (!nmtstc_service_available (gl.sinfo))
 		return;
 
 	/* test-networkmanager-service.py requires the hostname to contain a '.' */
-	nm_client_save_hostname (client, "foo", NULL, &error);
+	nm_client_save_hostname (gl.client, "foo", NULL, &error);
 	g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_HOSTNAME);
 	g_clear_error (&error);
 
-	nm_client_save_hostname_async (client, "example.com", NULL, save_hostname_cb, &done);
+	nm_client_save_hostname_async (gl.client, "example.com", NULL, save_hostname_cb, &done);
 
-	start = time (NULL);
-	do {
-		now = time (NULL);
+	until_ts = nm_utils_get_monotonic_timestamp_ms () + 5000;
+	while (TRUE) {
 		g_main_context_iteration (NULL, FALSE);
-	} while ((done == FALSE) && (now - start < 5));
-	g_assert (done == TRUE);
-	g_assert (remote == NULL);
+		if (done)
+			break;
+		if (nm_utils_get_monotonic_timestamp_ms () >= until_ts)
+			g_assert_not_reached ();
+	}
+
+	g_assert (gl.remote == NULL);
 }
 
 /*****************************************************************************/
@@ -529,14 +480,12 @@ main (int argc, char **argv)
 
 	nmtst_init (&argc, &argv, TRUE);
 
-	bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
-	g_assert_no_error (error);
+	gl.bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+	nmtst_assert_success (gl.bus, error);
 
-	sinfo = nmtstc_service_init ();
+	gl.sinfo = nmtstc_service_init ();
 
-	client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
-	g_assert (client != NULL);
+	gl.client = nmtstc_client_new (TRUE);
 
 	/* FIXME: these tests assume that they get run in order, but g_test_run()
 	 * does not actually guarantee that!
@@ -551,9 +500,9 @@ main (int argc, char **argv)
 
 	ret = g_test_run ();
 
-	nmtstc_service_cleanup (sinfo);
-	g_object_unref (client);
-	g_object_unref (bus);
+	nm_clear_pointer (&gl.sinfo, nmtstc_service_cleanup);
+	g_clear_object (&gl.client);
+	g_clear_object (&gl.bus);
 
 	return ret;
 }
diff --git a/libnm/tests/test-secret-agent.c b/libnm/tests/test-secret-agent.c
index cc060440..fef6071d 100644
--- a/libnm/tests/test-secret-agent.c
+++ b/libnm/tests/test-secret-agent.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 - 2014 Red Hat, Inc.
- *
+ * Copyright (C) 2010 - 2014 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -248,8 +234,9 @@ test_setup (TestSecretAgentData *sadata, gconstpointer test_data)
 	if (!sadata->sinfo)
 		return;
 
-	sadata->client = nm_client_new (NULL, &error);
-	g_assert_no_error (error);
+	g_assert (g_main_context_get_thread_default () == NULL);
+
+	sadata->client = nmtstc_client_new (TRUE);
 
 	sadata->loop = g_main_loop_new (NULL, FALSE);
 	sadata->timeout_id = g_timeout_add_seconds (5, timeout_assert, NULL);