Show changes to 5 files +380 −2
Makefile, t/helper/test-3071-wildmatch.c, t/t3071-wildmatch.sh, test-lib.c, test-lib.h
diff --git a/Makefile b/Makefile
index 2a81ae22e9..567387b558 100644
--- a/Makefile
+++ b/Makefile
@@ -644,6 +644,7 @@ X =
PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
+TEST_PROGRAMS_NEED_X += test-3071-wildmatch
TEST_PROGRAMS_NEED_X += test-chmtime
TEST_PROGRAMS_NEED_X += test-ctype
TEST_PROGRAMS_NEED_X += test-config
@@ -895,6 +896,7 @@ LIB_OBJS += sub-process.o
LIB_OBJS += symlinks.o
LIB_OBJS += tag.o
LIB_OBJS += tempfile.o
+LIB_OBJS += test-lib.o
LIB_OBJS += tmp-objdir.o
LIB_OBJS += trace.o
LIB_OBJS += trailer.o
diff --git a/t/helper/test-3071-wildmatch.c b/t/helper/test-3071-wildmatch.c
new file mode 100644
index 0000000000..24a657202d
--- /dev/null
+++ b/t/helper/test-3071-wildmatch.c
@@ -0,0 +1,273 @@
+#include "cache.h"
+#include "test-lib.h"
+
+struct match_input {
+ int expect_true;
+ const char *text;
+ const char *pattern;
+};
+
+static struct match_input match_tests[] = {
+ /* Basic wildmatch features */
+ { 1, "foo", "foo" },
+ { 0, "foo", "bar" },
+ { 1, "", "" },
+ { 1, "foo", "???" },
+ { 0, "foo", "??" },
+ { 1, "foo", "*" },
+ { 1, "foo", "f*" },
+ { 0, "foo", "*f" },
+ { 1, "foo", "*foo*" },
+ { 1, "foobar", "*ob*a*r*" },
+ { 1, "aaaaaaabababab", "*ab" },
+ { 1, "foo*", "foo\\*" },
+ { 0, "foobar", "foo\\*bar" },
+ { 1, "f\\oo", "f\\\\oo" },
+ { 1, "ball", "*[al]?" },
+ { 0, "ten", "[ten]" },
+ { 0, "ten", "**[!te]" },
+ { 0, "ten", "**[!ten]" },
+ { 1, "ten", "t[a-g]n" },
+ { 0, "ten", "t[!a-g]n" },
+ { 1, "ton", "t[!a-g]n" },
+ { 1, "ton", "t[^a-g]n" },
+ { 1, "a]b", "a[]]b" },
+ { 1, "a-b", "a[]-]b" },
+ { 1, "a]b", "a[]-]b" },
+ { 0, "aab", "a[]-]b" },
+ { 1, "aab", "a[]a-]b" },
+ { 1, "]", "]" },
+
+ /* Extended slash-matching features */
+ { 0, "foo/baz/bar", "foo*bar" },
+ { 0, "foo/baz/bar", "foo**bar" },
+ { 0, "foobazbar", "foo**bar" },
+ { 1, "foo/baz/bar", "foo/**/bar" },
+ { 1, "foo/baz/bar", "foo/**/**/bar" },
+ { 1, "foo/b/a/z/bar", "foo/**/bar" },
+ { 1, "foo/b/a/z/bar", "foo/**/**/bar" },
+ { 1, "foo/bar", "foo/**/bar" },
+ { 1, "foo/bar", "foo/**/**/bar" },
+ { 0, "foo/bar", "foo?bar" },
+ { 0, "foo/bar", "foo[/]bar" },
+ { 0, "foo/bar", "foo[^a-z]bar" },
+ { 0, "foo/bar", "f[^eiu][^eiu][^eiu][^eiu][^eiu]r" },
+ { 1, "foo-bar", "f[^eiu][^eiu][^eiu][^eiu][^eiu]r" },
+ { 1, "foo", "**/foo" },
+ { 1, "XXX/foo", "**/foo" },
+ { 1, "bar/baz/foo", "**/foo" },
+ { 0, "bar/baz/foo", "*/foo" },
+ { 0, "foo/bar/baz", "**/bar*" },
+ { 1, "deep/foo/bar/baz", "**/bar/*" },
+ { 0, "deep/foo/bar/baz/", "**/bar/*" },
+ { 1, "deep/foo/bar/baz/", "**/bar/**" },
+ { 0, "deep/foo/bar", "**/bar/*" },
+ { 1, "deep/foo/bar/", "**/bar/**" },
+ { 0, "foo/bar/baz", "**/bar**" },
+ { 1, "foo/bar/baz/x", "*/bar/**" },
+ { 0, "deep/foo/bar/baz/x", "*/bar/**" },
+ { 1, "deep/foo/bar/baz/x", "**/bar/*/*" },
+
+ /* Various additional tests */
+ { 0, "acrt", "a[c-c]st" },
+ { 1, "acrt", "a[c-c]rt" },
+ { 0, "]", "[!]-]" },
+ { 1, "a", "[!]-]" },
+ { 0, "", "\\" },
+ { 0, "\\", "\\" },
+ { 0, "XXX/\\", "*/\\" },
+ { 1, "XXX/\\", "*/\\\\" },
+ { 1, "foo", "foo" },
+ { 1, "@foo", "@foo" },
+ { 0, "foo", "@foo" },
+ { 1, "[ab]", "\\[ab]" },
+ { 1, "[ab]", "[[]ab]" },
+ { 1, "[ab]", "[[:]ab]" },
+ { 0, "[ab]", "[[::]ab]" },
+ { 1, "[ab]", "[[:digit]ab]" },
+ { 1, "[ab]", "[\\[:]ab]" },
+ { 1, "?a?b", "\\??\\?b" },
+ { 1, "abc", "\\a\\b\\c" },
+ { 0, "foo", "" },
+ { 1, "foo/bar/baz/to", "**/t[o]" },
+
+ /* Character class tests */
+ { 1, "a1B", "[[:alpha:]][[:digit:]][[:upper:]]" },
+ { 0, "a", "[[:digit:][:upper:][:space:]]" },
+ { 1, "A", "[[:digit:][:upper:][:space:]]" },
+ { 1, "1", "[[:digit:][:upper:][:space:]]" },
+ { 0, "1", "[[:digit:][:upper:][:spaci:]]" },
+ { 1, " ", "[[:digit:][:upper:][:space:]]" },
+ { 0, ".", "[[:digit:][:upper:][:space:]]" },
+ { 1, ".", "[[:digit:][:punct:][:space:]]" },
+ { 1, "5", "[[:xdigit:]]" },
+ { 1, "f", "[[:xdigit:]]" },
+ { 1, "D", "[[:xdigit:]]" },
+ { 1, "_", "[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]" },
+ { 1, ".", "[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]" },
+ { 1, "5", "[a-c[:digit:]x-z]" },
+ { 1, "b", "[a-c[:digit:]x-z]" },
+ { 1, "y", "[a-c[:digit:]x-z]" },
+ { 0, "q", "[a-c[:digit:]x-z]" },
+
+ /* Additional tests, including some malformed wildmats */
+ { 1, "]", "[\\\\-^]" },
+ { 0, "[", "[\\\\-^]" },
+ { 1, "-", "[\\-_]" },
+ { 1, "]", "[\\]]" },
+ { 0, "\\]", "[\\]]" },
+ { 0, "\\", "[\\]]" },
+ { 0, "ab", "a[]b" },
+ { 0, "a[]b", "a[]b" },
+ { 0, "ab[", "ab[" },
+ { 0, "ab", "[!" },
+ { 0, "ab", "[-" },
+ { 1, "-", "[-]" },
+ { 0, "-", "[a-" },
+ { 0, "-", "[!a-" },
+ { 1, "-", "[--A]" },
+ { 1, "5", "[--A]" },
+ { 1, " ", "[ --]" },
+ { 1, "$", "[ --]" },
+ { 1, "-", "[ --]" },
+ { 0, "0", "[ --]" },
+ { 1, "-", "[---]" },
+ { 1, "-", "[------]" },
+ { 0, "j", "[a-e-n]" },
+ { 1, "-", "[a-e-n]" },
+ { 1, "a", "[!------]" },
+ { 0, "[", "[]-a]" },
+ { 1, "^", "[]-a]" },
+ { 0, "^", "[!]-a]" },
+ { 1, "[", "[!]-a]" },
+ { 1, "^", "[a^bc]" },
+ { 1, "-b]", "[a-]b]" },
+ { 0, "\\", "[\\]" },
+ { 1, "\\", "[\\\\]" },
+ { 0, "\\", "[!\\\\]" },
+ { 1, "G", "[A-\\\\]" },
+ { 0, "aaabbb", "b*a" },
+ { 0, "aabcaa", "*ba*" },
+ { 1, ",", "[,]" },
+ { 1, ",", "[\\\\,]" },
+ { 1, "\\", "[\\\\,]" },
+ { 1, "-", "[,-.]" },
+ { 0, "+", "[,-.]" },
+ { 0, "-.]", "[,-.]" },
+ { 1, "2", "[\\1-\\3]" },
+ { 1, "3", "[\\1-\\3]" },
+ { 0, "4", "[\\1-\\3]" },
+ { 1, "\\", "[[-\\]]" },
+ { 1, "[", "[[-\\]]" },
+ { 1, "]", "[[-\\]]" },
+ { 0, "-", "[[-\\]]" },
+
+ /* Test recursion and the abort code (use "wildtest -i" to see iteration counts) */
+ { 1, "-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*" },
+ { 0, "-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*" },
+ { 0, "-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*" },
+ { 1, "XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1", "XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*" },
+ { 0, "XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1", "XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*" },
+ { 1, "abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt", "**/*a*b*g*n*t" },
+ { 0, "abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz", "**/*a*b*g*n*t" },
+ { 0, "foo", "*/*/*" },
+ { 0, "foo/bar", "*/*/*" },
+ { 1, "foo/bba/arr", "*/*/*" },
+ { 0, "foo/bb/aa/rr", "*/*/*" },
+ { 1, "foo/bb/aa/rr", "**/**/**" },
+ { 1, "abcXdefXghi", "*X*i" },
+ { 0, "ab/cXd/efXg/hi", "*X*i" },
+ { 1, "ab/cXd/efXg/hi", "*/*X*/*/*i" },
+ { 1, "ab/cXd/efXg/hi", "**/*X*/**/*i" },
+
+ /* Case-sensitivity features */
+ { 0, "a", "[A-Z]" },
+ { 1, "A", "[A-Z]" },
+ { 0, "A", "[a-z]" },
+ { 1, "a", "[a-z]" },
+ { 0, "a", "[[:upper:]]" },
+ { 1, "A", "[[:upper:]]" },
+ { 0, "A", "[[:lower:]]" },
+ { 1, "a", "[[:lower:]]" },
+ { 0, "A", "[B-Za]" },
+ { 1, "a", "[B-Za]" },
+ { 0, "A", "[B-a]" },
+ { 1, "a", "[B-a]" },
+ { 0, "z", "[Z-y]" },
+ { 1, "Z", "[Z-y]" },
+};
+
+static struct match_input pathmatch_tests[] = {
+ { 1, "foo", "foo" },
+ { 0, "foo", "fo" },
+ { 1, "foo/bar", "foo/bar" },
+ { 1, "foo/bar", "foo/*" },
+ { 1, "foo/bba/arr", "foo/*" },
+ { 1, "foo/bba/arr", "foo/**" },
+ { 1, "foo/bba/arr", "foo*" },
+ { 1, "foo/bba/arr", "foo**" },
+ { 1, "foo/bba/arr", "foo/*arr" },
+ { 1, "foo/bba/arr", "foo/**arr" },
+ { 0, "foo/bba/arr", "foo/*z" },
+ { 0, "foo/bba/arr", "foo/**z" },
+ { 1, "foo/bar", "foo?bar" },
+ { 1, "foo/bar", "foo[/]bar" },
+ { 1, "foo/bar", "foo[^a-z]bar" },
+ { 0, "foo", "*/*/*" },
+ { 0, "foo/bar", "*/*/*" },
+ { 1, "foo/bba/arr", "*/*/*" },
+ { 1, "foo/bb/aa/rr", "*/*/*" },
+ { 1, "abcXdefXghi", "*X*i" },
+ { 1, "ab/cXd/efXg/hi", "*/*X*/*/*i" },
+ { 1, "ab/cXd/efXg/hi", "*Xg*i" },
+};
+
+static struct match_input icase_match_tests[] = {
+ { 1, "a", "[A-Z]" },
+ { 1, "A", "[A-Z]" },
+ { 1, "A", "[a-z]" },
+ { 1, "a", "[a-z]" },
+ { 1, "a", "[[:upper:]]" },
+ { 1, "A", "[[:upper:]]" },
+ { 1, "A", "[[:lower:]]" },
+ { 1, "a", "[[:lower:]]" },
+ { 1, "A", "[B-Za]" },
+ { 1, "a", "[B-Za]" },
+ { 1, "A", "[B-a]" },
+ { 1, "a", "[B-a]" },
+ { 1, "z", "[Z-y]" },
+ { 1, "Z", "[Z-y]" },
+};
+
+static void test_match(const char *name,
+ const struct match_input *input,
+ unsigned flags)
+{
+ int ret;
+
+ start_test("%s: %s match '%s' '%s'",
+ name,
+ input->expect_true ? " " : "no",
+ input->text, input->pattern);
+ ret = wildmatch(input->pattern, input->text, flags);
+ end_test(input->expect_true ? ret == 0 : ret != 0);
+}
+
+int cmd_main(int ac, const char **av)
+{
+ int i;
+
+ init_test_suite(ac, av);
+
+ for (i = 0; i < ARRAY_SIZE(match_tests); i++)
+ test_match("wildmatch", match_tests + i, WM_PATHNAME);
+
+ for (i = 0; i < ARRAY_SIZE(pathmatch_tests); i++)
+ test_match("pathmatch", pathmatch_tests + i, 0);
+
+ for (i = 0; i < ARRAY_SIZE(icase_match_tests); i++)
+ test_match("iwildmatch", icase_match_tests + i,
+ WM_PATHNAME | WM_CASEFOLD);
+
+ all_tests_done();
+}
diff --git a/t/t3071-wildmatch.sh b/t/t3071-wildmatch.sh
new file mode 100755
index 0000000000..6e83b4d684
--- /dev/null
+++ b/t/t3071-wildmatch.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec helper/test-3071-wildmatch t3071-wildmatch "$@"
diff --git a/test-lib.c b/test-lib.c
new file mode 100644
index 0000000000..8e8b7cd6df
--- /dev/null
+++ b/test-lib.c
@@ -0,0 +1,97 @@
+#include "cache.h"
+#include "test-lib.h"
+
+static int test_failure;
+static int test_count;
+static int test_fixed;
+static int test_broken;
+static int test_success;
+
+static const char *suite_name;
+static int harness_active;
+static char *test_name;
+
+void init_test_suite(int ac, const char **av)
+{
+ char cwd[PATH_MAX];
+
+ harness_active = getenv("HARNESS_ACTIVE") != NULL;
+ suite_name = av[1];
+
+ getcwd(cwd, sizeof(cwd));
+ setenv("TEST_DIRECTORY", cwd, 0);
+ setenv("TEST_OUTPUT_DIRECTORY", getenv("TEST_DIRECTORY"), 0);
+
+ ac--;
+ av++;
+
+ dup2(1, 3);
+}
+
+static int skip_test(void)
+{
+ return 0; /* not supported yet */
+}
+
+int start_test(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ test_name = xstrvfmt(fmt, ap);
+ va_end(ap);
+ test_count++;
+
+ if (skip_test())
+ return 0;
+
+ return 1;
+}
+
+void end_test(int ok)
+{
+ if (ok) {
+ test_success++;
+ dprintf(3, "ok %d - %s\n", test_count, test_name);
+ } else {
+ test_failure++;
+ dprintf(3, "not ok %d - %s\n", test_count, test_name);
+ }
+
+ free(test_name);
+ test_name = NULL;
+
+ /* -i not supported yet */
+}
+
+void all_tests_done(void)
+{
+ if (!getenv("HARNESS_ACTIVE")) {
+ struct strbuf sb = STRBUF_INIT;
+ FILE *fp;
+
+ strbuf_addf(&sb, "mkdir -p %s/test-results",
+ getenv("TEST_OUTPUT_DIRECTORY"));
+ system(sb.buf);
+ strbuf_release(&sb);
+
+ strbuf_addf(&sb, "%s/test-results/%s.counts",
+ getenv("TEST_OUTPUT_DIRECTORY"),
+ suite_name);
+ fp = fopen(sb.buf, "w");
+ strbuf_release(&sb);
+
+ fprintf(fp, "total %d\nsuccess %d\n"
+ "fixed %d\nbroken %d\nfailed %d\n\n",
+ test_count, test_success,
+ test_fixed, test_broken, test_failure);
+ fclose(fp);
+ }
+ if (test_failure) {
+ dprintf(3, "# failed %d among FIXME\n", test_failure);
+ } else {
+ dprintf(3, "# passed all FIXME\n");
+ }
+ dprintf(3, "1..%d\n", test_count);
+ exit(0);
+}
diff --git a/test-lib.h b/test-lib.h
new file mode 100644
index 0000000000..f8122c1719
--- /dev/null
+++ b/test-lib.h
@@ -0,0 +1,5 @@
+void init_test_suite(int ac, const char **av);
+void all_tests_done(void) NORETURN;
+
+int start_test(const char *fmt, ...);
+void end_test(int ok);
-- 8< --
--
Duy