summary refs log tree commit diff
path: root/libnm-core/tests
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core/tests')
-rw-r--r--libnm-core/tests/meson.build14
-rw-r--r--libnm-core/tests/test-crypto.c1
-rw-r--r--libnm-core/tests/test-general.c405
-rw-r--r--libnm-core/tests/test-keyfile.c4
-rw-r--r--libnm-core/tests/test-secrets.c2
-rw-r--r--libnm-core/tests/test-setting.c692
-rw-r--r--libnm-core/tests/test-settings-defaults.c2
7 files changed, 1074 insertions, 46 deletions
diff --git a/libnm-core/tests/meson.build b/libnm-core/tests/meson.build
index 627e6b53..7d772c80 100644
--- a/libnm-core/tests/meson.build
+++ b/libnm-core/tests/meson.build
@@ -14,7 +14,7 @@ enum = gnome.mkenums(
   sources: 'test-general-enums.h',
   identifier_prefix: nm_id_prefix,
   c_template: enum_types + '.c.template',
-  h_template: enum_types + '.h.template'
+  h_template: enum_types + '.h.template',
 )
 
 test_units = [
@@ -24,7 +24,7 @@ test_units = [
   'test-keyfile',
   'test-secrets',
   'test-setting',
-  'test-settings-defaults'
+  'test-settings-defaults',
 ]
 
 test_cert_dir = join_paths(meson.current_source_dir(), 'certs')
@@ -33,16 +33,20 @@ foreach test_unit: test_units
   exe = executable(
     'libnm-core-' + test_unit,
     [test_unit + '.c'] + enum,
-    dependencies: nm_core_dep,
+    dependencies: [
+      nm_core_dep,
+      libnm_systemd_shared_no_logging_dep,
+    ],
     c_args: [
         '-DNETWORKMANAGER_COMPILATION_TEST',
         '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE',
       ],
-    link_with: libnm_core
+    link_with: libnm_core,
   )
   test(
     'libnm-core/' + test_unit,
     test_script,
-    args: test_args + [exe.full_path()]
+    args: test_args + [exe.full_path()],
+    timeout: default_test_timeout,
   )
 endforeach
diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c
index 6b63e6fd..4ac9679b 100644
--- a/libnm-core/tests/test-crypto.c
+++ b/libnm-core/tests/test-crypto.c
@@ -26,7 +26,6 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
 
 #include "nm-crypto-impl.h"
 #include "nm-utils.h"
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 67b80098..7d97296a 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -23,8 +23,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-utils/c-list-util.h"
 #include "nm-utils/nm-enum-utils.h"
 
@@ -755,10 +753,12 @@ vpn_check_empty_func (const char *key, const char *value, gpointer user_data)
 static void
 test_setting_vpn_items (void)
 {
-	gs_unref_object NMSettingVpn *s_vpn = NULL;
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingVpn *s_vpn;
 
-	s_vpn = (NMSettingVpn *) nm_setting_vpn_new ();
-	g_assert (s_vpn);
+	connection = nmtst_create_minimal_connection ("vpn-items", NULL, NM_SETTING_VPN_SETTING_NAME, NULL);
+
+	s_vpn = nm_connection_get_setting_vpn (connection);
 
 	nm_setting_vpn_add_data_item (s_vpn, "foobar1", "blahblah1");
 	nm_setting_vpn_add_data_item (s_vpn, "foobar2", "blahblah2");
@@ -772,7 +772,14 @@ test_setting_vpn_items (void)
 	nm_setting_vpn_remove_data_item (s_vpn, "foobar3");
 	nm_setting_vpn_remove_data_item (s_vpn, "foobar4");
 
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
 	nm_setting_vpn_add_secret (s_vpn, "foobar1", "blahblah1");
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
 	nm_setting_vpn_add_secret (s_vpn, "foobar2", "blahblah2");
 	nm_setting_vpn_add_secret (s_vpn, "foobar3", "blahblah3");
 	nm_setting_vpn_add_secret (s_vpn, "foobar4", "blahblah4");
@@ -782,8 +789,25 @@ test_setting_vpn_items (void)
 	nm_setting_vpn_remove_secret (s_vpn, "foobar1");
 	nm_setting_vpn_remove_secret (s_vpn, "foobar2");
 	nm_setting_vpn_remove_secret (s_vpn, "foobar3");
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	nm_setting_vpn_add_data_item (s_vpn, "foobar4-flags", "blahblah4");
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	nm_setting_vpn_add_data_item (s_vpn, "foobar4-flags", "2");
+
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
 	nm_setting_vpn_remove_secret (s_vpn, "foobar4");
 
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	nm_setting_vpn_remove_data_item (s_vpn, "foobar4-flags");
+
 	/* Try to add some blank values and make sure they are rejected */
 	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (key != NULL));
 	nm_setting_vpn_add_data_item (s_vpn, NULL, NULL);
@@ -1351,8 +1375,6 @@ test_setting_gsm_apn_bad_chars (void)
 	s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
 	g_assert (s_gsm);
 
-	g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
-
 	/* Make sure a valid APN works */
 	g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar123.-baz", NULL);
 	g_assert (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL));
@@ -1382,8 +1404,6 @@ test_setting_gsm_apn_underscore (void)
 	s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
 	g_assert (s_gsm);
 
-	g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
-
 	/* 65-character long */
 	g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar_baz", NULL);
 	nmtst_assert_setting_verifies (NM_SETTING (s_gsm));
