testsuite: improve coverage of shared/util.h

Add tests to improve coverage of shared/util.h functions.
diff --git a/testsuite/rootfs-pristine/test-util2/write-str-safe-correct.txt b/testsuite/rootfs-pristine/test-util2/write-str-safe-correct.txt
new file mode 100644
index 0000000..30d74d2
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-util2/write-str-safe-correct.txt
@@ -0,0 +1 @@
+test
\ No newline at end of file
diff --git a/testsuite/test-util.c b/testsuite/test-util.c
index 5fa8c44..b0c4ce6 100644
--- a/testsuite/test-util.c
+++ b/testsuite/test-util.c
@@ -16,9 +16,14 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <fcntl.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #include <shared/util.h>
 
@@ -96,4 +101,110 @@
 		.out = TESTSUITE_ROOTFS "test-util/freadline_wrapped-correct.txt",
 	});
 
+static int test_strchr_replace(const struct test *t)
+{
+	_cleanup_free_ char *s = strdup("this is a test string");
+	const char *res = "thiC iC a teCt Ctring";
+
+	strchr_replace(s, 's', 'C');
+	assert_return(streq(s, res), EXIT_FAILURE);
+
+	return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_strchr_replace,
+	.description = "check implementation of strchr_replace()",
+	.need_spawn = false,
+	);
+
+static int test_underscores(const struct test *t)
+{
+	struct teststr {
+		char *val;
+		const char *res;
+	} teststr[] = {
+		{ strdup("aa-bb-cc_"), "aa_bb_cc_" },
+		{ strdup("-aa-bb-cc-"), "_aa_bb_cc_" },
+		{ strdup("-aa[-bb-]cc-"), "_aa[-bb-]cc_" },
+		{ strdup("-aa-[bb]-cc-"), "_aa_[bb]_cc_" },
+		{ strdup("-aa-[b-b]-cc-"), "_aa_[b-b]_cc_" },
+		{ strdup("-aa-b[-]b-cc"), "_aa_b[-]b_cc" },
+		{ }
+	}, *iter;
+
+	for (iter = &teststr[0]; iter->val != NULL; iter++) {
+		_cleanup_free_ char *val = iter->val;
+		underscores(val);
+		assert_return(streq(val, iter->res), EXIT_FAILURE);
+	}
+
+	return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_underscores,
+	.description = "check implementation of underscores()",
+	.need_spawn = false,
+	);
+
+static int test_path_ends_with_kmod_ext(const struct test *t)
+{
+	struct teststr {
+		const char *val;
+		bool res;
+	} teststr[] = {
+		{ "/bla.ko", true },
+#ifdef ENABLE_ZLIB
+		{ "/bla.ko.gz", true },
+#endif
+#ifdef ENABLE_XZ
+		{ "/bla.ko.xz", true },
+#endif
+		{ "/bla.ko.x", false },
+		{ "/bla.ko.", false },
+		{ "/bla.koz", false },
+		{ "/b", false },
+		{ }
+	}, *iter;
+
+	for (iter = &teststr[0]; iter->val != NULL; iter++) {
+		assert_return(path_ends_with_kmod_ext(iter->val,
+						      strlen(iter->val)) == iter->res,
+			      EXIT_FAILURE);
+	}
+
+	return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_path_ends_with_kmod_ext,
+	.description = "check implementation of path_ends_with_kmod_ext()",
+	.need_spawn = false,
+	);
+
+#define TEST_WRITE_STR_SAFE_FILE "/write-str-safe"
+#define TEST_WRITE_STR_SAFE_PATH TESTSUITE_ROOTFS "test-util2/" TEST_WRITE_STR_SAFE_FILE
+static int test_write_str_safe(const struct test *t)
+{
+	const char *s = "test";
+	int fd;
+
+	fd = open(TEST_WRITE_STR_SAFE_FILE ".txt", O_CREAT|O_TRUNC|O_WRONLY, 0644);
+	assert_return(fd >= 0, EXIT_FAILURE);
+
+	write_str_safe(fd, s, strlen(s));
+	close(fd);
+
+	return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_write_str_safe,
+	.description = "check implementation of write_str_safe()",
+	.config = {
+		[TC_ROOTFS] = TESTSUITE_ROOTFS "test-util2/",
+	},
+	.need_spawn = true,
+	.output = {
+		.files = (const struct keyval[]) {
+			{ TEST_WRITE_STR_SAFE_PATH ".txt",
+			  TEST_WRITE_STR_SAFE_PATH "-correct.txt" },
+			{ }
+		},
+	});
+
+
 TESTSUITE_MAIN();