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/certs/test-aes-128-key.pem (renamed from libnm-core/tests/certs/test-aes-key.pem)0
-rw-r--r--libnm-core/tests/certs/test-aes-256-key.pem54
-rw-r--r--libnm-core/tests/meson.build3
-rw-r--r--libnm-core/tests/test-crypto.c116
-rw-r--r--libnm-core/tests/test-general.c383
-rw-r--r--libnm-core/tests/test-keyfile.c14
-rw-r--r--libnm-core/tests/test-setting.c325
7 files changed, 737 insertions, 158 deletions
diff --git a/libnm-core/tests/certs/test-aes-key.pem b/libnm-core/tests/certs/test-aes-128-key.pem
index aab8f463..aab8f463 100644
--- a/libnm-core/tests/certs/test-aes-key.pem
+++ b/libnm-core/tests/certs/test-aes-128-key.pem
diff --git a/libnm-core/tests/certs/test-aes-256-key.pem b/libnm-core/tests/certs/test-aes-256-key.pem
new file mode 100644
index 00000000..e51bafd3
--- /dev/null
+++ b/libnm-core/tests/certs/test-aes-256-key.pem
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,5FF6BD2D4E57E8933D4A6814DEF5305A
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/libnm-core/tests/meson.build b/libnm-core/tests/meson.build
index 2eeadd88..627e6b53 100644
--- a/libnm-core/tests/meson.build
+++ b/libnm-core/tests/meson.build
@@ -37,8 +37,7 @@ foreach test_unit: test_units
     c_args: [
         '-DNETWORKMANAGER_COMPILATION_TEST',
         '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE',
-      ] +
-      nm_build_cflags,
+      ],
     link_with: libnm_core
   )
   test(
diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c
index fb99ffea..6b63e6fd 100644
--- a/libnm-core/tests/test-crypto.c
+++ b/libnm-core/tests/test-crypto.c
@@ -28,7 +28,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "crypto.h"
+#include "nm-crypto-impl.h"
 #include "nm-utils.h"
 #include "nm-errors.h"
 #include "nm-core-internal.h"
@@ -99,37 +99,20 @@ static void
 test_cert (gconstpointer test_data)
 {
 	gs_free char *path = NULL;
-	GByteArray *array;
+	gs_unref_bytes GBytes *cert = NULL;
 	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 	GError *error = NULL;
+	gboolean success;
 
 	path = g_build_filename (TEST_CERT_DIR, (const char *) test_data, NULL);
 
-	array = crypto_load_and_verify_certificate (path, &format, &error);
-	g_assert_no_error (error);
+	success = nm_crypto_load_and_verify_certificate (path, &format, &cert, &error);
+	nmtst_assert_success (success, error);
 	g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_X509);
 
-	g_byte_array_free (array, TRUE);
-
 	g_assert (nm_utils_file_is_certificate (path));
 }
 