@@ -1636,6 +1656,25 @@ test_connection_to_dbus_setting_name (void)
 	s_wsec = make_test_wsec_setting ("connection-to-dbus-setting-name");
 	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
 
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	              NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	              NULL);
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, NM_SETTING_SECRET_FLAG_NONE,
+	              NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NONE,
+	              NULL);
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
 	dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
 
 	/* Make sure the keys of the first level dict are setting names, not
@@ -2946,7 +2985,6 @@ test_connection_good_base_types (void)
 
 	setting = nm_setting_gsm_new ();
 	g_object_set (setting,
-	              NM_SETTING_GSM_NUMBER, "*99#",
 	              NM_SETTING_GSM_APN, "metered.billing.sucks",
 	              NULL);
 	nm_connection_add_setting (connection, setting);
@@ -3267,11 +3305,110 @@ test_data_compare_secrets_new (NMSettingSecretFlags secret_flags,
 }
 
 static void
+_test_compare_secrets_check_diff (NMSetting *a,
+                                  NMSetting *b,
+                                  NMSettingCompareFlags flags,
+                                  gboolean exp_same_psk,
+                                  gboolean exp_same_psk_flags)
+{
+	gs_unref_hashtable GHashTable *h = NULL;
+	NMSettingDiffResult _RESULT_IN_A = NM_SETTING_DIFF_RESULT_IN_A;
+	NMSettingDiffResult _RESULT_IN_B = NM_SETTING_DIFF_RESULT_IN_B;
+	gboolean invert_results;
+	gboolean diff_result;
+	NMSettingSecretFlags a_psk_flags = nm_setting_wireless_security_get_psk_flags (NM_SETTING_WIRELESS_SECURITY (a));
+	NMSettingSecretFlags b_psk_flags = nm_setting_wireless_security_get_psk_flags (NM_SETTING_WIRELESS_SECURITY (b));
+	const char *a_psk = nm_setting_wireless_security_get_psk (NM_SETTING_WIRELESS_SECURITY (a));
+	const char *b_psk = nm_setting_wireless_security_get_psk (NM_SETTING_WIRELESS_SECURITY (b));
+
+	g_assert (NM_IS_SETTING_WIRELESS_SECURITY (a));
+	g_assert (NM_IS_SETTING_WIRELESS_SECURITY (b));
+
+	invert_results = nmtst_get_rand_bool ();
+	if (invert_results) {
+		_RESULT_IN_A = NM_SETTING_DIFF_RESULT_IN_B;
+		_RESULT_IN_B = NM_SETTING_DIFF_RESULT_IN_A;
+	}
+
+	diff_result = nm_setting_diff (a, b, flags, invert_results, &h);
+
+	g_assert (exp_same_psk_flags == (a_psk_flags == b_psk_flags));
+
+	if (nm_streq0 (a_psk, b_psk))
+		g_assert (exp_same_psk);
+	else {
+		if (flags == NM_SETTING_COMPARE_FLAG_EXACT)
+			g_assert (!exp_same_psk);
+		else if (flags == NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS) {
+			if (   !NM_FLAGS_HAS (a_psk_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+			    && !NM_FLAGS_HAS (b_psk_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED))
+				g_assert (!exp_same_psk);
+			else if (   !NM_FLAGS_HAS (a_psk_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+			         && NM_FLAGS_HAS (b_psk_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED))
+				g_assert (!exp_same_psk);
+			else
+				g_assert (exp_same_psk);
+		} else if (flags == NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS) {
+			if (   !NM_FLAGS_HAS (a_psk_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)
+			    && !NM_FLAGS_HAS (b_psk_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED))
+				g_assert (!exp_same_psk);
+			else if (   !NM_FLAGS_HAS (a_psk_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)
+			         && NM_FLAGS_HAS (b_psk_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED))
+				g_assert (!exp_same_psk);
+			else
+				g_assert (exp_same_psk);
+		} else if (flags == NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
+			g_assert (exp_same_psk);
+		else
+			g_assert_not_reached ();
+	}
+
+	g_assert (diff_result == (exp_same_psk && exp_same_psk_flags));
+	g_assert (diff_result == (!h));
+
+	if (!diff_result) {
+		if (flags == NM_SETTING_COMPARE_FLAG_EXACT)
+			g_assert (!exp_same_psk);
+		else if (   NM_IN_SET (flags, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS,
+		                              NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+		         && (a_psk_flags != b_psk_flags)
+		         && nm_setting_wireless_security_get_psk_flags (NM_SETTING_WIRELESS_SECURITY (a)) == NM_SETTING_SECRET_FLAG_NONE)
+			g_assert (!exp_same_psk);
+		else
+			g_assert (exp_same_psk);
+
+		g_assert ((!exp_same_psk) == g_hash_table_contains (h, NM_SETTING_WIRELESS_SECURITY_PSK));
+		if (!exp_same_psk) {
+			if (nm_setting_wireless_security_get_psk (NM_SETTING_WIRELESS_SECURITY (a)))
+				g_assert_cmpint (GPOINTER_TO_UINT (g_hash_table_lookup (h, NM_SETTING_WIRELESS_SECURITY_PSK)), ==, _RESULT_IN_A);
+			else
+				g_assert_cmpint (GPOINTER_TO_UINT (g_hash_table_lookup (h, NM_SETTING_WIRELESS_SECURITY_PSK)), ==, _RESULT_IN_B);
+		}
+
+		g_assert ((!exp_same_psk_flags) == g_hash_table_contains (h, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS));
+		if (!exp_same_psk_flags) {
+			if (nm_setting_wireless_security_get_psk_flags (NM_SETTING_WIRELESS_SECURITY (a)) != NM_SETTING_SECRET_FLAG_NONE)
+				g_assert_cmpint (GPOINTER_TO_UINT (g_hash_table_lookup (h, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS)), ==, _RESULT_IN_A);
+			else
+				g_assert_cmpint (GPOINTER_TO_UINT (g_hash_table_lookup (h, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS)), ==, _RESULT_IN_B);
+		}
+
+		g_assert_cmpint (g_hash_table_size (h), ==, (!exp_same_psk) + (!exp_same_psk_flags));
+	}
+
+	g_assert (diff_result == nm_setting_compare (a, b, flags));
+	g_assert (diff_result == nm_setting_compare (b, a, flags));
+
+}
+
+static void
 test_setting_compare_secrets (gconstpointer test_data)
 {
 	const TestDataCompareSecrets *data = test_data;
-	gs_unref_object NMSetting *old = NULL, *new = NULL;
-	gboolean success;
+	gs_unref_object NMConnection *conn_old = NULL;
+	gs_unref_object NMConnection *conn_new = NULL;
+	gs_unref_object NMSetting *old = NULL;
+	gs_unref_object NMSetting *new = NULL;
 
 	/* Make sure that a connection with transient/unsaved secrets compares
 	 * successfully to the same connection without those secrets.
@@ -3284,17 +3421,45 @@ test_setting_compare_secrets (gconstpointer test_data)
 	              NULL);
 	nm_setting_set_secret_flags (old, NM_SETTING_WIRELESS_SECURITY_PSK, data->secret_flags, NULL);
 
-	/* Clear the PSK from the duplicated setting */
 	new = nm_setting_duplicate (old);
-	if (data->remove_secret) {
+	if (data->remove_secret)
 		g_object_set (new, NM_SETTING_WIRELESS_SECURITY_PSK, NULL, NULL);
 
-		success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT);
-		g_assert (success == FALSE);
+	g_assert ((!data->remove_secret) == nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT));
+	g_assert ((!data->remove_secret) == nm_setting_compare (new, old, NM_SETTING_COMPARE_FLAG_EXACT));
+
+	_test_compare_secrets_check_diff (old, new, NM_SETTING_COMPARE_FLAG_EXACT, !data->remove_secret, TRUE);
+	_test_compare_secrets_check_diff (new, old, NM_SETTING_COMPARE_FLAG_EXACT, !data->remove_secret, TRUE);
+
+	g_assert (nm_setting_compare (old, new, data->comp_flags));
+	g_assert (nm_setting_compare (new, old, data->comp_flags));
+
+	_test_compare_secrets_check_diff (old, new, data->comp_flags, TRUE, TRUE);
+	_test_compare_secrets_check_diff (new, old, data->comp_flags, TRUE, TRUE);
+
+	/* OK. Try again, but this time not only change the secret, also let the secret flags differ... */
+	if (data->secret_flags != NM_SETTING_SECRET_FLAG_NONE) {
+		nm_setting_set_secret_flags (new, NM_SETTING_WIRELESS_SECURITY_PSK, NM_SETTING_SECRET_FLAG_NONE, NULL);
+
+		_test_compare_secrets_check_diff (old, new, NM_SETTING_COMPARE_FLAG_EXACT, FALSE, FALSE);
+		_test_compare_secrets_check_diff (new, old, NM_SETTING_COMPARE_FLAG_EXACT, FALSE, FALSE);
+
+		_test_compare_secrets_check_diff (old, new, data->comp_flags, TRUE, FALSE);
+		_test_compare_secrets_check_diff (new, old, data->comp_flags, FALSE, FALSE);
+
+		nm_setting_set_secret_flags (new, NM_SETTING_WIRELESS_SECURITY_PSK, data->secret_flags, NULL);
 	}
 
-	success = nm_setting_compare (old, new, data->comp_flags);
-	g_assert (success);
+	conn_old = nmtst_create_minimal_connection ("test-compare-secrets", NULL, NM_SETTING_WIRELESS_SETTING_NAME, NULL);
+	nm_connection_add_setting (conn_old, nm_setting_duplicate (old));
+	conn_new = nm_simple_connection_new_clone (conn_old);
+	nm_connection_add_setting (conn_new, nm_setting_duplicate (new));
+
+	g_assert ((!data->remove_secret) == nm_connection_compare (conn_old, conn_new, NM_SETTING_COMPARE_FLAG_EXACT));
+	g_assert ((!data->remove_secret) == nm_connection_compare (conn_new, conn_old, NM_SETTING_COMPARE_FLAG_EXACT));
+
+	g_assert (nm_connection_compare (conn_old, conn_new, data->comp_flags));
+	g_assert (nm_connection_compare (conn_new, conn_old, data->comp_flags));
 }
 
 static void
