From 4e828b7230d2d8dfebec87aa9d262ee10b8e45a4 Mon Sep 17 00:00:00 2001
From: James Y Knight <jyknight@google.com>
Date: Mon, 22 Jul 2019 20:25:38 -0400
Subject: [PATCH] Add some basic tests for glob.

---
 src/functional/glob.c                         | 135 ++++++++++++++++++
 src/functional/glob.data/basic/dir/file       |   0
 src/functional/glob.data/basic/file           |   0
 src/functional/glob.data/basic/symlink-dir    |   1 +
 src/functional/glob.data/basic/symlink-file   |   1 +
 .../glob.data/broken-links/broken-symlink     |   1 +
 src/functional/glob.data/weird-chars/foo-[a]  |   0
 src/functional/glob.data/weird-chars/foo-a    |   0
 8 files changed, 138 insertions(+)
 create mode 100644 src/functional/glob.c
 create mode 100644 src/functional/glob.data/basic/dir/file
 create mode 100644 src/functional/glob.data/basic/file
 create mode 120000 src/functional/glob.data/basic/symlink-dir
 create mode 120000 src/functional/glob.data/basic/symlink-file
 create mode 120000 src/functional/glob.data/broken-links/broken-symlink
 create mode 100644 src/functional/glob.data/weird-chars/foo-[a]
 create mode 100644 src/functional/glob.data/weird-chars/foo-a