-static GByteArray *
-file_to_byte_array (const char *filename)
-{
-	char *contents;
-	GByteArray *array = NULL;
-	gsize length = 0;
-
-	if (g_file_get_contents (filename, &contents, &length, NULL)) {
-		array = g_byte_array_sized_new (length);
-		g_byte_array_append (array, (guint8 *) contents, length);
-		g_assert (array->len == length);
-		g_free (contents);
-	}
-	return array;
-}
-
 static void
 test_load_private_key (const char *path,
                        const char *password,
@@ -138,13 +121,13 @@ test_load_private_key (const char *path,
 {
 	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
 	gboolean is_encrypted = FALSE;
-	GByteArray *array, *decrypted;
+	gs_unref_bytes GBytes *array = NULL;
 	GError *error = NULL;
 
 	g_assert (nm_utils_file_is_private_key (path, &is_encrypted));
 	g_assert (is_encrypted);
 
-	array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
+	array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
 	/* Even if the password is wrong, we should determine the key type */
 	g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
 
@@ -164,16 +147,14 @@ test_load_private_key (const char *path,
 	g_assert (array != NULL);
 
 	if (decrypted_path) {
-		/* Compare the crypto decrypted key against a known-good decryption */
-		decrypted = file_to_byte_array (decrypted_path);
-		g_assert (decrypted != NULL);
-		g_assert (decrypted->len == array->len);
-		g_assert (memcmp (decrypted->data, array->data, array->len) == 0);
+		gs_free char *contents = NULL;
+		gsize length;
 
-		g_byte_array_free (decrypted, TRUE);
+		/* Compare the crypto decrypted key against a known-good decryption */
+		if (!g_file_get_contents (decrypted_path, &contents, &length, NULL))
+			g_assert_not_reached ();
+		g_assert (nm_utils_gbytes_equal_mem (array, contents, length));
 	}
-
-	g_byte_array_free (array, TRUE);
 }
 
 static void
@@ -187,7 +168,7 @@ test_load_pkcs12 (const char *path,
 
 	g_assert (nm_utils_file_is_private_key (path, NULL));
 
-	format = crypto_verify_private_key (path, password, &is_encrypted, &error);
+	format = nm_crypto_verify_private_key (path, password, &is_encrypted, &error);
 	if (expected_error != -1) {
 		g_assert_error (error, NM_CRYPTO_ERROR, expected_error);
 		g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
@@ -209,7 +190,7 @@ test_load_pkcs12_no_password (const char *path)
 	g_assert (nm_utils_file_is_private_key (path, NULL));
 
 	/* We should still get a valid returned crypto file format */
-	format = crypto_verify_private_key (path, NULL, &is_encrypted, &error);
+	format = nm_crypto_verify_private_key (path, NULL, &is_encrypted, &error);
 	g_assert_no_error (error);
 	g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_PKCS12);
 	g_assert (is_encrypted);
@@ -221,7 +202,7 @@ test_is_pkcs12 (const char *path, gboolean expect_fail)
 	gboolean is_pkcs12;
 	GError *error = NULL;
 
-	is_pkcs12 = crypto_is_pkcs12_file (path, &error);
+	is_pkcs12 = nm_crypto_is_pkcs12_file (path, &error);
 
 	if (expect_fail) {
 		g_assert_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA);
@@ -244,7 +225,7 @@ test_load_pkcs8 (const char *path,
 
 	g_assert (nm_utils_file_is_private_key (path, NULL));
 
-	format = crypto_verify_private_key (path, password, &is_encrypted, &error);
+	format = nm_crypto_verify_private_key (path, password, &is_encrypted, &error);
 	if (expected_error != -1) {
 		g_assert_error (error, NM_CRYPTO_ERROR, expected_error);
 		g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
@@ -261,34 +242,35 @@ test_encrypt_private_key (const char *path,
                           const char *password)
 {
 	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
-	GByteArray *array, *encrypted, *re_decrypted;
+	gs_unref_bytes GBytes *array = NULL;
+	gs_unref_bytes GBytes *encrypted = NULL;
+	gs_unref_bytes GBytes *re_decrypted = NULL;
 	GError *error = NULL;
 
-	array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
-	g_assert_no_error (error);
-	g_assert (array != NULL);
+	array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
+	nmtst_assert_success (array, error);
 	g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
 
 	/* Now re-encrypt the private key */
-	encrypted = nm_utils_rsa_key_encrypt (array->data, array->len, password, NULL, &error);
-	g_assert_no_error (error);
-	g_assert (encrypted != NULL);
+	encrypted = nmtst_crypto_rsa_key_encrypt (g_bytes_get_data (array, NULL),
+	                                          g_bytes_get_size (array),
+	                                          password,
+	                                          NULL,
+	                                          &error);
+	nmtst_assert_success (encrypted, error);
 
 	/* Then re-decrypt the private key */
 	key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
-	re_decrypted = crypto_decrypt_openssl_private_key_data (encrypted->data, encrypted->len,
-	                                                        password, &key_type, &error);
-	g_assert_no_error (error);
-	g_assert (re_decrypted != NULL);
+	re_decrypted = nmtst_crypto_decrypt_openssl_private_key_data (g_bytes_get_data (encrypted, NULL),
+	                                                              g_bytes_get_size (encrypted),
+	                                                              password,
+	                                                              &key_type,
+	                                                              &error);
+	nmtst_assert_success (re_decrypted, error);
 	g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
 
 	/* Compare the original decrypted key with the re-decrypted key */
-	g_assert_cmpint (array->len, ==, re_decrypted->len);
-	g_assert (!memcmp (array->data, re_decrypted->data, array->len));
-
-	g_byte_array_free (re_decrypted, TRUE);
-	g_byte_array_free (encrypted, TRUE);
-	g_byte_array_free (array, TRUE);
+	g_assert (g_bytes_equal (array, re_decrypted));
 }
 
 static void
@@ -417,15 +399,16 @@ test_md5 (void)
 
 	for (i = 0; i < G_N_ELEMENTS (md5_tests); i++) {
 		memset (digest, 0, sizeof (digest));
-		crypto_md5_hash (md5_tests[i].salt,
-		                 /* crypto_md5_hash() used to clamp salt_len to 8.  It
-		                  * doesn't any more, so we need to do it here now to
-		                  * get output that matches md5_tests[i].result.
-		                  */
-		                 md5_tests[i].salt ? 8 : 0,
-		                 md5_tests[i].password,
-		                 strlen (md5_tests[i].password),
-		                 digest, md5_tests[i].digest_size);
+		nm_crypto_md5_hash ((const guint8 *) md5_tests[i].salt,
+		                    /* nm_crypto_md5_hash() used to clamp salt_len to 8.  It
+		                     * doesn't any more, so we need to do it here now to
+		                     * get output that matches md5_tests[i].result.
+		                     */
+		                    md5_tests[i].salt ? 8 : 0,
+		                    (const guint8 *) md5_tests[i].password,
+		                    strlen (md5_tests[i].password),
+		                    (guint8 *) digest,
+		                    md5_tests[i].digest_size);
 
 		hex = nm_utils_bin2hexstr (digest, md5_tests[i].digest_size, -1);
 		g_assert_cmpstr (hex, ==, md5_tests[i].result);
@@ -444,7 +427,7 @@ main (int argc, char **argv)
 
 	nmtst_init (&argc, &argv, TRUE);
 
-	success = crypto_init (&error);
+	success = _nm_crypto_init (&error);
 	g_assert_no_error (error);
 	g_assert (success);
 
@@ -476,8 +459,11 @@ main (int argc, char **argv)
 	g_test_add_data_func ("/libnm/crypto/key/padding-8",
 	                      "test2_key_and_cert.pem, 12345testing",
 	                      test_key);
-	g_test_add_data_func ("/libnm/crypto/key/aes",
-	                      "test-aes-key.pem, test-aes-password",
+	g_test_add_data_func ("/libnm/crypto/key/aes-128",
+	                      "test-aes-128-key.pem, test-aes-password",
+	                      test_key);
+	g_test_add_data_func ("/libnm/crypto/key/aes-256",
+	                      "test-aes-256-key.pem, test-aes-password",
 	                      test_key);
 	g_test_add_data_func ("/libnm/crypto/key/decrypted",
 	                      "test-key-only-decrypted.pem",
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 3b9debd9..38531248 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2008 - 2011 Red Hat, Inc.
+ * Copyright 2008 - 2018 Red Hat, Inc.
  *
  */
 
@@ -43,6 +43,7 @@
 #include "nm-setting-bridge-port.h"
 #include "nm-setting-cdma.h"
 #include "nm-setting-connection.h"
+#include "nm-setting-ethtool.h"
 #include "nm-setting-generic.h"
 #include "nm-setting-gsm.h"
 #include "nm-setting-infiniband.h"
@@ -61,9 +62,11 @@
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
 #include "nm-setting-wireless-security.h"
+#include "nm-setting-wpan.h"
 #include "nm-simple-connection.h"
 #include "nm-keyfile-internal.h"
 #include "nm-utils/nm-dedup-multi.h"
+#include "nm-ethtool-utils.h"
 
 #include "test-general-enums.h"
 
@@ -222,8 +225,8 @@ test_nm_g_slice_free_fcn (void)
 	p = g_slice_new (gint32);
 	(nm_g_slice_free_fcn (gint32)) (p);
 
-	p = g_slice_new (gint);
-	(nm_g_slice_free_fcn (gint)) (p);
+	p = g_slice_new (int);
+	(nm_g_slice_free_fcn (int)) (p);
 
 	p = g_slice_new (gint64);
 	nm_g_slice_free_fcn_gint64 (p);
@@ -235,7 +238,7 @@ test_nm_g_slice_free_fcn (void)
 /*****************************************************************************/
 
 static void
-_do_test_nm_utils_strsplit_set (const char *str, ...)
+_do_test_nm_utils_strsplit_set (gboolean escape, const char *str, ...)
 {
 	gs_unref_ptrarray GPtrArray *args_array = g_ptr_array_new ();
 	const char *const*args;
@@ -252,7 +255,7 @@ _do_test_nm_utils_strsplit_set (const char *str, ...)
 
 	args = (const char *const*) args_array->pdata;
 
-	words = nm_utils_strsplit_set (str, " \t\n");
+	words = nm_utils_strsplit_set (str, " \t\n", escape);
 
 	if (!args[0]) {
 		g_assert (!words);
@@ -265,7 +268,7 @@ _do_test_nm_utils_strsplit_set (const char *str, ...)
 		g_assert (args[i]);
 		g_assert (words[i]);
 		g_assert (args[i][0]);
-		g_assert (NM_STRCHAR_ALL (args[i], ch, !NM_IN_SET (ch, ' ', '\t', '\n')));
+		g_assert (escape || NM_STRCHAR_ALL (args[i], ch, !NM_IN_SET (ch, ' ', '\t', '\n')));
 		g_assert_cmpstr (args[i], ==, words[i]);
 	}
 }
@@ -276,21 +279,29 @@ _do_test_nm_utils_strsplit_set (const char *str, ...)
 static void
 test_nm_utils_strsplit_set (void)
 {
-	do_test_nm_utils_strsplit_set (NULL);
-	do_test_nm_utils_strsplit_set ("");
-	do_test_nm_utils_strsplit_set ("\t");
-	do_test_nm_utils_strsplit_set (" \t\n");
-	do_test_nm_utils_strsplit_set ("a", "a");
-	do_test_nm_utils_strsplit_set ("a b", "a", "b");
-	do_test_nm_utils_strsplit_set ("a\rb", "a\rb");
-	do_test_nm_utils_strsplit_set ("  a\rb  ", "a\rb");
-	do_test_nm_utils_strsplit_set ("  a bbbd afds ere", "a", "bbbd", "afds", "ere");
-	do_test_nm_utils_strsplit_set ("1 2 3 4 5 6 7 8 9 0 "
+	do_test_nm_utils_strsplit_set (FALSE, NULL);
+	do_test_nm_utils_strsplit_set (FALSE, "");
+	do_test_nm_utils_strsplit_set (FALSE, "\t");
+	do_test_nm_utils_strsplit_set (FALSE, " \t\n");
+	do_test_nm_utils_strsplit_set (FALSE, "a", "a");
+	do_test_nm_utils_strsplit_set (FALSE, "a b", "a", "b");
+	do_test_nm_utils_strsplit_set (FALSE, "a\rb", "a\rb");
+	do_test_nm_utils_strsplit_set (FALSE, "  a\rb  ", "a\rb");
+	do_test_nm_utils_strsplit_set (FALSE, "  a bbbd afds ere", "a", "bbbd", "afds", "ere");
+	do_test_nm_utils_strsplit_set (FALSE,
+	                               "1 2 3 4 5 6 7 8 9 0 "
 	                               "1 2 3 4 5 6 7 8 9 0 "
 	                               "1 2 3 4 5 6 7 8 9 0",
 	                               "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
 	                               "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
 	                               "1", "2", "3", "4", "5", "6", "7", "8", "9", "0");
+	do_test_nm_utils_strsplit_set (TRUE, "\\", "\\");
+	do_test_nm_utils_strsplit_set (TRUE, "\\ ", "\\ ");
+	do_test_nm_utils_strsplit_set (TRUE, "\\\\", "\\\\");
+	do_test_nm_utils_strsplit_set (TRUE, "\\\t", "\\\t");
+	do_test_nm_utils_strsplit_set (TRUE, "foo\\", "foo\\");
+	do_test_nm_utils_strsplit_set (TRUE, "bar foo\\", "bar", "foo\\");
+	do_test_nm_utils_strsplit_set (TRUE, "\\ a b\\ \\  c", "\\ a", "b\\ \\ ", "c");
 }
 
 /*****************************************************************************/
@@ -2594,6 +2605,7 @@ test_connection_diff_a_only (void)
 			{ NM_SETTING_CONNECTION_AUTOCONNECT,          NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,  NM_SETTING_DIFF_RESULT_IN_A },
+			{ NM_SETTING_CONNECTION_MULTI_CONNECT,        NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_READ_ONLY,            NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_PERMISSIONS,          NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_ZONE,                 NM_SETTING_DIFF_RESULT_IN_A },
@@ -2606,6 +2618,7 @@ test_connection_diff_a_only (void)
 			{ NM_SETTING_CONNECTION_LLDP,                 NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_AUTH_RETRIES,         NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_MDNS,                 NM_SETTING_DIFF_RESULT_IN_A },
+			{ NM_SETTING_CONNECTION_LLMNR,                NM_SETTING_DIFF_RESULT_IN_A },
 			{ NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }
 		} },
 		{ NM_SETTING_WIRED_SETTING_NAME, {
@@ -5328,18 +5341,17 @@ test_hexstr2bin (void)
 		{ "aab:ccc:ddd" },
 		{ "aab::ccc:ddd" },
 	};
-	GBytes *b;
 	guint i;
 
 	for (i = 0; i < G_N_ELEMENTS (items); i++) {
+		gs_unref_bytes GBytes *b = NULL;
+
 		b = nm_utils_hexstr2bin (items[i].str);
-		if (items[i].expected_len) {
+		if (items[i].expected_len)
 			g_assert (b);
-			g_assert_cmpint (g_bytes_get_size (b), ==, items[i].expected_len);
-			g_assert (memcmp (g_bytes_get_data (b, NULL), items[i].expected, g_bytes_get_size (b)) == 0);
-			g_bytes_unref (b);
-		} else
-			g_assert (b == NULL);
+		else
+			g_assert (!b);
+		g_assert (nm_utils_gbytes_equal_mem (b, items[i].expected, items[i].expected_len));
 	}
 }
 
@@ -6033,7 +6045,7 @@ static void
 test_nm_utils_is_power_of_two (void)
 {
 	guint64 xyes, xno;
-	gint i, j;
+	int i, j;
 	GRand *rand = nmtst_get_rand ();
 	int numbits;
 
@@ -6124,7 +6136,7 @@ _test_find_binary_search_do (const int *array, gsize len)
 
 	expected_result = _nm_utils_ptrarray_find_first (parray, len, pneedle);
 
-	idx = _nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL, &idx_first, &idx_last);
+	idx = nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL, &idx_first, &idx_last);
 	if (expected_result >= 0) {
 		g_assert_cmpint (expected_result, ==, idx);
 	} else {
@@ -6186,12 +6198,12 @@ _test_find_binary_search_do_uint32 (const int *int_array, gsize len)
 			expected_result = idx;
 	}
 
-	idx = _nm_utils_array_find_binary_search (array,
-	                                          sizeof (guint32),
-	                                          len,
-	                                          &NEEDLE,
-	                                          nm_cmp_uint32_p_with_data,
-	                                          NULL);
+	idx = nm_utils_array_find_binary_search (array,
+	                                         sizeof (guint32),
+	                                         len,
+	                                         &NEEDLE,
+	                                         nm_cmp_uint32_p_with_data,
+	                                         NULL);
 	if (expected_result >= 0)
 		g_assert_cmpint (expected_result, ==, idx);
 	else {
@@ -6291,11 +6303,11 @@ test_nm_utils_ptrarray_find_binary_search_with_duplicates (void)
 			for (i = 0; i < i_len + BIN_SEARCH_W_DUPS_JITTER; i++) {
 				gconstpointer p = GINT_TO_POINTER (i);
 
-				idx = _nm_utils_ptrarray_find_binary_search (arr, i_len, p, _test_bin_search2_cmp, NULL, &idx_first, &idx_last);
+				idx = nm_utils_ptrarray_find_binary_search (arr, i_len, p, _test_bin_search2_cmp, NULL, &idx_first, &idx_last);
 
 				idx_first2 = _nm_utils_ptrarray_find_first (arr, i_len, p);
 
-				idx2 = _nm_utils_array_find_binary_search (arr, sizeof (gpointer), i_len, &p, _test_bin_search2_cmp_p, NULL);
+				idx2 = nm_utils_array_find_binary_search (arr, sizeof (gpointer), i_len, &p, _test_bin_search2_cmp_p, NULL);
 				g_assert_cmpint (idx, ==, idx2);
 
 				if (idx_first2 < 0) {
@@ -6547,74 +6559,161 @@ test_nm_utils_enum (void)
 /*****************************************************************************/
 
 static void
-do_test_utils_str_utf8safe (const char *str, const char *expected, NMUtilsStrUtf8SafeFlags flags)
+_do_test_utils_str_utf8safe_unescape (const char *str, const char *expected, gsize expected_len)
 {
-	const char *str_safe, *s;
-	gs_free char *str2 = NULL;
-	gs_free char *str3 = NULL;
+	gsize l;
+	const char *s;
+	gs_free gpointer buf_free_1 = NULL;
+	gs_free char *str_free_1 = NULL;
 
-	str_safe = nm_utils_str_utf8safe_escape (str, flags, &str2);
+	s = nm_utils_buf_utf8safe_unescape (str, &l, &buf_free_1);
+	g_assert_cmpint (expected_len, ==, l);
+	g_assert_cmpstr (s, ==, expected);
 
-	str3 = nm_utils_str_utf8safe_escape_cp (str, flags);
-	g_assert_cmpstr (str3, ==, str_safe);
-	g_assert ((!str && !str3) || (str != str3));
-	g_clear_pointer (&str3, g_free);
+	if (str == NULL) {
+		g_assert (!s);
+		g_assert (!buf_free_1);
+		g_assert_cmpint (l, ==, 0);
+	} else {
+		g_assert (s);
+		if (!strchr (str, '\\')) {
+			g_assert (!buf_free_1);
+			g_assert (s == str);
+			g_assert_cmpint (l, ==, strlen (str));
+		} else {
+			g_assert (buf_free_1);
+			g_assert (s == buf_free_1);
+			g_assert (memcmp (s, expected, expected_len) == 0);
+		}
+	}
+
+	if (   expected
+	    && l == strlen (expected)) {
+		/* there are no embeeded NULs. Check that nm_utils_str_utf8safe_unescape() yields the same result. */
+		s = nm_utils_str_utf8safe_unescape (str, &str_free_1);
+		g_assert_cmpstr (s, ==, expected);
+		if (strchr (str, '\\')) {
+			g_assert (str_free_1 != str);
+			g_assert (s == str_free_1);
+		} else
+			g_assert (s == str);
+	}
+}
+
+#define do_test_utils_str_utf8safe_unescape(str, expected) \
+	_do_test_utils_str_utf8safe_unescape (""str"", expected, NM_STRLEN (expected))
+
+static void
+_do_test_utils_str_utf8safe (const char *str, gsize str_len, const char *expected, NMUtilsStrUtf8SafeFlags flags)
+{
+	const char *str_safe;
+	const char *buf_safe;
+	const char *s;
+	gs_free gpointer buf_free_1 = NULL;
+	gs_free char *str_free_1 = NULL;
+	gs_free char *str_free_2 = NULL;
+	gs_free char *str_free_3 = NULL;
+	gs_free char *str_free_4 = NULL;
+	gs_free char *str_free_5 = NULL;
+	gs_free char *str_free_6 = NULL;
+	gs_free char *str_free_7 = NULL;
+	gs_free char *str_free_8 = NULL;
+	gboolean str_has_nul = FALSE;
+
+	buf_safe = nm_utils_buf_utf8safe_escape (str, str_len, flags, &str_free_1);
+
+	str_safe = nm_utils_str_utf8safe_escape (str, flags, &str_free_2);
+
+	if (str_len == 0) {
+		g_assert (buf_safe == NULL);
+		g_assert (str_free_1 == NULL);
+		g_assert (str_safe == str);
+		g_assert (str == NULL || str[0] == '\0');
+		g_assert (str_free_2 == NULL);
+	} else if (str_len == strlen (str)) {
+		g_assert (buf_safe);
+		g_assert_cmpstr (buf_safe, ==, str_safe);
+
+		/* nm_utils_buf_utf8safe_escape() can only return a pointer equal to the input string,
+		 * if and only if str_len is negative. Otherwise, the input str won't be NUL terminated
+		 * and cannot be returned. */
+		g_assert (buf_safe != str);
+		g_assert (buf_safe == str_free_1);
+	} else
+		str_has_nul = TRUE;
+
+	str_free_3 = nm_utils_str_utf8safe_escape_cp (str, flags);
+	g_assert_cmpstr (str_free_3, ==, str_safe);
+	g_assert ((!str && !str_free_3) || (str != str_free_3));
+
+	if (str_len > 0)
+		_do_test_utils_str_utf8safe_unescape (buf_safe, str, str_len);
 
 	if (expected == NULL) {
+		g_assert (!str_has_nul);
+
 		g_assert (str_safe == str);
-		g_assert (!str2);
+		g_assert (!str_free_2);
 		if (str) {
 			g_assert (!strchr (str, '\\'));
 			g_assert (g_utf8_validate (str, -1, NULL));
 		}
 
-		g_assert (str == nm_utils_str_utf8safe_unescape (str_safe, &str3));
-		g_assert (!str3);
+		g_assert (str == nm_utils_str_utf8safe_unescape (str_safe, &str_free_4));
+		g_assert (!str_free_4);
 
-		str3 = nm_utils_str_utf8safe_unescape_cp (str_safe);
+		str_free_5 = nm_utils_str_utf8safe_unescape_cp (str_safe);
 		if (str) {
-			g_assert (str3 != str);
-			g_assert_cmpstr (str3, ==, str);
+			g_assert (str_free_5 != str);
+			g_assert_cmpstr (str_free_5, ==, str);
 		} else
-			g_assert (!str3);
-		g_clear_pointer (&str3, g_free);
+			g_assert (!str_free_5);
 		return;
 	}
 
-	g_assert (str);
-	g_assert (str_safe != str);
-	g_assert (str_safe == str2);
-	g_assert (   strchr (str, '\\')
-	          || !g_utf8_validate (str, -1, NULL)
-	          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII)
-	              && NM_STRCHAR_ANY (str, ch, (guchar) ch >= 127))
-	          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)
-	              && NM_STRCHAR_ANY (str, ch, (guchar) ch < ' ')));
-	g_assert (g_utf8_validate (str_safe, -1, NULL));
+	if (!str_has_nul) {
+		g_assert (str);
+		g_assert (str_safe != str);
+		g_assert (str_safe == str_free_2);
+		g_assert (   strchr (str, '\\')
+		          || !g_utf8_validate (str, -1, NULL)
+		          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII)
+		              && NM_STRCHAR_ANY (str, ch, (guchar) ch >= 127))
+		          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)
+		              && NM_STRCHAR_ANY (str, ch, (guchar) ch < ' ')));
+		g_assert (g_utf8_validate (str_safe, -1, NULL));
 
-	str3 = g_strcompress (str_safe);
-	g_assert_cmpstr (str, ==, str3);
-	g_clear_pointer (&str3, g_free);
+		str_free_6 = g_strcompress (str_safe);
+		g_assert_cmpstr (str, ==, str_free_6);
 
-	str3 = nm_utils_str_utf8safe_unescape_cp (str_safe);
-	g_assert (str3 != str);
-	g_assert_cmpstr (str3, ==, str);
-	g_clear_pointer (&str3, g_free);
+		str_free_7 = nm_utils_str_utf8safe_unescape_cp (str_safe);
+		g_assert (str_free_7 != str);
+		g_assert_cmpstr (str_free_7, ==, str);
 
-	s = nm_utils_str_utf8safe_unescape (str_safe, &str3);
-	g_assert (str3 != str);
-	g_assert (s == str3);
-	g_assert_cmpstr (str3, ==, str);
-	g_clear_pointer (&str3, g_free);
+		s = nm_utils_str_utf8safe_unescape (str_safe, &str_free_8);
+		g_assert (str_free_8 != str);
+		g_assert (s == str_free_8);
+		g_assert_cmpstr (str_free_8, ==, str);
+
+		g_assert_cmpstr (str_safe, ==, expected);
+
+		return;
+	}
+
+	g_assert_cmpstr (buf_safe, ==, expected);
 
-	g_assert_cmpstr (str_safe, ==, expected);
 }
+#define do_test_utils_str_utf8safe(str, expected, flags) \
+	_do_test_utils_str_utf8safe (""str"", NM_STRLEN (str), expected, flags)
 
 static void
 test_utils_str_utf8safe (void)
 {
-	do_test_utils_str_utf8safe (NULL, NULL,                                       NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	_do_test_utils_str_utf8safe (NULL, 0, NULL,                                   NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+
 	do_test_utils_str_utf8safe ("", NULL,                                         NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\\", "\\\\",                                     NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\\a", "\\\\a",                                   NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
 	do_test_utils_str_utf8safe ("\314", "\\314",                                  NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
 	do_test_utils_str_utf8safe ("\314\315x\315\315x", "\\314\\315x\\315\\315x",   NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
 	do_test_utils_str_utf8safe ("\314\315xx", "\\314\\315xx",                     NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
@@ -6636,6 +6735,18 @@ test_utils_str_utf8safe (void)
 	do_test_utils_str_utf8safe ("㈞abä㈞b", NULL,                                 NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
 	do_test_utils_str_utf8safe ("abäb", "ab\\303\\244b",                          NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII);
 	do_test_utils_str_utf8safe ("ab\ab", "ab\\007b",                              NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL);
+
+	do_test_utils_str_utf8safe ("\0", "\\000",                                    NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\0a\0", "\\000a\\000",                           NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\\\0", "\\\\\\000",                              NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\n\0", "\n\\000",                                NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\n\0", "\\012\\000",                             NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL);
+
+	do_test_utils_str_utf8safe_unescape ("\n\\0", "\n\0");
+	do_test_utils_str_utf8safe_unescape ("\n\\01", "\n\01");
+	do_test_utils_str_utf8safe_unescape ("\n\\012", "\n\012");
+	do_test_utils_str_utf8safe_unescape ("\n\\.", "\n.");
+	do_test_utils_str_utf8safe_unescape ("\\n\\.3\\r", "\n.3\r");
 }
 
 /*****************************************************************************/
@@ -6951,7 +7062,7 @@ test_route_attributes_format (void)
 /*****************************************************************************/
 
 static gboolean
-do_test_nm_set_out_called (gint *call_count)
+do_test_nm_set_out_called (int *call_count)
 {
 	(*call_count)++;
 	return TRUE;
@@ -7005,6 +7116,120 @@ test_get_start_time_for_pid (void)
 
 /*****************************************************************************/
 
+static void
+test_nm_va_args_macros (void)
+{
+#define GET_NARG_1(...) \
+	NM_NARG (__VA_ARGS__)
+
+	g_assert_cmpint ( 0, ==, GET_NARG_1 ());
+	g_assert_cmpint ( 1, ==, GET_NARG_1 (x));
+	g_assert_cmpint ( 2, ==, GET_NARG_1 ( ,  ));
+	g_assert_cmpint ( 2, ==, GET_NARG_1 ( , x));
+	g_assert_cmpint ( 2, ==, GET_NARG_1 (x,  ));
+	g_assert_cmpint ( 2, ==, GET_NARG_1 (x, x));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 ( ,  ,  ));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 ( ,  , x));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 ( , x,  ));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 ( , x, x));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 (x,  ,  ));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 (x,  , x));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 (x, x,  ));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 (x, x, x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( ,  ,  ,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( ,  ,  , x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( ,  , x,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( ,  , x, x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( , x,  ,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( , x,  , x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( , x, x,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( , x, x, x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x,  ,  ,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x,  ,  , x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x,  , x,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x,  , x, x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x, x,  ,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x, x,  , x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x, x, x,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x, x, x, x));
+
+	g_assert_cmpint ( 5, ==, GET_NARG_1 (x, x, x, x, x));
+	g_assert_cmpint ( 6, ==, GET_NARG_1 (x, x, x, x, x, x));
+	g_assert_cmpint ( 7, ==, GET_NARG_1 (x, x, x, x, x, x, x));
+	g_assert_cmpint ( 8, ==, GET_NARG_1 (x, x, x, x, x, x, x, x));
+	g_assert_cmpint ( 9, ==, GET_NARG_1 (x, x, x, x, x, x, x, x, x));
+	g_assert_cmpint (10, ==, GET_NARG_1 (x, x, x, x, x, x, x, x, x, x));
+
+	G_STATIC_ASSERT_EXPR (0 == GET_NARG_1 ());
+	G_STATIC_ASSERT_EXPR (1 == GET_NARG_1 (x));
+	G_STATIC_ASSERT_EXPR (2 == GET_NARG_1 (x, x));
+}
+
+/*****************************************************************************/
+
+static void
+test_ethtool_offload (void)
+{
+	const NMEthtoolData *d;
+
+	g_assert_cmpint (nm_ethtool_id_get_by_name ("invalid"),    ==, NM_ETHTOOL_ID_UNKNOWN);
+	g_assert_cmpint (nm_ethtool_id_get_by_name ("feature-rx"), ==, NM_ETHTOOL_ID_FEATURE_RX);
+
+	d = nm_ethtool_data_get_by_optname (NM_ETHTOOL_OPTNAME_FEATURE_RXHASH);
+	g_assert (d);
+	g_assert_cmpint (d->id, ==, NM_ETHTOOL_ID_FEATURE_RXHASH);
+	g_assert_cmpstr (d->optname, ==, NM_ETHTOOL_OPTNAME_FEATURE_RXHASH);
+}
+
+static void
+test_nm_utils_escape_spaces (void)
+{
+	char *to_free;
+
+	g_assert_cmpstr (_nm_utils_escape_spaces (NULL, &to_free), ==, NULL);
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("", &to_free), ==, "");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces (" ", &to_free), ==, "\\ ");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("\t ", &to_free), ==, "\\\t\\ ");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("abc", &to_free), ==, "abc");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("abc def", &to_free), ==, "abc\\ def");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("abc\tdef", &to_free), ==, "abc\\\tdef");
+	g_free (to_free);
+}
+
+static void
+test_nm_utils_unescape_spaces (void)
+{
+#define CHECK_STR(in, out) \
+	G_STMT_START { \
+		gs_free char *str = g_strdup (in); \
+		\
+		g_assert_cmpstr (_nm_utils_unescape_spaces (str), ==, out); \
+	} G_STMT_END
+
+	CHECK_STR ("\\a", "\\a");
+	CHECK_STR ("foobar", "foobar");
+	CHECK_STR ("foo bar", "foo bar");
+	CHECK_STR ("foo\\ bar", "foo bar");
+	CHECK_STR ("foo\\", "foo\\");
+	CHECK_STR ("\\\\\t", "\\\t");
+
+#undef CHECK_STR
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
@@ -7144,8 +7369,8 @@ int main (int argc, char **argv)
 
 	g_test_add_func ("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
 	g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two);
-	g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search);
-	g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates);
+	g_test_add_func ("/core/general/nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search);
+	g_test_add_func ("/core/general/nm_utils_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates);
 	g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey);
 	g_test_add_func ("/core/general/nm_ptrarray_len", test_nm_ptrarray_len);
 
@@ -7153,12 +7378,16 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx);
 	g_test_add_func ("/core/general/_nm_utils_validate_json", test_nm_utils_check_valid_json);
 	g_test_add_func ("/core/general/_nm_utils_team_config_equal", test_nm_utils_team_config_equal);
+	g_test_add_func ("/core/general/_nm_utils_escape_spaces", test_nm_utils_escape_spaces);
+	g_test_add_func ("/core/general/_nm_utils_unescape_spaces", test_nm_utils_unescape_spaces);
 	g_test_add_func ("/core/general/test_nm_utils_enum", test_nm_utils_enum);
 	g_test_add_func ("/core/general/nm-set-out", test_nm_set_out);
 	g_test_add_func ("/core/general/route_attributes/parse", test_route_attributes_parse);
 	g_test_add_func ("/core/general/route_attributes/format", test_route_attributes_format);
 
 	g_test_add_func ("/core/general/get_start_time_for_pid", test_get_start_time_for_pid);
+	g_test_add_func ("/core/general/test_nm_va_args_macros", test_nm_va_args_macros);
+	g_test_add_func ("/core/general/test_ethtool_offload", test_ethtool_offload);
 
 	return g_test_run ();
 }
diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c
index 672c72b6..d941fa22 100644
--- a/libnm-core/tests/test-keyfile.c
+++ b/libnm-core/tests/test-keyfile.c
@@ -127,10 +127,8 @@ _assert_gbytes (GBytes *bytes, gconstpointer data, gssize len)
 
 	if (!len)
 		g_assert (!bytes);
-	else {
-		g_assert_cmpint (g_bytes_get_size (bytes), ==, len);
-		g_assert (memcmp (g_bytes_get_data (bytes, NULL), data, len) == 0);
-	}
+
+	g_assert (nm_utils_gbytes_equal_mem (bytes, data, len));
 }
 
 static GKeyFile *
@@ -277,8 +275,9 @@ _keyfile_convert (NMConnection **con,
 
 				b1 = nm_setting_802_1x_get_ca_cert_blob (s1);
 				b2 = nm_setting_802_1x_get_ca_cert_blob (s2);
-				g_assert_cmpint (g_bytes_get_size (b1), ==, g_bytes_get_size (b2));
-				g_assert (memcmp (g_bytes_get_data (b1, NULL), g_bytes_get_data (b2, NULL), g_bytes_get_size (b1)) == 0);
+				g_assert (b1);
+				g_assert (b2);
+				g_assert (g_bytes_equal (b1, b2));
 				break;
 			}
 			default:
@@ -344,8 +343,7 @@ _test_8021x_cert_check (NMConnection *con,
 		}
 
 		g_assert (blob);
-		g_assert_cmpint (g_bytes_get_size (blob), ==, val_len);
-		g_assert (!memcmp (g_bytes_get_data (blob, NULL), value, val_len));
+		g_assert (nm_utils_gbytes_equal_mem (blob, value, val_len));
 
 		kval = g_key_file_get_string (keyfile, "802-1x", "ca-cert", NULL);
 		g_assert (kval);
diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c
index e71d6b7c..c8eda381 100644
--- a/libnm-core/tests/test-setting.c
+++ b/libnm-core/tests/test-setting.c
@@ -23,9 +23,12 @@
 #include <string.h>
 
 #include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-core-internal.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-bond.h"
 #include "nm-setting-dcb.h"
+#include "nm-setting-ethtool.h"
 #include "nm-setting-team.h"
 #include "nm-setting-team-port.h"
 #include "nm-setting-tc-config.h"
@@ -34,6 +37,7 @@
 #include "nm-simple-connection.h"
 #include "nm-setting-connection.h"
 #include "nm-errors.h"
+#include "nm-keyfile-internal.h"
 
 #include "nm-utils/nm-test-utils.h"
 
@@ -46,7 +50,7 @@ compare_blob_data (const char *test,
                    const char *key_path,
                    GBytes *key)
 {
-	char *contents = NULL;
+	gs_free char *contents = NULL;
 	gsize len = 0;
 	GError *error = NULL;
 	gboolean success;
@@ -57,18 +61,18 @@ compare_blob_data (const char *test,
 	nmtst_assert_success (success, error);
 
 	g_assert_cmpmem (contents, len, g_bytes_get_data (key, NULL), g_bytes_get_size (key));
-
-	g_free (contents);
 }
 
 static void
 check_scheme_path (GBytes *value, const char *path)
 {
 	const guint8 *p;
+	gsize l;
 
 	g_assert (value);
 
-	p = g_bytes_get_data (value, NULL);
+	p = g_bytes_get_data (value, &l);
+	g_assert_cmpint (l, ==, strlen (path) + NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1);
 	g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0);
 	p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
 	g_assert (memcmp (p, path, strlen (path)) == 0);
@@ -998,7 +1002,7 @@ test_runner_loadbalance_sync_from_config (void)
 {
 	gs_unref_ptrarray GPtrArray *tx_hash = NULL;
 
-	tx_hash = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+	tx_hash = g_ptr_array_new_with_free_func (g_free);
 	g_ptr_array_add (tx_hash, g_strdup ("eth"));
 	g_ptr_array_add (tx_hash, g_strdup ("ipv4"));
 	g_ptr_array_add (tx_hash, g_strdup ("ipv6"));
@@ -1035,7 +1039,7 @@ test_runner_lacp_sync_from_config (void)
 {
 	gs_unref_ptrarray GPtrArray *tx_hash = NULL;
 
-	tx_hash = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+	tx_hash = g_ptr_array_new_with_free_func (g_free);
 	g_ptr_array_add (tx_hash, g_strdup ("eth"));
 	g_ptr_array_add (tx_hash, g_strdup ("ipv4"));
 	g_ptr_array_add (tx_hash, g_strdup ("ipv6"));
@@ -1263,6 +1267,307 @@ test_team_port_full_config (void)
 /*****************************************************************************/
 
 static void
+test_ethtool_1 (void)
+{
+	gs_unref_object NMConnection *con = NULL;
+	gs_unref_object NMConnection *con2 = NULL;
+	gs_unref_object NMConnection *con3 = NULL;
+	gs_unref_variant GVariant *variant = NULL;
+	gs_free_error GError *error = NULL;
+	gs_unref_keyfile GKeyFile *keyfile = NULL;
+	NMSettingConnection *s_con;
+	NMSettingEthtool *s_ethtool;
+	NMSettingEthtool *s_ethtool2;
+	NMSettingEthtool *s_ethtool3;
+
+	con = nmtst_create_minimal_connection ("ethtool-1",
+	                                        NULL,
+	                                        NM_SETTING_WIRED_SETTING_NAME,
+	                                        &s_con);
+	s_ethtool = NM_SETTING_ETHTOOL (nm_setting_ethtool_new ());
+	nm_connection_add_setting (con, NM_SETTING (s_ethtool));
+
+	nm_setting_ethtool_set_feature (s_ethtool,
+	                                NM_ETHTOOL_OPTNAME_FEATURE_RX,
+	                                NM_TERNARY_TRUE);
+	nm_setting_ethtool_set_feature (s_ethtool,
+	                                NM_ETHTOOL_OPTNAME_FEATURE_LRO,
+	                                NM_TERNARY_FALSE);
+
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RX), ==, NM_TERNARY_TRUE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_SG),  ==, NM_TERNARY_DEFAULT);
+
+	nmtst_connection_normalize (con);
+
+	variant = nm_connection_to_dbus (con, NM_CONNECTION_SERIALIZE_ALL);
+
+	con2 = nm_simple_connection_new_from_dbus (variant, &error);
+	nmtst_assert_success (con2, error);
+
+	s_ethtool2 = NM_SETTING_ETHTOOL (nm_connection_get_setting (con2, NM_TYPE_SETTING_ETHTOOL));
+
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_RX),  ==, NM_TERNARY_TRUE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_SG),  ==, NM_TERNARY_DEFAULT);
+
+	nmtst_assert_connection_verifies_without_normalization (con2);
+
+	nmtst_assert_connection_equals (con, FALSE, con2, FALSE);
+
+	keyfile = nm_keyfile_write (con, NULL, NULL, &error);
+	nmtst_assert_success (keyfile, error);
+
+	con3 = nm_keyfile_read (keyfile,
+	                        "ethtool-keyfile-name",
+	                        NULL,
+	                        NULL,
+	                        NULL,
+	                        &error);
+	nmtst_assert_success (con3, error);
+
+	nmtst_connection_normalize (con3);
+
+	nmtst_assert_connection_equals (con, FALSE, con3, FALSE);
+
+	s_ethtool3 = NM_SETTING_ETHTOOL (nm_connection_get_setting (con3, NM_TYPE_SETTING_ETHTOOL));
+
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_RX),  ==, NM_TERNARY_TRUE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_SG),  ==, NM_TERNARY_DEFAULT);
+}
+
+/*****************************************************************************/
+
+static void
+test_sriov_vf (void)
+{
+	NMSriovVF *vf1, *vf2;
+	GError *error = NULL;
+	char *str;
+
+	vf1 = nm_sriov_vf_new (1);
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22:33:44:55"));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (100));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (500));
+
+	str = nm_utils_sriov_vf_to_str (vf1, FALSE, &error);
+	g_assert_no_error (error);
+	g_assert_cmpstr (str, ==, "1 mac=00:11:22:33:44:55 max-tx-rate=500 min-tx-rate=100 spoof-check=true trust=false");
+	g_free (str);
+
+	vf2 = nm_utils_sriov_vf_from_str (" 1  mac=00:11:22:33:44:55  max-tx-rate=500 min-tx-rate=100", &error);
+	nmtst_assert_success (vf2, error);
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (FALSE));
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE));
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (TRUE));
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, NULL);
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+
+	g_assert (nm_sriov_vf_equal (vf1, vf2));
+
+	nm_sriov_vf_unref (vf1);
+	nm_sriov_vf_unref (vf2);
+}
+
+static void
+test_sriov_vf_dup (void)
+{
+	NMSriovVF *vf1, *vf2;
+
+	vf1 = nm_sriov_vf_new (1);
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("foobar"));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (10));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (1000));
+	nm_sriov_vf_add_vlan (vf1, 80);
+	nm_sriov_vf_set_vlan_qos (vf1, 80, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+	vf2 = nm_sriov_vf_dup (vf1);
+	g_assert (nm_sriov_vf_equal (vf1, vf2));
+
+	nm_sriov_vf_unref (vf1);
+	nm_sriov_vf_unref (vf2);
+}
+
+static void
+test_sriov_vf_vlan (void)
+{
+	NMSriovVF *vf;
+	const guint *vlan_ids;
+	guint num;
+	GError *error = NULL;
+	gs_free char *str = NULL;
+
+	vf = nm_sriov_vf_new (19);
+	nm_sriov_vf_set_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22"));
+	g_assert (nm_sriov_vf_add_vlan (vf, 80));
+	g_assert (!nm_sriov_vf_add_vlan (vf, 80));
+	g_assert (nm_sriov_vf_add_vlan (vf, 82));
+	g_assert (nm_sriov_vf_add_vlan (vf, 83));
+	g_assert (nm_sriov_vf_add_vlan (vf, 81));
+	g_assert (!nm_sriov_vf_remove_vlan (vf, 100));
+	g_assert (nm_sriov_vf_remove_vlan (vf, 82));
+	nm_sriov_vf_set_vlan_qos (vf, 81, 0xabba);
+	nm_sriov_vf_set_vlan_protocol (vf, 81, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+	vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num);
+	g_assert (vlan_ids);
+	g_assert_cmpint (num, ==, 3);
+	g_assert_cmpint (vlan_ids[0], ==, 80);
+	g_assert_cmpint (vlan_ids[1], ==, 81);
+	g_assert_cmpint (vlan_ids[2], ==, 83);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 80), ==, 0x0);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 80), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 0xabba);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+	nm_sriov_vf_unref (vf);
+
+	vf = nm_utils_sriov_vf_from_str ("20 spoof-check=false vlans=85.0.q;4000.0x20.ad;81.10;83", &error);
+	nmtst_assert_success (vf, error);
+	vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num);
+	g_assert (vlan_ids);
+	g_assert_cmpint (num, ==, 4);
+	g_assert_cmpint (vlan_ids[0], ==, 81);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 10);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_assert_cmpint (vlan_ids[1], ==, 83);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 83), ==, 0);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 83), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_assert_cmpint (vlan_ids[2], ==, 85);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 85), ==, 0);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 85), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_assert_cmpint (vlan_ids[3], ==, 4000);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 4000), ==, 0x20);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 4000), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+	str = nm_utils_sriov_vf_to_str (vf, FALSE, &error);
+	nmtst_assert_success (str, error);
+	g_assert_cmpstr (str, ==, "20 spoof-check=false vlans=81.10;83;85;4000.32.ad");
+
+	nm_sriov_vf_unref (vf);
+}
+
+static void
+test_sriov_setting (void)
+{
+	gs_unref_object NMConnection *con = NULL;
+	NMSettingConnection *s_con;
+	NMSettingSriov *s_sriov = NULL;
+	NMSriovVF *vf1, *vf2, *vf3;
+	GError *error = NULL;
+	gboolean success;
+
+	con = nm_simple_connection_new ();
+
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	nm_connection_add_setting (con, NM_SETTING (s_con));
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test SR-IOV connection",
+	              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0",
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NULL);
+
+	nm_connection_add_setting (con, nm_setting_wired_new ());
+
+	s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
+	nm_connection_add_setting (con, NM_SETTING (s_sriov));
+
+	g_object_set (s_sriov, NM_SETTING_SRIOV_TOTAL_VFS, 16, NULL);
+	nm_setting_sriov_add_vf (s_sriov, (vf1 = nm_sriov_vf_new (0)));
+	nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (4)));
+	nm_setting_sriov_add_vf (s_sriov, (vf3 = nm_sriov_vf_new (10)));
+	g_assert (nm_setting_sriov_remove_vf_by_index (s_sriov, 4));
+	nm_sriov_vf_unref (vf2);
+	nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (2)));
+
+	nmtst_assert_connection_verifies_and_normalizable (con);
+	nmtst_connection_normalize (con);
+	success = nm_setting_verify ((NMSetting *) s_sriov, con, &error);
+	nmtst_assert_success (success, error);
+
+	g_assert_cmpint (nm_setting_sriov_get_num_vfs (s_sriov), ==, 3);
+	g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 0)), ==, 0);
+	g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 1)), ==, 2);
+	g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 2)), ==, 10);
+
+	nm_sriov_vf_unref (vf1);
+	nm_sriov_vf_unref (vf2);
+	nm_sriov_vf_unref (vf3);
+}
+
+typedef struct {
+	guint id;
+	guint qos;
+	bool  proto_ad;
+} VlanData;
+
+static void
+_test_sriov_parse_vlan_one (const char *string, gboolean exp_res, VlanData *data, guint data_length)
+{
+	NMSriovVF *vf;
+	gboolean res;
+	guint i, num_vlans;
+	const guint *vlan_ids;
+
+	vf = nm_sriov_vf_new (1);
+	g_assert (vf);
+
+	res = _nm_sriov_vf_parse_vlans (vf, string, NULL);
+	g_assert_cmpint (res, ==, exp_res);
+
+	if (exp_res) {
+		vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+		g_assert_cmpint (num_vlans, ==, data_length);
+		for (i = 0; i < num_vlans; i++) {
+			g_assert_cmpint (vlan_ids[i], ==, data[i].id);
+			g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]), ==, data[i].qos);
+			g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]),
+			                 ==,
+			                 data[i].proto_ad ? NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD: NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+		}
+	}
+
+	nm_sriov_vf_unref (vf);
+}
+
+#define test_sriov_parse_vlan_one(string, result, ...) \
+	{ \
+		VlanData _data[] = { __VA_ARGS__ }; \
+		guint _length = G_N_ELEMENTS (_data); \
+		\
+		_test_sriov_parse_vlan_one (string, result, _data, _length); \
+	}
+
+static void
+test_sriov_parse_vlans (void)
+{
+	test_sriov_parse_vlan_one ("",  FALSE, {});
+	test_sriov_parse_vlan_one ("1", TRUE, {1, 0, 0});
+	test_sriov_parse_vlan_one ("1;2", TRUE, {1, 0, 0}, {2, 0, 0});
+	test_sriov_parse_vlan_one ("4095;;2", TRUE, {2, 0, 0}, {4095, 0, 0});
+	test_sriov_parse_vlan_one ("1 2", FALSE, {});
+	test_sriov_parse_vlan_one ("4096", FALSE, {});
+	test_sriov_parse_vlan_one ("1.10", TRUE, {1, 10, 0});
+	test_sriov_parse_vlan_one ("1.20.ad", TRUE, {1, 20, 1});
+	test_sriov_parse_vlan_one ("1.21.q", TRUE, {1, 21, 0});
+	test_sriov_parse_vlan_one ("9.20.foo", FALSE, {});
+	test_sriov_parse_vlan_one ("1.20.ad.12", FALSE, {});
+	test_sriov_parse_vlan_one ("1;1.10", FALSE, {});
+	test_sriov_parse_vlan_one ("1..1;2", FALSE, {});
+	test_sriov_parse_vlan_one ("1..ad;2", FALSE, {});
+	test_sriov_parse_vlan_one ("1.2.ad;2.0.q;5;3", TRUE, {1, 2, 1}, {2, 0, 0}, {3, 0, 0}, {5, 0, 0});
+}
+
+/*****************************************************************************/
+
+static void
 test_tc_config_qdisc (void)
 {
 	NMTCQdisc *qdisc1, *qdisc2;
@@ -1669,6 +1974,14 @@ main (int argc, char **argv)
 	g_test_add_func ("/libnm/settings/dcb/priorities", test_dcb_priorities_valid);
 	g_test_add_func ("/libnm/settings/dcb/bandwidth-sums", test_dcb_bandwidth_sums);
 
+	g_test_add_func ("/libnm/settings/ethtool/1", test_ethtool_1);
+
+	g_test_add_func ("/libnm/settings/sriov/vf", test_sriov_vf);
+	g_test_add_func ("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup);
+	g_test_add_func ("/libnm/settings/sriov/vf-vlan", test_sriov_vf_vlan);
+	g_test_add_func ("/libnm/settings/sriov/setting", test_sriov_setting);
+	g_test_add_func ("/libnm/settings/sriov/vlans", test_sriov_parse_vlans);
+
 	g_test_add_func ("/libnm/settings/tc_config/qdisc", test_tc_config_qdisc);
 	g_test_add_func ("/libnm/settings/tc_config/action", test_tc_config_action);
 	g_test_add_func ("/libnm/settings/tc_config/tfilter", test_tc_config_tfilter);