@@ -3473,6 +3638,41 @@ test_connection_changed_cb (NMConnection *connection, gboolean *data)
 	*data = TRUE;
 }
 
+static guint32
+_netmask_to_prefix (guint32 netmask)
+{
+	guint32 prefix;
+	guint8 v;
+	const guint8 *p = (guint8 *) &netmask;
+
+	if (p[3]) {
+		prefix = 24;
+		v = p[3];
+	} else if (p[2]) {
+		prefix = 16;
+		v = p[2];
+	} else if (p[1]) {
+		prefix = 8;
+		v = p[1];
+	} else {
+		prefix = 0;
+		v = p[0];
+	}
+
+	while (v) {
+		prefix++;
+		v <<= 1;
+	}
+
+	g_assert_cmpint (prefix, <=, 32);
+
+	/* we re-implemented the netmask-to-prefix code differently. Check
+	 * that they agree. */
+	g_assert_cmpint (prefix, ==, nm_utils_ip4_netmask_to_prefix (netmask));
+
+	return prefix;
+}
+
 static void
 test_ip4_prefix_to_netmask (void)
 {
@@ -3480,7 +3680,7 @@ test_ip4_prefix_to_netmask (void)
 
 	for (i = 0; i<=32; i++) {
 		guint32 netmask = _nm_utils_ip4_prefix_to_netmask (i);
-		int plen = nm_utils_ip4_netmask_to_prefix (netmask);
+		int plen = _netmask_to_prefix (netmask);
 
 		g_assert_cmpint (i, ==, plen);
 		{
@@ -3510,7 +3710,7 @@ test_ip4_netmask_to_prefix (void)
 		guint32 netmask = _nm_utils_ip4_prefix_to_netmask (i);
 		guint32 netmask_lowest_bit = netmask & ~_nm_utils_ip4_prefix_to_netmask (i-1);
 
-		g_assert_cmpint (i, ==, nm_utils_ip4_netmask_to_prefix (netmask));
+		g_assert_cmpint (i, ==, _netmask_to_prefix (netmask));
 
 		for (j = 0; j < 2*i; j++) {
 			guint32 r = g_rand_int (rand);
@@ -3524,7 +3724,7 @@ test_ip4_netmask_to_prefix (void)
 
 			/* create an invalid netmask with holes and check that the function
 			 * returns the longest prefix. */
-			prefix_holey = nm_utils_ip4_netmask_to_prefix (netmask_holey);
+			prefix_holey = _netmask_to_prefix (netmask_holey);
 
 			g_assert_cmpint (i, ==, prefix_holey);
 		}
@@ -5054,7 +5254,7 @@ test_setting_ip4_gateway (void)
 	GVariantBuilder addrs_builder;
 	GError *error = NULL;
 
-	g_assert_cmpstr (nm_utils_inet4_ntop (addr_vals_0[0], NULL), ==, "192.168.1.10");
+	nmtst_assert_ip4_address (addr_vals_0[0], "192.168.1.10");
 
 	/* When serializing on the daemon side, ipv4.gateway is copied to the first
 	 * entry of ipv4.addresses
@@ -5096,7 +5296,7 @@ test_setting_ip4_gateway (void)
 
 		addr_array = g_variant_get_fixed_array (addr_var, &length, sizeof (guint32));
 		g_assert_cmpint (length, ==, 3);
-		g_assert_cmpstr (nm_utils_inet4_ntop (addr_array[2], NULL), ==, "192.168.1.1");
+		nmtst_assert_ip4_address (addr_array[2], "192.168.1.1");
 		g_variant_unref (addr_var);
 	}
 	g_variant_unref (value);
@@ -5203,7 +5403,7 @@ test_setting_ip6_gateway (void)
 
 		gateway_bytes = g_variant_get_fixed_array (gateway_var, &length, 1);
 		g_assert_cmpint (length, ==, 16);
-		g_assert_cmpstr (nm_utils_inet6_ntop ((struct in6_addr *) gateway_bytes, NULL), ==, "abcd::1");
+		nmtst_assert_ip6_address ((struct in6_addr *) gateway_bytes, "abcd::1");
 		g_variant_unref (gateway_var);
 	}
 	g_variant_unref (value);
@@ -5322,6 +5522,158 @@ test_setting_user_data (void)
 
 /*****************************************************************************/
 
+typedef union {
+	struct sockaddr     sa;
+	struct sockaddr_in  in;
+	struct sockaddr_in6 in6;
+} SockAddrUnion;
+
+static void
+_sock_addr_endpoint (const char *endpoint,
+                     const char *host,
+                     gint32 port)
+{
+	nm_auto_unref_sockaddrendpoint NMSockAddrEndpoint *ep = NULL;
+	const char *s_endpoint;
+	const char *s_host;
+	gint32 s_port;
+	SockAddrUnion sockaddr = { };
+
+	g_assert (endpoint);
+	g_assert (!host == (port == -1));
+	g_assert (port >= -1 && port <= G_MAXUINT16);
+
+	ep = nm_sock_addr_endpoint_new (endpoint);
+	g_assert (ep);
+
+	s_endpoint = nm_sock_addr_endpoint_get_endpoint (ep);
+	s_host = nm_sock_addr_endpoint_get_host (ep);
+	s_port = nm_sock_addr_endpoint_get_port (ep);
+	g_assert_cmpstr (endpoint, ==, s_endpoint);
+	g_assert_cmpstr (host,     ==, s_host);
+	g_assert_cmpint (port,     ==, s_port);
+
+	g_assert (!nm_sock_addr_endpoint_get_fixed_sockaddr (ep, &sockaddr));
+
+	if (endpoint[0] != ' ') {
+		gs_free char *endpoint2 = NULL;
+
+		/* also test with a leading space */
+		endpoint2 = g_strdup_printf (" %s", endpoint);
+		_sock_addr_endpoint (endpoint2, host, port);
+	}
+
+	if (endpoint[0] && endpoint[strlen (endpoint) - 1] != ' ') {
+		gs_free char *endpoint2 = NULL;
+
+		/* also test with a trailing space */
+		endpoint2 = g_strdup_printf ("%s ", endpoint);
+		_sock_addr_endpoint (endpoint2, host, port);
+	}
+}
+
+static void
+_sock_addr_endpoint_fixed (const char *endpoint,
+                           const char *host,
+                           guint16 port,
+                           guint scope_id)
+{
+	nm_auto_unref_sockaddrendpoint NMSockAddrEndpoint *ep = NULL;
+	const char *s_endpoint;
+	const char *s_host;
+	gint32 s_port;
+	int addr_family;
+	NMIPAddr addrbin;
+	SockAddrUnion sockaddr = { };
+
+	g_assert (endpoint);
+	g_assert (host);
+	g_assert (port > 0);
+
+	if (!nm_utils_parse_inaddr_bin (AF_UNSPEC, host, &addr_family, &addrbin))
+		g_assert_not_reached ();
+
+	ep = nm_sock_addr_endpoint_new (endpoint);
+	g_assert (ep);
+
+	s_endpoint = nm_sock_addr_endpoint_get_endpoint (ep);
+	s_host = nm_sock_addr_endpoint_get_host (ep);
+	s_port = nm_sock_addr_endpoint_get_port (ep);
+	g_assert_cmpstr (endpoint, ==, s_endpoint);
+	g_assert_cmpstr (NULL,     !=, s_host);
+	g_assert_cmpint (port,     ==, s_port);
+
+	if (!nm_sock_addr_endpoint_get_fixed_sockaddr (ep, &sockaddr))
+		g_assert_not_reached ();
+
+	g_assert_cmpint (sockaddr.sa.sa_family, ==, addr_family);
+	if (addr_family == AF_INET) {
+		const SockAddrUnion s = {
+			.in = {
+				.sin_family = AF_INET,
+				.sin_addr   = addrbin.addr4_struct,
+				.sin_port   = htons (port),
+			},
+		};
+
+		g_assert_cmpint (sockaddr.in.sin_addr.s_addr, ==, addrbin.addr4);
+		g_assert_cmpint (sockaddr.in.sin_port, ==, htons (port));
+		g_assert (memcmp (&s, &sockaddr, sizeof (s.in)) == 0);
+	} else if (addr_family == AF_INET6) {
+		const SockAddrUnion s = {
+			.in6 = {
+				.sin6_family   = AF_INET6,
+				.sin6_addr     = addrbin.addr6,
+				.sin6_scope_id = scope_id,
+				.sin6_port     = htons (port),
+			},
+		};
+
+		g_assert (memcmp (&sockaddr.in6.sin6_addr, &addrbin, sizeof (addrbin.addr6)) == 0);
+		g_assert_cmpint (sockaddr.in6.sin6_port, ==, htons (port));
+		g_assert_cmpint (sockaddr.in6.sin6_scope_id, ==, scope_id);
+		g_assert_cmpint (sockaddr.in6.sin6_flowinfo, ==, 0);
+		g_assert (memcmp (&s, &sockaddr, sizeof (s.in6)) == 0);
+	} else
+		g_assert_not_reached ();
+}
+
+static void
+test_sock_addr_endpoint (void)
+{
+	_sock_addr_endpoint ("",                NULL, -1);
+	_sock_addr_endpoint (":",               NULL, -1);
+	_sock_addr_endpoint ("a",               NULL, -1);
+	_sock_addr_endpoint ("a:",              NULL, -1);
+	_sock_addr_endpoint (":a",              NULL, -1);
+	_sock_addr_endpoint ("[]:a",            NULL, -1);
+	_sock_addr_endpoint ("[]a",             NULL, -1);
+	_sock_addr_endpoint ("[]:",             NULL, -1);
+	_sock_addr_endpoint ("[a]b",            NULL, -1);
+	_sock_addr_endpoint ("[a:b",            NULL, -1);
+	_sock_addr_endpoint ("[a[:b",           NULL, -1);
+	_sock_addr_endpoint ("a:6",             "a",  6);
+	_sock_addr_endpoint ("a:6",             "a",  6);
+	_sock_addr_endpoint ("[a]:6",           "a",  6);
+	_sock_addr_endpoint ("[a]:6",           "a",  6);
+	_sock_addr_endpoint ("[a]:655",         "a",  655);
+	_sock_addr_endpoint ("[ab]:][6",        NULL, -1);
+	_sock_addr_endpoint ("[ab]:]:[6",       NULL, -1);
+	_sock_addr_endpoint ("[a[]:b",          NULL, -1);
+	_sock_addr_endpoint ("[192.169.6.x]:6", "192.169.6.x", 6);
+	_sock_addr_endpoint ("[192.169.6.x]:0", NULL, -1);
+	_sock_addr_endpoint ("192.169.6.7:0",   NULL, -1);
+
+	_sock_addr_endpoint_fixed ("192.169.6.7:6",   "192.169.6.7", 6, 0);
+	_sock_addr_endpoint_fixed ("[192.169.6.7]:6", "192.169.6.7", 6, 0);
+	_sock_addr_endpoint_fixed ("[a:b::]:6", "a:b::", 6, 0);
+	_sock_addr_endpoint_fixed ("[a:b::%7]:6", "a:b::", 6, 7);
+	_sock_addr_endpoint_fixed ("a:b::1%75:6", "a:b::1", 6, 75);
+	_sock_addr_endpoint_fixed ("a:b::1%0:64", "a:b::1", 64, 0);
+}
+
+/*****************************************************************************/
+
 static void
 test_hexstr2bin (void)
 {
@@ -5687,7 +6039,7 @@ __test_uuid (const char *expected_uuid, const char *str, gssize slen, char *uuid
 static void
 test_nm_utils_uuid_generate_from_strings (void)
 {
-	const NMUuid uuid0 = { 0 };
+	const NMUuid uuid0 = { };
 
 	g_assert_cmpmem (&uuid0, sizeof (uuid0), _uuid ("00000000-0000-0000-0000-000000000000"), 16);
 
@@ -7535,6 +7887,8 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/general/test_setting_compare_default_strv", test_setting_compare_default_strv);
 	g_test_add_func ("/core/general/test_setting_user_data", test_setting_user_data);
 
+	g_test_add_func ("/core/general/test_sock_addr_endpoint", test_sock_addr_endpoint);
+
 	g_test_add_func ("/core/general/hexstr2bin", test_hexstr2bin);
 	g_test_add_func ("/core/general/nm_strquote", test_nm_strquote);
 	g_test_add_func ("/core/general/test_nm_utils_uuid_generate_from_string", test_nm_utils_uuid_generate_from_string);
@@ -7564,4 +7918,3 @@ int main (int argc, char **argv)
 
 	return g_test_run ();
 }
-
diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c
index 78e01ce9..9b9d1c04 100644
--- a/libnm-core/tests/test-keyfile.c
+++ b/libnm-core/tests/test-keyfile.c
@@ -249,7 +249,7 @@ _keyfile_convert (NMConnection **con,
 		k0_c1_k2 = _nm_keyfile_write (k0_c1, write_handler, write_data);
 		k0_c1_k2_c3 = _nm_keyfile_read (k0_c1_k2, keyfile_name, read_handler, read_data, FALSE);
 
-		/* It is a expeced behavior, that if @k0 contains a relative path ca-cert, @k0_c1 will
+		/* It is a expected behavior, that if @k0 contains a relative path ca-cert, @k0_c1 will
 		 * contain that path as relative. But @k0_c1_k2 and @k0_c1_k2_c3 will have absolute paths.
 		 * In this case, hack up @k0_c1_k2_c3 to contain the same relative path. */
 		s1 = nm_connection_get_setting_802_1x (k0_c1);
@@ -417,7 +417,7 @@ test_8021x_cert (void)
 	g_assert_no_error (error);
 	g_assert (success);
 
-	/* test reseting ca-cert to different values and see whether we can write/read. */
+	/* test resetting ca-cert to different values and see whether we can write/read. */
 
 	nm_connection_add_setting (con, NM_SETTING (s_8021x));
 	nmtst_assert_connection_verifies_and_normalizable (con);
diff --git a/libnm-core/tests/test-secrets.c b/libnm-core/tests/test-secrets.c
index d31c43d2..5debe5cf 100644
--- a/libnm-core/tests/test-secrets.c
+++ b/libnm-core/tests/test-secrets.c
@@ -21,8 +21,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-8021x.h"
 #include "nm-setting-cdma.h"
 #include "nm-setting-connection.h"
diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c
index 4e010feb..2011273a 100644
--- a/libnm-core/tests/test-setting.c
+++ b/libnm-core/tests/test-setting.c
@@ -20,7 +20,6 @@
 #include "nm-default.h"
 
 #include <linux/pkt_sched.h>
-#include <string.h>
 
 #include "nm-utils.h"
 #include "nm-utils-private.h"
@@ -45,6 +44,101 @@
 
 /*****************************************************************************/
 
+/* converts @dict to a connection. In this case, @dict must be good, without warnings, so that
+ * NM_SETTING_PARSE_FLAGS_STRICT and NM_SETTING_PARSE_FLAGS_BEST_EFFORT yield the exact same results. */
+static NMConnection *
+_connection_new_from_dbus_strict (GVariant *dict,
+                                  gboolean normalize)
+{
+	gs_unref_object NMConnection *con_x_0 = NULL;
+	gs_unref_object NMConnection *con_x_s = NULL;
+	gs_unref_object NMConnection *con_x_e = NULL;
+	gs_unref_object NMConnection *con_n_0 = NULL;
+	gs_unref_object NMConnection *con_n_s = NULL;
+	gs_unref_object NMConnection *con_n_e = NULL;
+	gs_free_error GError *error = NULL;
+	guint i;
+
+	g_assert (g_variant_is_of_type (dict, NM_VARIANT_TYPE_CONNECTION));
+
+	con_x_0 = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_NONE, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_x_0), error);
+
+	con_x_s = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_STRICT, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_x_s), error);
+
+	con_x_e = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_x_e), error);
+
+	con_n_0 = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_n_0), error);
+
+	con_n_s = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_STRICT | NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_n_s), error);
+
+	con_n_e = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT | NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_n_e), error);
+
+	nmtst_assert_connection_verifies (con_x_0);
+	nmtst_assert_connection_verifies (con_x_e);
+	nmtst_assert_connection_verifies (con_x_s);
+
+	nmtst_assert_connection_verifies_without_normalization (con_n_0);
+	nmtst_assert_connection_verifies_without_normalization (con_n_e);
+	nmtst_assert_connection_verifies_without_normalization (con_n_s);
+
+	/* randomly compare some pairs that we created. They must all be equal,
+	 * after accounting for normalization. */
+	for (i = 0; i < 10; i++) {
+		NMConnection *cons[] = { con_x_0, con_x_s, con_x_e, con_n_0, con_n_s, con_n_e };
+		guint idx_a = (nmtst_get_rand_int () % G_N_ELEMENTS (cons));
+		guint idx_b = (nmtst_get_rand_int () % G_N_ELEMENTS (cons));
+		gboolean normalize_a, normalize_b;
+
+		if (idx_a <= 2 && idx_b <= 2) {
+			normalize_a = nmtst_get_rand_bool ();
+			normalize_b = normalize_a;
+		} else if (idx_a > 2 && idx_b > 2) {
+			normalize_a = nmtst_get_rand_bool ();
+			normalize_b = nmtst_get_rand_bool ();
+		} else {
+			normalize_a = (idx_a <= 2) ? TRUE : nmtst_get_rand_bool ();
+			normalize_b = (idx_b <= 2) ? TRUE : nmtst_get_rand_bool ();
+		}
+		nmtst_assert_connection_equals (cons[idx_a], normalize_a, cons[idx_b], normalize_b);
+	}
+
+	return (normalize)
+	       ? g_steal_pointer (&con_x_0)
+	       : g_steal_pointer (&con_n_0);
+}
+
+/*****************************************************************************/
+
+static char *
+_create_random_ipaddr (int addr_family, gboolean as_service)
+{
+	char delimiter = as_service ? ':' : '/';
+	int num;
+
+	if (addr_family == AF_UNSPEC)
+		addr_family = nmtst_rand_select (AF_INET, AF_INET6);
+
+	g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
+
+	if (as_service)
+		num = (nmtst_get_rand_int () % 1000) + 30000;
+	else
+		num = addr_family == AF_INET ? 32 : 128;
+
+	if (addr_family == AF_INET)
+		return g_strdup_printf ("192.168.%u.%u%c%d", nmtst_get_rand_int () % 256, nmtst_get_rand_int () % 256, delimiter, num);
+	else
+		return g_strdup_printf ("a:b:c::%02x:%02x%c%d", nmtst_get_rand_int () % 256, nmtst_get_rand_int () % 256, delimiter, num);
+}
+
+/*****************************************************************************/
+
 static void
 compare_blob_data (const char *test,
                    const char *key_path,
@@ -421,20 +515,17 @@ create_bond_connection (NMConnection **con, NMSettingBond **s_bond)
 }
 
 #define test_verify_options(exp, ...) \