diff --git a/src/functional/glob.c b/src/functional/glob.c
new file mode 100644
index 0000000..69ffea5
--- /dev/null
+++ b/src/functional/glob.c
@@ -0,0 +1,135 @@
+#include <string.h>
+#include <stdlib.h>
+#include <glob.h>
+#include <stdio.h>
+
+#include "test.h"
+
+glob_t globbuf;
+
+int paths_match(int expected_num, const char **expected) {
+	if (globbuf.gl_pathc + globbuf.gl_offs != expected_num)
+		return 0;
+	for (int i = 0; i < expected_num; ++i) {
+		if ((globbuf.gl_pathv[i] == NULL) != (expected[i] == NULL))
+			return 0;
+		if (expected[i] && strcmp(globbuf.gl_pathv[i], expected[i]) != 0)
+			return 0;
+	}
+	return 1;
+}
+
+void print_mismatch(int expected_num, const char **expected) {
+	t_printf("Expected paths (%d):\n", expected_num);
+	for (int i = 0; i < expected_num; ++i)
+		t_printf("  %s\n", expected[i] ? expected[i] : "NULL");
+	t_printf("Got paths (%d):\n", globbuf.gl_pathc + globbuf.gl_offs);
+	for (int i = 0; i < globbuf.gl_pathc + globbuf.gl_offs; ++i)
+		t_printf("  %s\n", globbuf.gl_pathv ? globbuf.gl_pathv[i] : "NULL");
+}
+
+#define TEST_NOFREE(pat, flags, expected_ret, ...) do {			\
+	int tflags = flags|extraflags;					\
+	int ret = glob(pat, tflags, NULL, &globbuf);			\
+	const char *e[] = __VA_ARGS__;					\
+	if (ret != expected_ret)					\
+		t_error("glob(\"%s\", %d) failed (return %d, expected %d)\n", \
+			pat, tflags, ret, expected_ret);		\
+	if (!paths_match(sizeof(e)/sizeof(*e), e)) {			\
+		t_error("glob(\"%s\", %d) returned unexpected paths.\n", \
+			pat, tflags);					\
+		print_mismatch(sizeof(e)/sizeof(*e), e);		\
+	}								\
+} while(0)
+
+#define TEST(pat, flags, expected_ret, ...) do {		\
+	TEST_NOFREE(pat, flags, expected_ret, __VA_ARGS__);	\
+	globfree(&globbuf);					\
+} while(0)
+
+#define TEST_UNMATCH(pat) do {					\
+	if (extraflags & GLOB_NOCHECK)				\
+		TEST(pat, extraflags, 0, {pat});		\
+	else							\
+		TEST(pat, extraflags, GLOB_NOMATCH, {});	\
+} while(0)
+
+#define MARK(str) ((extraflags & GLOB_MARK) ? str "/" : str)
+
+static void do_tests(int extraflags) {
+	// Check basic functioning of paths
+	TEST("basic/file", 0,
+	     0, {"basic/file"});
+	TEST("basic/symlink-file", 0,
+	     0, {"basic/symlink-file"});
+	TEST("basic/dir", 0,
+	     0, {MARK("basic/dir")});
+	TEST("basic/symlink-dir", 0,
+	     0, {MARK("basic/symlink-dir")});
+	// With trailing slash input, always returns a trailing slash
+	TEST("basic/dir/", 0,
+	     0, {"basic/dir/"});
+
+	// And patterns...
+	TEST("basic/*", 0,
+	     0, {MARK("basic/dir"), "basic/file", MARK("basic/symlink-dir"), "basic/symlink-file"});
+	TEST("basic/d?r", 0,
+	     0, {MARK("basic/dir")});
+	TEST("basic/[df]??", 0,
+	     0, {MARK("basic/dir")});
+	TEST("basic/fi[l]e", 0,
+	     0, {"basic/file"});
+	TEST("basic/*/", 0,
+	     0, {"basic/dir/", "basic/symlink-dir/"});
+	TEST("basic/*/*", 0,
+	     0, {"basic/dir/file", "basic/symlink-dir/file"});
+
+	TEST("weird-chars/foo-[a]", 0,
+	     0, {"weird-chars/foo-a"});
+	TEST("weird-chars/foo-\\[a\\]", 0,
+	     0, {"weird-chars/foo-[a]"});
+
+	// Non-matching patterns
+	TEST_UNMATCH("basic/not-there");
+	TEST_UNMATCH("basic/not-there*");
+	TEST_UNMATCH("basic/not-there/*");
+	// A file can't be specified with a trailing slash
+	TEST_UNMATCH("basic/file/");
+
+	// Check GLOB_APPEND, GLOB_DOOFFS, and both together
+	globbuf.gl_offs = 2; // should be ignored without GLOB_DOOFFS
+	TEST_NOFREE("basic/file", 0,
+		    0, {"basic/file"});
+	TEST("basic/dir", GLOB_APPEND,
+	     0, {"basic/file", MARK("basic/dir")});
+
+	globbuf.gl_offs = 2;
+	TEST_NOFREE("basic/file", GLOB_DOOFFS,
+		    0, {NULL, NULL, "basic/file"});
+	TEST("basic/dir", GLOB_APPEND|GLOB_DOOFFS,
+	     0, {NULL, NULL, "basic/file", MARK("basic/dir")});
+
+	// Check proper support for broken symlinks. Both completely-specified,
+	// and pattern matched.
+	TEST("broken-links/broken-symlink", 0,
+	     0, {"broken-links/broken-symlink"});
+
+	TEST("broken-links/*", 0,
+	     0, {"broken-links/broken-symlink"});
+}
+
+
+int main(int argc, char **argv)
+{
+	char buf[512];
+	if (!t_pathrel(buf, sizeof buf, argv[0], "glob.data")) {
+		t_error("failed to obtain relative path to glob data\n");
+		return 1;
+	}
+	chdir(buf);
+
+	do_tests(0);
+	do_tests(GLOB_MARK);
+	do_tests(GLOB_NOCHECK);
+	return t_status;
+}
diff --git a/src/functional/glob.data/basic/dir/file b/src/functional/glob.data/basic/dir/file
new file mode 100644
index 0000000..e69de29
diff --git a/src/functional/glob.data/basic/file b/src/functional/glob.data/basic/file
new file mode 100644
index 0000000..e69de29
diff --git a/src/functional/glob.data/basic/symlink-dir b/src/functional/glob.data/basic/symlink-dir
new file mode 120000
index 0000000..8724519
--- /dev/null
+++ b/src/functional/glob.data/basic/symlink-dir
@@ -0,0 +1 @@
+dir
\ No newline at end of file
diff --git a/src/functional/glob.data/basic/symlink-file b/src/functional/glob.data/basic/symlink-file
new file mode 120000
index 0000000..1a010b1
--- /dev/null
+++ b/src/functional/glob.data/basic/symlink-file
@@ -0,0 +1 @@
+file
\ No newline at end of file
diff --git a/src/functional/glob.data/broken-links/broken-symlink b/src/functional/glob.data/broken-links/broken-symlink
new file mode 120000
index 0000000..456794e
--- /dev/null
+++ b/src/functional/glob.data/broken-links/broken-symlink
@@ -0,0 +1 @@
+no-such-file
\ No newline at end of file
diff --git a/src/functional/glob.data/weird-chars/foo-[a] b/src/functional/glob.data/weird-chars/foo-[a]
new file mode 100644
index 0000000..e69de29
diff --git a/src/functional/glob.data/weird-chars/foo-a b/src/functional/glob.data/weird-chars/foo-a
new file mode 100644
index 0000000..e69de29
-- 
2.22.0.657.g960e92d24f-goog