-	G_STMT_START { \
-		const char *__opts[] = { __VA_ARGS__ , NULL }; \
-		\
-		_test_verify_options (__opts, exp); \
-	} G_STMT_END
+	_test_verify_options (NM_MAKE_STRV (__VA_ARGS__), exp)
 
 static void
-_test_verify_options (const char **options, gboolean expected_result)
+_test_verify_options (const char *const *options,
+                      gboolean expected_result)
 {
 	gs_unref_object NMConnection *con = NULL;
 	NMSettingBond *s_bond;
 	GError *error = NULL;
 	gboolean success;
-	const char **option;
+	const char *const *option;
 
 	create_bond_connection (&con, &s_bond);
 
@@ -1946,6 +2037,587 @@ test_tc_config_dbus (void)
 
 /*****************************************************************************/
 
+static GPtrArray *
+_rndt_wg_peers_create (void)
+{
+	GPtrArray *wg_peers;
+	guint i, n;
+
+	wg_peers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_wireguard_peer_unref);
+
+	n = nmtst_get_rand_int () % 10;
+	for (i = 0; i < n; i++) {
+		NMWireGuardPeer *peer;
+		guint8 public_key_buf[NM_WIREGUARD_PUBLIC_KEY_LEN];
+		guint8 preshared_key_buf[NM_WIREGUARD_SYMMETRIC_KEY_LEN];
+		gs_free char *public_key = NULL;
+		gs_free char *preshared_key = NULL;
+		gs_free char *s_endpoint = NULL;
+		guint i_aip, n_aip;
+
+		/* we don't bother to create a valid curve25519 public key. Of course, libnm cannot
+		 * check whether the public key is bogus or not. Hence, for our purpose a random
+		 * bogus key is good enough. */
+		public_key = g_base64_encode (nmtst_rand_buf (NULL, public_key_buf, sizeof (public_key_buf)), sizeof (public_key_buf));
+
+		preshared_key = g_base64_encode (nmtst_rand_buf (NULL, preshared_key_buf, sizeof (preshared_key_buf)), sizeof (preshared_key_buf));
+
+		s_endpoint = _create_random_ipaddr (AF_UNSPEC, TRUE);
+
+		peer = nm_wireguard_peer_new ();
+		if (!nm_wireguard_peer_set_public_key (peer, public_key, TRUE))
+			g_assert_not_reached ();
+
+		if (!nm_wireguard_peer_set_preshared_key (peer, nmtst_rand_select (NULL, preshared_key), TRUE))
+			g_assert_not_reached ();
+
+		nm_wireguard_peer_set_preshared_key_flags (peer, nmtst_rand_select (NM_SETTING_SECRET_FLAG_NONE,
+		                                                                    NM_SETTING_SECRET_FLAG_NOT_SAVED,
+		                                                                    NM_SETTING_SECRET_FLAG_AGENT_OWNED));
+
+		nm_wireguard_peer_set_persistent_keepalive (peer,
+		                                            nmtst_rand_select ((guint32) 0, nmtst_get_rand_int ()));
+
+		if (!nm_wireguard_peer_set_endpoint (peer, nmtst_rand_select (s_endpoint, NULL), TRUE))
+			g_assert_not_reached ();
+
+		n_aip = nmtst_rand_select (0, nmtst_get_rand_int () % 10);
+		for (i_aip = 0; i_aip < n_aip; i_aip++) {
+			gs_free char *aip = NULL;
+
+			aip = _create_random_ipaddr (AF_UNSPEC, FALSE);
+			if (!nm_wireguard_peer_append_allowed_ip (peer, aip, FALSE))
+				g_assert_not_reached ();
+		}
+
+		g_assert (nm_wireguard_peer_is_valid (peer, TRUE, TRUE, NULL));
+
+		nm_wireguard_peer_seal (peer);
+		g_ptr_array_add (wg_peers, peer);
+	}
+
+	return wg_peers;
+}
+
+static const char *
+_rndt_wg_peers_to_keyfile (GPtrArray *wg_peers,
+                           gboolean strict,
+                           char **out_str)
+{
+	nm_auto_free_gstring GString *gstr = NULL;
+	nm_auto_free_gstring GString *gstr_aip = NULL;
+	guint i, j;
+
+	g_assert (wg_peers);
+	g_assert (out_str && !*out_str);
+
+	nm_gstring_prepare (&gstr);
+	for (i = 0; i < wg_peers->len; i++) {
+		const NMWireGuardPeer *peer = wg_peers->pdata[i];
+		gs_free char *s_endpoint = NULL;
+		gs_free char *s_preshared_key = NULL;
+		gs_free char *s_preshared_key_flags = NULL;
+		gs_free char *s_persistent_keepalive = NULL;
+		gs_free char *s_allowed_ips = NULL;
+
+		if (nm_wireguard_peer_get_endpoint (peer))
+			s_endpoint = g_strdup_printf ("endpoint=%s\n", nm_wireguard_peer_get_endpoint (peer));
+		else if (!strict)
+			s_endpoint = g_strdup_printf ("endpoint=\n");
+
+		if (   nm_wireguard_peer_get_preshared_key (peer)
+		    || !strict) {
+			if (nm_wireguard_peer_get_preshared_key_flags (peer) == NM_SETTING_SECRET_FLAG_NONE)
+				s_preshared_key = g_strdup_printf ("preshared-key=%s\n", nm_wireguard_peer_get_preshared_key (peer) ?: "");
+		}
+
+		if (   nm_wireguard_peer_get_preshared_key_flags (peer) != NM_SETTING_SECRET_FLAG_NOT_REQUIRED
+		    || !strict)
+			s_preshared_key_flags = g_strdup_printf ("preshared-key-flags=%d\n", (int) nm_wireguard_peer_get_preshared_key_flags (peer));
+
+		if (   nm_wireguard_peer_get_persistent_keepalive (peer) != 0
+		    || !strict)
+			s_persistent_keepalive = g_strdup_printf ("persistent-keepalive=%u\n", nm_wireguard_peer_get_persistent_keepalive (peer));
+
+		if (   nm_wireguard_peer_get_allowed_ips_len (peer) > 0
+		    || !strict) {
+			nm_gstring_prepare (&gstr_aip);
+			for (j = 0; j < nm_wireguard_peer_get_allowed_ips_len (peer); j++)
+				g_string_append_printf (gstr_aip, "%s;", nm_wireguard_peer_get_allowed_ip (peer, j, NULL));
+			s_allowed_ips = g_strdup_printf ("allowed-ips=%s\n", gstr_aip->str);
+		}
+
+		if (   !s_endpoint
+		    && !s_preshared_key
+		    && !s_preshared_key_flags
+		    && !s_persistent_keepalive
+		    && !s_allowed_ips)
+			s_endpoint = g_strdup_printf ("endpoint=\n");
+
+		g_string_append_printf (gstr,
+		                        "\n"
+		                        "[wireguard-peer.%s]\n"
+		                        "%s" /* endpoint */
+		                        "%s" /* preshared-key */
+		                        "%s" /* preshared-key-flags */
+		                        "%s" /* persistent-keepalive */
+		                        "%s" /* allowed-ips */
+		                        "",
+		                        nm_wireguard_peer_get_public_key (peer),
+		                        s_endpoint ?: "",
+		                        s_preshared_key ?: "",
+		                        s_preshared_key_flags ?: "",
+		                        s_persistent_keepalive ?: "",
+		                        s_allowed_ips ?: "");
+	}
+
+	return (*out_str = g_string_free (g_steal_pointer (&gstr), FALSE));
+}
+
+static void
+_rndt_wg_peers_assert_equal (NMSettingWireGuard *s_wg,
+                             GPtrArray *peers,
+                             gboolean consider_persistent_secrets,
+                             gboolean consider_all_secrets,
+                             gboolean expect_no_secrets)
+{
+	guint i;
+
+	g_assert (NM_IS_SETTING_WIREGUARD (s_wg));
+	g_assert (peers);
+
+	g_assert_cmpint (peers->len, ==, nm_setting_wireguard_get_peers_len (s_wg));
+
+	for (i = 0; i < peers->len; i++) {
+		const NMWireGuardPeer *a = peers->pdata[i];
+		const NMWireGuardPeer *b = nm_setting_wireguard_get_peer (s_wg, i);
+		gboolean consider_secrets;
+
+		g_assert (a);
+		g_assert (b);
+
+		g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS), ==, 0);
+
+		if (   consider_all_secrets
+		    || !nm_wireguard_peer_get_preshared_key (a))
+			consider_secrets = TRUE;
+		else if (nm_wireguard_peer_get_preshared_key (b))
+			consider_secrets = TRUE;
+		else if (   consider_persistent_secrets
+		         && nm_wireguard_peer_get_preshared_key_flags (b) == NM_SETTING_SECRET_FLAG_NONE)
+			consider_secrets = TRUE;
+		else
+			consider_secrets = FALSE;
+
+		if (consider_secrets) {
+			g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), ==, nm_wireguard_peer_get_preshared_key (b));
+			g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_EXACT), ==, 0);
+		}
+
+		if (expect_no_secrets)
+			g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (b), ==, NULL);
+	}
+}
+
+static void
+_rndt_wg_peers_fix_secrets (NMSettingWireGuard *s_wg,
+                            GPtrArray *peers)
+{
+	guint i;
+
+	g_assert (NM_IS_SETTING_WIREGUARD (s_wg));
+	g_assert (peers);
+
+	g_assert_cmpint (peers->len, ==, nm_setting_wireguard_get_peers_len (s_wg));
+
+	for (i = 0; i < peers->len; i++) {
+		const NMWireGuardPeer *a = peers->pdata[i];
+		const NMWireGuardPeer *b = nm_setting_wireguard_get_peer (s_wg, i);
+		nm_auto_unref_wgpeer NMWireGuardPeer *b_clone = NULL;
+
+		g_assert (a);
+		g_assert (b);
+
+		g_assert_cmpint (nm_wireguard_peer_get_preshared_key_flags (a), ==, nm_wireguard_peer_get_preshared_key_flags (b));
+		g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS), ==, 0);
+
+		if (!nm_streq0 (nm_wireguard_peer_get_preshared_key (a),
+		                nm_wireguard_peer_get_preshared_key (b))) {
+			g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), !=, NULL);
+			g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (b), ==, NULL);
+			g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_AGENT_OWNED,
+			                                                                    NM_SETTING_SECRET_FLAG_NOT_SAVED));
+			b_clone = nm_wireguard_peer_new_clone (b, TRUE);
+			if (!nm_wireguard_peer_set_preshared_key (b_clone, nm_wireguard_peer_get_preshared_key (a), TRUE))
+				g_assert_not_reached ();
+			nm_setting_wireguard_set_peer (s_wg, b_clone, i);
+			b = nm_setting_wireguard_get_peer (s_wg, i);
+			g_assert (b == b_clone);
+		} else {
+			if (nm_wireguard_peer_get_preshared_key (a)) {
+				g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_NONE,
+				                                                                    NM_SETTING_SECRET_FLAG_NOT_REQUIRED));
+			} else {
+				g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_AGENT_OWNED,
+				                                                                    NM_SETTING_SECRET_FLAG_NONE,
+				                                                                    NM_SETTING_SECRET_FLAG_NOT_SAVED,
+				                                                                    NM_SETTING_SECRET_FLAG_NOT_REQUIRED));
+			}
+		}
+
+		g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), ==, nm_wireguard_peer_get_preshared_key (b));
+		g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_EXACT), ==, 0);
+	}
+}
+
+static void
+test_roundtrip_conversion (gconstpointer test_data)
+{
+	const int MODE = GPOINTER_TO_INT (test_data);
+	const char *ID= nm_sprintf_bufa (100, "roundtip-conversion-%d", MODE);
+	const char *UUID= "63376701-b61e-4318-bf7e-664a1c1eeaab";
+	const char *INTERFACE_NAME = nm_sprintf_bufa (100, "ifname%d", MODE);
+	guint32 ETH_MTU = nmtst_rand_select ((guint32) 0u,
+	                                     nmtst_get_rand_int ());
+	const char *WG_PRIVATE_KEY = nmtst_get_rand_bool ()
+	                             ? "yGXGK+5bVnxSJUejH4vbpXbq+ZtaG4NB8IHRK/aVtE0="
+	                             : NULL;
+	const NMSettingSecretFlags WG_PRIVATE_KEY_FLAGS = nmtst_rand_select (NM_SETTING_SECRET_FLAG_NONE,
+	                                                                     NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	                                                                     NM_SETTING_SECRET_FLAG_AGENT_OWNED);
+	const guint WG_LISTEN_PORT = nmtst_rand_select (0u,
+	                                                nmtst_get_rand_int () % 0x10000);
+	const guint WG_FWMARK = nmtst_rand_select (0u,
+	                                           nmtst_get_rand_int ());
+	gs_unref_ptrarray GPtrArray *kf_data_arr = g_ptr_array_new_with_free_func (g_free);
+	gs_unref_ptrarray GPtrArray *wg_peers = NULL;
+	const NMConnectionSerializationFlags dbus_serialization_flags[] = {
+		NM_CONNECTION_SERIALIZE_ALL,
+		NM_CONNECTION_SERIALIZE_NO_SECRETS,
+		NM_CONNECTION_SERIALIZE_ONLY_SECRETS,
+	};
+	guint dbus_serialization_flags_idx;
+	gs_unref_object NMConnection *con = NULL;
+	gs_free_error GError *error = NULL;
+	gs_free char *tmp_str = NULL;
+	guint kf_data_idx;
+	NMSettingConnection *s_con = NULL;
+	NMSettingWired *s_eth = NULL;
+	NMSettingWireGuard *s_wg = NULL;
+	guint i;
+
+	switch (MODE) {
+	case 0:
+		con = nmtst_create_minimal_connection (ID, UUID, NM_SETTING_WIRED_SETTING_NAME, &s_con);
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME,
+		              INTERFACE_NAME,
+		              NULL);
+		nmtst_connection_normalize (con);
+
+		s_eth = NM_SETTING_WIRED (nm_connection_get_setting (con, NM_TYPE_SETTING_WIRED));
+		g_assert (NM_IS_SETTING_WIRED (s_eth));
+
+		g_object_set (s_eth,
+		              NM_SETTING_WIRED_MTU,
+		              ETH_MTU,
+		              NULL);
+
+		g_ptr_array_add (kf_data_arr,
+		    g_strdup_printf ("[connection]\n"
+		                     "id=%s\n"
+		                     "uuid=%s\n"
+		                     "type=ethernet\n"
+		                     "interface-name=%s\n"
+		                     "permissions=\n"
+		                     "\n"
+		                     "[ethernet]\n"
+		                     "mac-address-blacklist=\n"
+		                     "%s" /* mtu */
+		                     "\n"
+		                     "[ipv4]\n"
+		                     "dns-search=\n"
+		                     "method=auto\n"
+		                     "\n"
+		                     "[ipv6]\n"
+		                     "addr-gen-mode=stable-privacy\n"
+		                     "dns-search=\n"
+		                     "method=auto\n"
+		                     "",
+		                     ID,
+		                     UUID,
+		                     INTERFACE_NAME,
+		                       (ETH_MTU != 0)
+		                     ? nm_sprintf_bufa (100, "mtu=%u\n", ETH_MTU)
+		                     : ""));
+
+		g_ptr_array_add (kf_data_arr,
+		    g_strdup_printf ("[connection]\n"
+		                     "id=%s\n"
+		                     "uuid=%s\n"
+		                     "type=ethernet\n"
+		                     "interface-name=%s\n"
+		                     "permissions=\n"
+		                     "\n"
+		                     "[ethernet]\n"
+		                     "mac-address-blacklist=\n"
+		                     "%s" /* mtu */
+		                     "\n"
+		                     "[ipv4]\n"
+		                     "dns-search=\n"
+		                     "method=auto\n"
+		                     "\n"
+		                     "[ipv6]\n"
+		                     "addr-gen-mode=stable-privacy\n"
+		                     "dns-search=\n"
+		                     "method=auto\n"
+		                     "",
+		                     ID,
+		                     UUID,
+		                     INTERFACE_NAME,
+		                       (ETH_MTU != 0)
+		                     ? nm_sprintf_bufa (100, "mtu=%d\n", (int) ETH_MTU)
+		                     : ""));
+
+		break;
+
+	case 1:
+		con = nmtst_create_minimal_connection (ID, UUID, "wireguard", &s_con);
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME,
+		              INTERFACE_NAME,
+		              NULL);
+		nmtst_connection_normalize (con);
+
+		s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (con, NM_TYPE_SETTING_WIREGUARD));
+
+		g_ptr_array_add (kf_data_arr,
+		    g_strdup_printf ("[connection]\n"
+		                     "id=%s\n"
+		                     "uuid=%s\n"
+		                     "type=wireguard\n"
+		                     "interface-name=%s\n"
+		                     "permissions=\n"
+		                     "\n"
+		                     "[ipv4]\n"
+		                     "dns-search=\n"
+		                     "method=disabled\n"
+		                     "\n"
+		                     "[ipv6]\n"
+		                     "addr-gen-mode=stable-privacy\n"
+		                     "dns-search=\n"
+		                     "method=ignore\n"
+		                     "",
+		                     ID,
+		                     UUID,
+		                     INTERFACE_NAME));
+		break;
+
+	case 2:
+		con = nmtst_create_minimal_connection (ID, UUID, "wireguard", &s_con);
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME,
+		              INTERFACE_NAME,
+		              NULL);
+		nmtst_connection_normalize (con);
+
+		s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (con, NM_TYPE_SETTING_WIREGUARD));
+		g_object_set (s_wg,
+		              NM_SETTING_WIREGUARD_PRIVATE_KEY,
+		              WG_PRIVATE_KEY,
+		              NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
+		              WG_PRIVATE_KEY_FLAGS,
+		              NM_SETTING_WIREGUARD_LISTEN_PORT,
+		              WG_LISTEN_PORT,
+		              NM_SETTING_WIREGUARD_FWMARK,
+		              WG_FWMARK,
+		              NULL);
+
+		wg_peers = _rndt_wg_peers_create ();
+
+		for (i = 0; i < wg_peers->len; i++)
+			nm_setting_wireguard_append_peer (s_wg, wg_peers->pdata[i]);
+
+		nm_clear_g_free (&tmp_str);
+
+		g_ptr_array_add (kf_data_arr,
+		    g_strdup_printf ("[connection]\n"
+		                     "id=%s\n"
+		                     "uuid=%s\n"
+		                     "type=wireguard\n"
+		                     "interface-name=%s\n"
+		                     "permissions=\n"
+		                     "%s" /* [wireguard] */
+		                     "%s" /* fwmark */
+		                     "%s" /* listen-port */
+		                     "%s" /* private-key-flags */
+		                     "%s" /* private-key */
+		                     "%s" /* [wireguard-peers*] */
+		                     "\n"
+		                     "[ipv4]\n"
+		                     "dns-search=\n"
+		                     "method=disabled\n"
+		                     "\n"
+		                     "[ipv6]\n"
+		                     "addr-gen-mode=stable-privacy\n"
+		                     "dns-search=\n"
+		                     "method=ignore\n"
+		                     "",
+		                     ID,
+		                     UUID,
+		                     INTERFACE_NAME,
+		                     (  (   (WG_FWMARK != 0)
+		                         || (WG_LISTEN_PORT != 0)
+		                         || (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE)
+		                         || (   WG_PRIVATE_KEY
+		                             && WG_PRIVATE_KEY_FLAGS == NM_SETTING_SECRET_FLAG_NONE))
+		                      ? "\n[wireguard]\n"
+		                      : ""),
+		                     (  (WG_FWMARK != 0)
+		                      ? nm_sprintf_bufa (100, "fwmark=%u\n", WG_FWMARK)
+		                      : ""),
+		                     (  (WG_LISTEN_PORT != 0)
+		                      ? nm_sprintf_bufa (100, "listen-port=%u\n", WG_LISTEN_PORT)
+		                      : ""),
+		                     (  (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE)
+		                      ? nm_sprintf_bufa (100, "private-key-flags=%u\n", (guint) WG_PRIVATE_KEY_FLAGS)
+		                      : ""),
+		                     (  (   WG_PRIVATE_KEY
+		                         && WG_PRIVATE_KEY_FLAGS == NM_SETTING_SECRET_FLAG_NONE)
+		                      ? nm_sprintf_bufa (100, "private-key=%s\n", WG_PRIVATE_KEY)
+		                      : ""),
+		                     _rndt_wg_peers_to_keyfile (wg_peers, TRUE, &tmp_str)));
+
+		_rndt_wg_peers_assert_equal (s_wg, wg_peers, TRUE, TRUE, FALSE);
+		break;
+
+	default:
+		g_assert_not_reached ();
+	}
+
+	/* the first kf_data_arr entry is special: it is the exact result of what we expect
+	 * when converting @con to keyfile. Write @con to keyfile and compare the expected result
+	 * literally. */
+	{
+		gs_unref_keyfile GKeyFile *kf = NULL;
+
+		kf = nm_keyfile_write (con, NULL, NULL, &error);
+		nmtst_assert_success (kf, error);
+
+		/* the first kf_data_arr entry is special: it must be what the writer would
+		 * produce again. */
+		nmtst_keyfile_assert_data (kf, kf_data_arr->pdata[0], -1);
+	}
+
+	/* check that reading any of kf_data_arr yields the same result that we expect. */
+	for (kf_data_idx = 0; kf_data_idx < kf_data_arr->len; kf_data_idx++) {
+		gs_unref_object NMConnection *con2 = NULL;
+		NMSettingWireGuard *s_wg2 = NULL;
+		NMSettingWired *s_eth2 = NULL;
+
+		con2 = nmtst_create_connection_from_keyfile (kf_data_arr->pdata[kf_data_idx], "/no/where/file.nmconnection");
+
+		switch (MODE) {
+		case 0:
+			s_eth2 = NM_SETTING_WIRED (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIRED));
+			g_assert (NM_IS_SETTING_WIRED (s_eth2));
+
+			if (   ETH_MTU > (guint32) G_MAXINT
+			    && kf_data_idx == 1) {
+				/* older versions wrote values > 2^21 as signed integers, but the reader would
+				 * always reject such negative values for G_TYPE_UINT.
+				 *
+				 * The test case kf_data_idx #1 still writes the values in the old style.
+				 * The behavior was fixed, but such values are still rejected as invalid.
+				 *
+				 * Patch the setting so that the comparison below succeeds are usual. */
+				g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, 0);
+				g_object_set (s_eth2,
+				              NM_SETTING_WIRED_MTU,
+				              ETH_MTU,
+				              NULL);
+			}
+
+			g_assert_cmpint (nm_setting_wired_get_mtu (s_eth), ==, ETH_MTU);
+			g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, ETH_MTU);
+			break;
+
+		case 1:
+			s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
+			g_assert (NM_IS_SETTING_WIREGUARD (s_wg2));
+
+			g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, NULL);
+			g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, NULL);
+			break;
+
+		case 2:
+			s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
+			g_assert (NM_IS_SETTING_WIREGUARD (s_wg2));
+
+			/* the private key was lost due to the secret-flags. Patch it. */
+			if (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE) {
+				g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, NULL);
+				g_object_set (s_wg2,
+				              NM_SETTING_WIREGUARD_PRIVATE_KEY,
+				              WG_PRIVATE_KEY,
+				              NULL);
+			}
+
+			g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, WG_PRIVATE_KEY);
+			g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, WG_PRIVATE_KEY);
+
+			_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, FALSE, FALSE);
+			_rndt_wg_peers_fix_secrets (s_wg2, wg_peers);
+			_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, TRUE, FALSE);
+			break;
+		}
+
+		nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ());
+	}
+
+	for (dbus_serialization_flags_idx = 0; dbus_serialization_flags_idx < G_N_ELEMENTS (dbus_serialization_flags); dbus_serialization_flags_idx++) {
+		NMConnectionSerializationFlags flag = dbus_serialization_flags[dbus_serialization_flags_idx];
+		gs_unref_variant GVariant *con_var = NULL;
+		gs_unref_object NMConnection *con2 = NULL;
+		NMSettingWireGuard *s_wg2 = NULL;
+
+		con_var = nm_connection_to_dbus (con, flag);
+		g_assert (g_variant_is_of_type (con_var, NM_VARIANT_TYPE_CONNECTION));
+		g_assert (g_variant_is_floating (con_var));
+		g_variant_ref_sink (con_var);
+
+		if (flag == NM_CONNECTION_SERIALIZE_ALL) {
+			con2 = _connection_new_from_dbus_strict (con_var, TRUE);
+			nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ());
+
+			{
+				gs_unref_keyfile GKeyFile *kf = NULL;
+
+				kf = nm_keyfile_write (con2, NULL, NULL, &error);
+				nmtst_assert_success (kf, error);
+				nmtst_keyfile_assert_data (kf, kf_data_arr->pdata[0], -1);
+			}
+		}
+
+		switch (MODE) {
+		case 2:
+			if (flag == NM_CONNECTION_SERIALIZE_ALL) {
+				s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
+
+				if (flag == NM_CONNECTION_SERIALIZE_ALL)
+					_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, TRUE, FALSE);
+				else if (flag == NM_CONNECTION_SERIALIZE_NO_SECRETS)
+					_rndt_wg_peers_assert_equal (s_wg2, wg_peers, FALSE, FALSE, TRUE);
+				else
+					g_assert_not_reached ();
+			}
+			break;
+		}
+	}
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -2022,5 +2694,9 @@ main (int argc, char **argv)
 	g_test_add_func ("/libnm/settings/team-port/sycn_from_config_full", test_team_port_full_config);
 #endif
 
+	g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/0", GINT_TO_POINTER (0), test_roundtrip_conversion);
+	g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/1", GINT_TO_POINTER (1), test_roundtrip_conversion);
+	g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/2", GINT_TO_POINTER (2), test_roundtrip_conversion);
+
 	return g_test_run ();
 }
diff --git a/libnm-core/tests/test-settings-defaults.c b/libnm-core/tests/test-settings-defaults.c
index 95570b03..9e640543 100644
--- a/libnm-core/tests/test-settings-defaults.c
+++ b/libnm-core/tests/test-settings-defaults.c
@@ -21,8 +21,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-utils.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-cdma.h"