From e6e7530d10b74d763b4311ea93e0a831b810d6c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Wed, 13 Apr 2016 20:22:42 +0700 Subject: test helpers: move test-* to t/helper/ subdirectory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This keeps top dir a bit less crowded. And because these programs are for testing purposes, it makes sense that they stay somewhere in t/ Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index 5087ce1..05cb58a 100644 --- a/.gitignore +++ b/.gitignore @@ -179,39 +179,6 @@ /gitweb/gitweb.cgi /gitweb/static/gitweb.js /gitweb/static/gitweb.min.* -/test-chmtime -/test-ctype -/test-config -/test-date -/test-delta -/test-dump-cache-tree -/test-dump-split-index -/test-dump-untracked-cache -/test-fake-ssh -/test-scrap-cache-tree -/test-genrandom -/test-hashmap -/test-index-version -/test-line-buffer -/test-match-trees -/test-mergesort -/test-mktemp -/test-parse-options -/test-path-utils -/test-prio-queue -/test-read-cache -/test-regex -/test-revision-walking -/test-run-command -/test-sha1 -/test-sha1-array -/test-sigchain -/test-string-list -/test-submodule-config -/test-subprocess -/test-svn-fe -/test-urlmatch-normalization -/test-wildmatch /common-cmds.h *.tar.gz *.dsc diff --git a/Makefile b/Makefile index 0bed5cb..69d32bf 100644 --- a/Makefile +++ b/Makefile @@ -624,7 +624,7 @@ TEST_PROGRAMS_NEED_X += test-svn-fe TEST_PROGRAMS_NEED_X += test-urlmatch-normalization TEST_PROGRAMS_NEED_X += test-wildmatch -TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X)) +TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X)) # List built-in command $C whose implementation cmd_$C() is not in # builtin/$C.o but is linked in as part of some other command. @@ -1904,7 +1904,7 @@ VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o -TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS)) +TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(VCSSVN_OBJS) \ @@ -2211,7 +2211,7 @@ bin-wrappers/%: wrap-for-bin.sh @mkdir -p bin-wrappers $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's|@@BUILD_DIR@@|$(shell pwd)|' \ - -e 's|@@PROG@@|$(@F)|' < $< > $@ && \ + -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%,$(@F))|' < $< > $@ && \ chmod +x $@ # GNU make supports exporting all variables by "export" without parameters. @@ -2231,25 +2231,25 @@ perf: all .PHONY: test perf -test-ctype$X: ctype.o +t/helper/test-ctype$X: ctype.o -test-date$X: date.o ctype.o +t/helper/test-date$X: date.o ctype.o -test-delta$X: diff-delta.o patch-delta.o +t/helper/test-delta$X: diff-delta.o patch-delta.o -test-line-buffer$X: vcs-svn/lib.a +t/helper/test-line-buffer$X: vcs-svn/lib.a -test-parse-options$X: parse-options.o parse-options-cb.o +t/helper/test-parse-options$X: parse-options.o parse-options-cb.o -test-svn-fe$X: vcs-svn/lib.a +t/helper/test-svn-fe$X: vcs-svn/lib.a .PRECIOUS: $(TEST_OBJS) -test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS) +t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) -check-sha1:: test-sha1$X - ./test-sha1.sh +check-sha1:: t/helper/test-sha1$X + t/helper/test-sha1.sh SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ)) diff --git a/t/helper/.gitignore b/t/helper/.gitignore new file mode 100644 index 0000000..d6e8b36 --- /dev/null +++ b/t/helper/.gitignore @@ -0,0 +1,33 @@ +/test-chmtime +/test-ctype +/test-config +/test-date +/test-delta +/test-dump-cache-tree +/test-dump-split-index +/test-dump-untracked-cache +/test-fake-ssh +/test-scrap-cache-tree +/test-genrandom +/test-hashmap +/test-index-version +/test-line-buffer +/test-match-trees +/test-mergesort +/test-mktemp +/test-parse-options +/test-path-utils +/test-prio-queue +/test-read-cache +/test-regex +/test-revision-walking +/test-run-command +/test-sha1 +/test-sha1-array +/test-sigchain +/test-string-list +/test-submodule-config +/test-subprocess +/test-svn-fe +/test-urlmatch-normalization +/test-wildmatch diff --git a/t/helper/test-chmtime.c b/t/helper/test-chmtime.c new file mode 100644 index 0000000..dfe8a83 --- /dev/null +++ b/t/helper/test-chmtime.c @@ -0,0 +1,119 @@ +/* + * This program can either change modification time of the given + * file(s) or just print it. The program does not change atime or + * ctime (their values are explicitly preserved). + * + * The mtime can be changed to an absolute value: + * + * test-chmtime = file... + * + * Relative to the current time as returned by time(3): + * + * test-chmtime =+ (or =-) file... + * + * Or relative to the current mtime of the file: + * + * test-chmtime file... + * test-chmtime + (or -) file... + * + * Examples: + * + * To just print the mtime use --verbose and set the file mtime offset to 0: + * + * test-chmtime -v +0 file + * + * To set the mtime to current time: + * + * test-chmtime =+0 file + * + */ +#include "git-compat-util.h" +#include + +static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-) ..."; + +static int timespec_arg(const char *arg, long int *set_time, int *set_eq) +{ + char *test; + const char *timespec = arg; + *set_eq = (*timespec == '=') ? 1 : 0; + if (*set_eq) { + timespec++; + if (*timespec == '+') { + *set_eq = 2; /* relative "in the future" */ + timespec++; + } + } + *set_time = strtol(timespec, &test, 10); + if (*test) { + fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1); + return 0; + } + if ((*set_eq && *set_time < 0) || *set_eq == 2) { + time_t now = time(NULL); + *set_time += now; + } + return 1; +} + +int main(int argc, char *argv[]) +{ + static int verbose; + + int i = 1; + /* no mtime change by default */ + int set_eq = 0; + long int set_time = 0; + + if (argc < 3) + goto usage; + + if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) { + verbose = 1; + ++i; + } + if (timespec_arg(argv[i], &set_time, &set_eq)) + ++i; + else + goto usage; + + for (; i < argc; i++) { + struct stat sb; + struct utimbuf utb; + + if (stat(argv[i], &sb) < 0) { + fprintf(stderr, "Failed to stat %s: %s\n", + argv[i], strerror(errno)); + return 1; + } + +#ifdef GIT_WINDOWS_NATIVE + if (!(sb.st_mode & S_IWUSR) && + chmod(argv[i], sb.st_mode | S_IWUSR)) { + fprintf(stderr, "Could not make user-writable %s: %s", + argv[i], strerror(errno)); + return 1; + } +#endif + + utb.actime = sb.st_atime; + utb.modtime = set_eq ? set_time : sb.st_mtime + set_time; + + if (verbose) { + uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime; + printf("%"PRIuMAX"\t%s\n", mtime, argv[i]); + } + + if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) { + fprintf(stderr, "Failed to modify time on %s: %s\n", + argv[i], strerror(errno)); + return 1; + } + } + + return 0; + +usage: + fprintf(stderr, "usage: %s %s\n", argv[0], usage_str); + return 1; +} diff --git a/t/helper/test-config.c b/t/helper/test-config.c new file mode 100644 index 0000000..6a77552 --- /dev/null +++ b/t/helper/test-config.c @@ -0,0 +1,152 @@ +#include "cache.h" +#include "string-list.h" + +/* + * This program exposes the C API of the configuration mechanism + * as a set of simple commands in order to facilitate testing. + * + * Reads stdin and prints result of command to stdout: + * + * get_value -> prints the value with highest priority for the entered key + * + * get_value_multi -> prints all values for the entered key in increasing order + * of priority + * + * get_int -> print integer value for the entered key or die + * + * get_bool -> print bool value for the entered key or die + * + * get_string -> print string value for the entered key or die + * + * configset_get_value -> returns value with the highest priority for the entered key + * from a config_set constructed from files entered as arguments. + * + * configset_get_value_multi -> returns value_list for the entered key sorted in + * ascending order of priority from a config_set + * constructed from files entered as arguments. + * + * Examples: + * + * To print the value with highest priority for key "foo.bAr Baz.rock": + * test-config get_value "foo.bAr Baz.rock" + * + */ + + +int main(int argc, char **argv) +{ + int i, val; + const char *v; + const struct string_list *strptr; + struct config_set cs; + git_configset_init(&cs); + + if (argc < 2) { + fprintf(stderr, "Please, provide a command name on the command-line\n"); + goto exit1; + } else if (argc == 3 && !strcmp(argv[1], "get_value")) { + if (!git_config_get_value(argv[2], &v)) { + if (!v) + printf("(NULL)\n"); + else + printf("%s\n", v); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) { + strptr = git_config_get_value_multi(argv[2]); + if (strptr) { + for (i = 0; i < strptr->nr; i++) { + v = strptr->items[i].string; + if (!v) + printf("(NULL)\n"); + else + printf("%s\n", v); + } + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (argc == 3 && !strcmp(argv[1], "get_int")) { + if (!git_config_get_int(argv[2], &val)) { + printf("%d\n", val); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (argc == 3 && !strcmp(argv[1], "get_bool")) { + if (!git_config_get_bool(argv[2], &val)) { + printf("%d\n", val); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (argc == 3 && !strcmp(argv[1], "get_string")) { + if (!git_config_get_string_const(argv[2], &v)) { + printf("%s\n", v); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (!strcmp(argv[1], "configset_get_value")) { + for (i = 3; i < argc; i++) { + int err; + if ((err = git_configset_add_file(&cs, argv[i]))) { + fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]); + goto exit2; + } + } + if (!git_configset_get_value(&cs, argv[2], &v)) { + if (!v) + printf("(NULL)\n"); + else + printf("%s\n", v); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } else if (!strcmp(argv[1], "configset_get_value_multi")) { + for (i = 3; i < argc; i++) { + int err; + if ((err = git_configset_add_file(&cs, argv[i]))) { + fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]); + goto exit2; + } + } + strptr = git_configset_get_value_multi(&cs, argv[2]); + if (strptr) { + for (i = 0; i < strptr->nr; i++) { + v = strptr->items[i].string; + if (!v) + printf("(NULL)\n"); + else + printf("%s\n", v); + } + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } + } + + die("%s: Please check the syntax and the function name", argv[0]); + +exit0: + git_configset_clear(&cs); + return 0; + +exit1: + git_configset_clear(&cs); + return 1; + +exit2: + git_configset_clear(&cs); + return 2; +} diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c new file mode 100644 index 0000000..707a821 --- /dev/null +++ b/t/helper/test-ctype.c @@ -0,0 +1,42 @@ +#include "cache.h" + +static int rc; + +static void report_error(const char *class, int ch) +{ + printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch); + rc = 1; +} + +static int is_in(const char *s, int ch) +{ + /* We can't find NUL using strchr. It's classless anyway. */ + if (ch == '\0') + return 0; + return !!strchr(s, ch); +} + +#define TEST_CLASS(t,s) { \ + int i; \ + for (i = 0; i < 256; i++) { \ + if (is_in(s, i) != t(i)) \ + report_error(#t, i); \ + } \ +} + +#define DIGIT "0123456789" +#define LOWER "abcdefghijklmnopqrstuvwxyz" +#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +int main(int argc, char **argv) +{ + TEST_CLASS(isdigit, DIGIT); + TEST_CLASS(isspace, " \n\r\t"); + TEST_CLASS(isalpha, LOWER UPPER); + TEST_CLASS(isalnum, LOWER UPPER DIGIT); + TEST_CLASS(is_glob_special, "*?[\\"); + TEST_CLASS(is_regex_special, "$()*+.?[\\^{|"); + TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~"); + + return rc; +} diff --git a/t/helper/test-date.c b/t/helper/test-date.c new file mode 100644 index 0000000..63f3735 --- /dev/null +++ b/t/helper/test-date.c @@ -0,0 +1,73 @@ +#include "cache.h" + +static const char *usage_msg = "\n" +" test-date show [time_t]...\n" +" test-date parse [date]...\n" +" test-date approxidate [date]...\n"; + +static void show_dates(char **argv, struct timeval *now) +{ + struct strbuf buf = STRBUF_INIT; + + for (; *argv; argv++) { + time_t t = atoi(*argv); + show_date_relative(t, 0, now, &buf); + printf("%s -> %s\n", *argv, buf.buf); + } + strbuf_release(&buf); +} + +static void parse_dates(char **argv, struct timeval *now) +{ + struct strbuf result = STRBUF_INIT; + + for (; *argv; argv++) { + unsigned long t; + int tz; + + strbuf_reset(&result); + parse_date(*argv, &result); + if (sscanf(result.buf, "%lu %d", &t, &tz) == 2) + printf("%s -> %s\n", + *argv, show_date(t, tz, DATE_MODE(ISO8601))); + else + printf("%s -> bad\n", *argv); + } + strbuf_release(&result); +} + +static void parse_approxidate(char **argv, struct timeval *now) +{ + for (; *argv; argv++) { + time_t t; + t = approxidate_relative(*argv, now); + printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601))); + } +} + +int main(int argc, char **argv) +{ + struct timeval now; + const char *x; + + x = getenv("TEST_DATE_NOW"); + if (x) { + now.tv_sec = atoi(x); + now.tv_usec = 0; + } + else + gettimeofday(&now, NULL); + + argv++; + if (!*argv) + usage(usage_msg); + if (!strcmp(*argv, "show")) + show_dates(argv+1, &now); + else if (!strcmp(*argv, "parse")) + parse_dates(argv+1, &now); + else if (!strcmp(*argv, "approxidate")) + parse_approxidate(argv+1, &now); + else + usage(usage_msg); + return 0; +} diff --git a/t/helper/test-delta.c b/t/helper/test-delta.c new file mode 100644 index 0000000..4595cd6 --- /dev/null +++ b/t/helper/test-delta.c @@ -0,0 +1,78 @@ +/* + * test-delta.c: test code to exercise diff-delta.c and patch-delta.c + * + * (C) 2005 Nicolas Pitre + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "git-compat-util.h" +#include "delta.h" +#include "cache.h" + +static const char usage_str[] = + "test-delta (-d|-p) "; + +int main(int argc, char *argv[]) +{ + int fd; + struct stat st; + void *from_buf, *data_buf, *out_buf; + unsigned long from_size, data_size, out_size; + + if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) { + fprintf(stderr, "usage: %s\n", usage_str); + return 1; + } + + fd = open(argv[2], O_RDONLY); + if (fd < 0 || fstat(fd, &st)) { + perror(argv[2]); + return 1; + } + from_size = st.st_size; + from_buf = mmap(NULL, from_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (from_buf == MAP_FAILED) { + perror(argv[2]); + close(fd); + return 1; + } + close(fd); + + fd = open(argv[3], O_RDONLY); + if (fd < 0 || fstat(fd, &st)) { + perror(argv[3]); + return 1; + } + data_size = st.st_size; + data_buf = mmap(NULL, data_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (data_buf == MAP_FAILED) { + perror(argv[3]); + close(fd); + return 1; + } + close(fd); + + if (argv[1][1] == 'd') + out_buf = diff_delta(from_buf, from_size, + data_buf, data_size, + &out_size, 0); + else + out_buf = patch_delta(from_buf, from_size, + data_buf, data_size, + &out_size); + if (!out_buf) { + fprintf(stderr, "delta operation failed (returned NULL)\n"); + return 1; + } + + fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fd < 0 || write_in_full(fd, out_buf, out_size) != out_size) { + perror(argv[4]); + return 1; + } + + return 0; +} diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c new file mode 100644 index 0000000..bb53c0a --- /dev/null +++ b/t/helper/test-dump-cache-tree.c @@ -0,0 +1,67 @@ +#include "cache.h" +#include "tree.h" +#include "cache-tree.h" + + +static void dump_one(struct cache_tree *it, const char *pfx, const char *x) +{ + if (it->entry_count < 0) + printf("%-40s %s%s (%d subtrees)\n", + "invalid", x, pfx, it->subtree_nr); + else + printf("%s %s%s (%d entries, %d subtrees)\n", + sha1_to_hex(it->sha1), x, pfx, + it->entry_count, it->subtree_nr); +} + +static int dump_cache_tree(struct cache_tree *it, + struct cache_tree *ref, + const char *pfx) +{ + int i; + int errs = 0; + + if (!it || !ref) + /* missing in either */ + return 0; + + if (it->entry_count < 0) { + /* invalid */ + dump_one(it, pfx, ""); + dump_one(ref, pfx, "#(ref) "); + } + else { + dump_one(it, pfx, ""); + if (hashcmp(it->sha1, ref->sha1) || + ref->entry_count != it->entry_count || + ref->subtree_nr != it->subtree_nr) { + /* claims to be valid but is lying */ + dump_one(ref, pfx, "#(ref) "); + errs = 1; + } + } + + for (i = 0; i < it->subtree_nr; i++) { + char path[PATH_MAX]; + struct cache_tree_sub *down = it->down[i]; + struct cache_tree_sub *rdwn; + + rdwn = cache_tree_sub(ref, down->name); + xsnprintf(path, sizeof(path), "%s%.*s/", pfx, down->namelen, down->name); + if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path)) + errs = 1; + } + return errs; +} + +int main(int ac, char **av) +{ + struct index_state istate; + struct cache_tree *another = cache_tree(); + if (read_cache() < 0) + die("unable to read index file"); + istate = the_index; + istate.cache_tree = another; + cache_tree_update(&istate, WRITE_TREE_DRY_RUN); + return dump_cache_tree(active_cache_tree, another, ""); +} diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c new file mode 100644 index 0000000..861d28c --- /dev/null +++ b/t/helper/test-dump-split-index.c @@ -0,0 +1,36 @@ +#include "cache.h" +#include "split-index.h" +#include "ewah/ewok.h" + +static void show_bit(size_t pos, void *data) +{ + printf(" %d", (int)pos); +} + +int main(int ac, char **av) +{ + struct split_index *si; + int i; + + do_read_index(&the_index, av[1], 1); + printf("own %s\n", sha1_to_hex(the_index.sha1)); + si = the_index.split_index; + if (!si) { + printf("not a split index\n"); + return 0; + } + printf("base %s\n", sha1_to_hex(si->base_sha1)); + for (i = 0; i < the_index.cache_nr; i++) { + struct cache_entry *ce = the_index.cache[i]; + printf("%06o %s %d\t%s\n", ce->ce_mode, + sha1_to_hex(ce->sha1), ce_stage(ce), ce->name); + } + printf("replacements:"); + if (si->replace_bitmap) + ewah_each_bit(si->replace_bitmap, show_bit, NULL); + printf("\ndeletions:"); + if (si->delete_bitmap) + ewah_each_bit(si->delete_bitmap, show_bit, NULL); + printf("\n"); + return 0; +} diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c new file mode 100644 index 0000000..0a1c285 --- /dev/null +++ b/t/helper/test-dump-untracked-cache.c @@ -0,0 +1,66 @@ +#include "cache.h" +#include "dir.h" + +static int compare_untracked(const void *a_, const void *b_) +{ + const char *const *a = a_; + const char *const *b = b_; + return strcmp(*a, *b); +} + +static int compare_dir(const void *a_, const void *b_) +{ + const struct untracked_cache_dir *const *a = a_; + const struct untracked_cache_dir *const *b = b_; + return strcmp((*a)->name, (*b)->name); +} + +static void dump(struct untracked_cache_dir *ucd, struct strbuf *base) +{ + int i, len; + qsort(ucd->untracked, ucd->untracked_nr, sizeof(*ucd->untracked), + compare_untracked); + qsort(ucd->dirs, ucd->dirs_nr, sizeof(*ucd->dirs), + compare_dir); + len = base->len; + strbuf_addf(base, "%s/", ucd->name); + printf("%s %s", base->buf, + sha1_to_hex(ucd->exclude_sha1)); + if (ucd->recurse) + fputs(" recurse", stdout); + if (ucd->check_only) + fputs(" check_only", stdout); + if (ucd->valid) + fputs(" valid", stdout); + printf("\n"); + for (i = 0; i < ucd->untracked_nr; i++) + printf("%s\n", ucd->untracked[i]); + for (i = 0; i < ucd->dirs_nr; i++) + dump(ucd->dirs[i], base); + strbuf_setlen(base, len); +} + +int main(int ac, char **av) +{ + struct untracked_cache *uc; + struct strbuf base = STRBUF_INIT; + + /* Hack to avoid modifying the untracked cache when we read it */ + ignore_untracked_cache_config = 1; + + setup_git_directory(); + if (read_cache() < 0) + die("unable to read index file"); + uc = the_index.untracked; + if (!uc) { + printf("no untracked cache\n"); + return 0; + } + printf("info/exclude %s\n", sha1_to_hex(uc->ss_info_exclude.sha1)); + printf("core.excludesfile %s\n", sha1_to_hex(uc->ss_excludes_file.sha1)); + printf("exclude_per_dir %s\n", uc->exclude_per_dir); + printf("flags %08x\n", uc->dir_flags); + if (uc->root) + dump(uc->root, &base); + return 0; +} diff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c new file mode 100644 index 0000000..980de21 --- /dev/null +++ b/t/helper/test-fake-ssh.c @@ -0,0 +1,30 @@ +#include "git-compat-util.h" +#include "run-command.h" +#include "strbuf.h" + +int main(int argc, char **argv) +{ + const char *trash_directory = getenv("TRASH_DIRECTORY"); + struct strbuf buf = STRBUF_INIT; + FILE *f; + int i; + const char *child_argv[] = { NULL, NULL }; + + /* First, print all parameters into $TRASH_DIRECTORY/ssh-output */ + if (!trash_directory) + die("Need a TRASH_DIRECTORY!"); + strbuf_addf(&buf, "%s/ssh-output", trash_directory); + f = fopen(buf.buf, "w"); + if (!f) + die("Could not write to %s", buf.buf); + for (i = 0; i < argc; i++) + fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:"); + fprintf(f, "\n"); + fclose(f); + + /* Now, evaluate the *last* parameter */ + if (argc < 2) + return 0; + child_argv[0] = argv[argc - 1]; + return run_command_v_opt(child_argv, RUN_USING_SHELL); +} diff --git a/t/helper/test-genrandom.c b/t/helper/test-genrandom.c new file mode 100644 index 0000000..54824d0 --- /dev/null +++ b/t/helper/test-genrandom.c @@ -0,0 +1,33 @@ +/* + * Simple random data generator used to create reproducible test files. + * This is inspired from POSIX.1-2001 implementation example for rand(). + * Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2. + */ + +#include "git-compat-util.h" + +int main(int argc, char *argv[]) +{ + unsigned long count, next = 0; + unsigned char *c; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "usage: %s []\n", argv[0]); + return 1; + } + + c = (unsigned char *) argv[1]; + do { + next = next * 11 + *c; + } while (*c++); + + count = (argc == 3) ? strtoul(argv[2], NULL, 0) : -1L; + + while (count--) { + next = next * 1103515245 + 12345; + if (putchar((next >> 16) & 0xff) == EOF) + return -1; + } + + return 0; +} diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c new file mode 100644 index 0000000..cc2891d --- /dev/null +++ b/t/helper/test-hashmap.c @@ -0,0 +1,264 @@ +#include "git-compat-util.h" +#include "hashmap.h" + +struct test_entry +{ + struct hashmap_entry ent; + /* key and value as two \0-terminated strings */ + char key[FLEX_ARRAY]; +}; + +static const char *get_value(const struct test_entry *e) +{ + return e->key + strlen(e->key) + 1; +} + +static int test_entry_cmp(const struct test_entry *e1, + const struct test_entry *e2, const char* key) +{ + return strcmp(e1->key, key ? key : e2->key); +} + +static int test_entry_cmp_icase(const struct test_entry *e1, + const struct test_entry *e2, const char* key) +{ + return strcasecmp(e1->key, key ? key : e2->key); +} + +static struct test_entry *alloc_test_entry(int hash, char *key, int klen, + char *value, int vlen) +{ + struct test_entry *entry = malloc(sizeof(struct test_entry) + klen + + vlen + 2); + hashmap_entry_init(entry, hash); + memcpy(entry->key, key, klen + 1); + memcpy(entry->key + klen + 1, value, vlen + 1); + return entry; +} + +#define HASH_METHOD_FNV 0 +#define HASH_METHOD_I 1 +#define HASH_METHOD_IDIV10 2 +#define HASH_METHOD_0 3 +#define HASH_METHOD_X2 4 +#define TEST_SPARSE 8 +#define TEST_ADD 16 +#define TEST_SIZE 100000 + +static unsigned int hash(unsigned int method, unsigned int i, const char *key) +{ + unsigned int hash = 0; + switch (method & 3) + { + case HASH_METHOD_FNV: + hash = strhash(key); + break; + case HASH_METHOD_I: + hash = i; + break; + case HASH_METHOD_IDIV10: + hash = i / 10; + break; + case HASH_METHOD_0: + hash = 0; + break; + } + + if (method & HASH_METHOD_X2) + hash = 2 * hash; + return hash; +} + +/* + * Test performance of hashmap.[ch] + * Usage: time echo "perfhashmap method rounds" | test-hashmap + */ +static void perf_hashmap(unsigned int method, unsigned int rounds) +{ + struct hashmap map; + char buf[16]; + struct test_entry **entries; + unsigned int *hashes; + unsigned int i, j; + + entries = malloc(TEST_SIZE * sizeof(struct test_entry *)); + hashes = malloc(TEST_SIZE * sizeof(int)); + for (i = 0; i < TEST_SIZE; i++) { + snprintf(buf, sizeof(buf), "%i", i); + entries[i] = alloc_test_entry(0, buf, strlen(buf), "", 0); + hashes[i] = hash(method, i, entries[i]->key); + } + + if (method & TEST_ADD) { + /* test adding to the map */ + for (j = 0; j < rounds; j++) { + hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0); + + /* add entries */ + for (i = 0; i < TEST_SIZE; i++) { + hashmap_entry_init(entries[i], hashes[i]); + hashmap_add(&map, entries[i]); + } + + hashmap_free(&map, 0); + } + } else { + /* test map lookups */ + hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0); + + /* fill the map (sparsely if specified) */ + j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE; + for (i = 0; i < j; i++) { + hashmap_entry_init(entries[i], hashes[i]); + hashmap_add(&map, entries[i]); + } + + for (j = 0; j < rounds; j++) { + for (i = 0; i < TEST_SIZE; i++) { + hashmap_get_from_hash(&map, hashes[i], + entries[i]->key); + } + } + + hashmap_free(&map, 0); + } +} + +#define DELIM " \t\r\n" + +/* + * Read stdin line by line and print result of commands to stdout: + * + * hash key -> strhash(key) memhash(key) strihash(key) memihash(key) + * put key value -> NULL / old value + * get key -> NULL / value + * remove key -> NULL / old value + * iterate -> key1 value1\nkey2 value2\n... + * size -> tablesize numentries + * + * perfhashmap method rounds -> test hashmap.[ch] performance + */ +int main(int argc, char *argv[]) +{ + char line[1024]; + struct hashmap map; + int icase; + + /* init hash map */ + icase = argc > 1 && !strcmp("ignorecase", argv[1]); + hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase + : test_entry_cmp), 0); + + /* process commands from stdin */ + while (fgets(line, sizeof(line), stdin)) { + char *cmd, *p1 = NULL, *p2 = NULL; + int l1 = 0, l2 = 0, hash = 0; + struct test_entry *entry; + + /* break line into command and up to two parameters */ + cmd = strtok(line, DELIM); + /* ignore empty lines */ + if (!cmd || *cmd == '#') + continue; + + p1 = strtok(NULL, DELIM); + if (p1) { + l1 = strlen(p1); + hash = icase ? strihash(p1) : strhash(p1); + p2 = strtok(NULL, DELIM); + if (p2) + l2 = strlen(p2); + } + + if (!strcmp("hash", cmd) && l1) { + + /* print results of different hash functions */ + printf("%u %u %u %u\n", strhash(p1), memhash(p1, l1), + strihash(p1), memihash(p1, l1)); + + } else if (!strcmp("add", cmd) && l1 && l2) { + + /* create entry with key = p1, value = p2 */ + entry = alloc_test_entry(hash, p1, l1, p2, l2); + + /* add to hashmap */ + hashmap_add(&map, entry); + + } else if (!strcmp("put", cmd) && l1 && l2) { + + /* create entry with key = p1, value = p2 */ + entry = alloc_test_entry(hash, p1, l1, p2, l2); + + /* add / replace entry */ + entry = hashmap_put(&map, entry); + + /* print and free replaced entry, if any */ + puts(entry ? get_value(entry) : "NULL"); + free(entry); + + } else if (!strcmp("get", cmd) && l1) { + + /* lookup entry in hashmap */ + entry = hashmap_get_from_hash(&map, hash, p1); + + /* print result */ + if (!entry) + puts("NULL"); + while (entry) { + puts(get_value(entry)); + entry = hashmap_get_next(&map, entry); + } + + } else if (!strcmp("remove", cmd) && l1) { + + /* setup static key */ + struct hashmap_entry key; + hashmap_entry_init(&key, hash); + + /* remove entry from hashmap */ + entry = hashmap_remove(&map, &key, p1); + + /* print result and free entry*/ + puts(entry ? get_value(entry) : "NULL"); + free(entry); + + } else if (!strcmp("iterate", cmd)) { + + struct hashmap_iter iter; + hashmap_iter_init(&map, &iter); + while ((entry = hashmap_iter_next(&iter))) + printf("%s %s\n", entry->key, get_value(entry)); + + } else if (!strcmp("size", cmd)) { + + /* print table sizes */ + printf("%u %u\n", map.tablesize, map.size); + + } else if (!strcmp("intern", cmd) && l1) { + + /* test that strintern works */ + const char *i1 = strintern(p1); + const char *i2 = strintern(p1); + if (strcmp(i1, p1)) + printf("strintern(%s) returns %s\n", p1, i1); + else if (i1 == p1) + printf("strintern(%s) returns input pointer\n", p1); + else if (i1 != i2) + printf("strintern(%s) != strintern(%s)", i1, i2); + else + printf("%s\n", i1); + + } else if (!strcmp("perfhashmap", cmd) && l1 && l2) { + + perf_hashmap(atoi(p1), atoi(p2)); + + } else { + + printf("Unknown command %s\n", cmd); + + } + } + + hashmap_free(&map, 1); + return 0; +} diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c new file mode 100644 index 0000000..05d4699 --- /dev/null +++ b/t/helper/test-index-version.c @@ -0,0 +1,14 @@ +#include "cache.h" + +int main(int argc, char **argv) +{ + struct cache_header hdr; + int version; + + memset(&hdr,0,sizeof(hdr)); + if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr)) + return 0; + version = ntohl(hdr.hdr_version); + printf("%d\n", version); + return 0; +} diff --git a/t/helper/test-line-buffer.c b/t/helper/test-line-buffer.c new file mode 100644 index 0000000..1e58f04 --- /dev/null +++ b/t/helper/test-line-buffer.c @@ -0,0 +1,91 @@ +/* + * test-line-buffer.c: code to exercise the svn importer's input helper + */ + +#include "git-compat-util.h" +#include "strbuf.h" +#include "vcs-svn/line_buffer.h" + +static uint32_t strtouint32(const char *s) +{ + char *end; + uintmax_t n = strtoumax(s, &end, 10); + if (*s == '\0' || *end != '\0') + die("invalid count: %s", s); + return (uint32_t) n; +} + +static void handle_command(const char *command, const char *arg, struct line_buffer *buf) +{ + switch (*command) { + case 'b': + if (starts_with(command, "binary ")) { + struct strbuf sb = STRBUF_INIT; + strbuf_addch(&sb, '>'); + buffer_read_binary(buf, &sb, strtouint32(arg)); + fwrite(sb.buf, 1, sb.len, stdout); + strbuf_release(&sb); + return; + } + case 'c': + if (starts_with(command, "copy ")) { + buffer_copy_bytes(buf, strtouint32(arg)); + return; + } + case 's': + if (starts_with(command, "skip ")) { + buffer_skip_bytes(buf, strtouint32(arg)); + return; + } + default: + die("unrecognized command: %s", command); + } +} + +static void handle_line(const char *line, struct line_buffer *stdin_buf) +{ + const char *arg = strchr(line, ' '); + if (!arg) + die("no argument in line: %s", line); + handle_command(line, arg + 1, stdin_buf); +} + +int main(int argc, char *argv[]) +{ + struct line_buffer stdin_buf = LINE_BUFFER_INIT; + struct line_buffer file_buf = LINE_BUFFER_INIT; + struct line_buffer *input = &stdin_buf; + const char *filename; + char *s; + + if (argc == 1) + filename = NULL; + else if (argc == 2) + filename = argv[1]; + else + usage("test-line-buffer [file | &fd] < script"); + + if (buffer_init(&stdin_buf, NULL)) + die_errno("open error"); + if (filename) { + if (*filename == '&') { + if (buffer_fdinit(&file_buf, strtouint32(filename + 1))) + die_errno("error opening fd %s", filename + 1); + } else { + if (buffer_init(&file_buf, filename)) + die_errno("error opening %s", filename); + } + input = &file_buf; + } + + while ((s = buffer_read_line(&stdin_buf))) + handle_line(s, input); + + if (filename && buffer_deinit(&file_buf)) + die("error reading from %s", filename); + if (buffer_deinit(&stdin_buf)) + die("input error"); + if (ferror(stdout)) + die("output error"); + return 0; +} diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c new file mode 100644 index 0000000..4dad709 --- /dev/null +++ b/t/helper/test-match-trees.c @@ -0,0 +1,26 @@ +#include "cache.h" +#include "tree.h" + +int main(int ac, char **av) +{ + unsigned char hash1[20], hash2[20], shifted[20]; + struct tree *one, *two; + + setup_git_directory(); + + if (get_sha1(av[1], hash1)) + die("cannot parse %s as an object name", av[1]); + if (get_sha1(av[2], hash2)) + die("cannot parse %s as an object name", av[2]); + one = parse_tree_indirect(hash1); + if (!one) + die("not a tree-ish %s", av[1]); + two = parse_tree_indirect(hash2); + if (!two) + die("not a tree-ish %s", av[2]); + + shift_tree(one->object.oid.hash, two->object.oid.hash, shifted, -1); + printf("shifted: %s\n", sha1_to_hex(shifted)); + + exit(0); +} diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c new file mode 100644 index 0000000..ea3b959 --- /dev/null +++ b/t/helper/test-mergesort.c @@ -0,0 +1,52 @@ +#include "cache.h" +#include "mergesort.h" + +struct line { + char *text; + struct line *next; +}; + +static void *get_next(const void *a) +{ + return ((const struct line *)a)->next; +} + +static void set_next(void *a, void *b) +{ + ((struct line *)a)->next = b; +} + +static int compare_strings(const void *a, const void *b) +{ + const struct line *x = a, *y = b; + return strcmp(x->text, y->text); +} + +int main(int argc, char **argv) +{ + struct line *line, *p = NULL, *lines = NULL; + struct strbuf sb = STRBUF_INIT; + + for (;;) { + if (strbuf_getwholeline(&sb, stdin, '\n')) + break; + line = xmalloc(sizeof(struct line)); + line->text = strbuf_detach(&sb, NULL); + if (p) { + line->next = p->next; + p->next = line; + } else { + line->next = NULL; + lines = line; + } + p = line; + } + + lines = llist_mergesort(lines, get_next, set_next, compare_strings); + + while (lines) { + printf("%s", lines->text); + lines = lines->next; + } + return 0; +} diff --git a/t/helper/test-mktemp.c b/t/helper/test-mktemp.c new file mode 100644 index 0000000..c8c5421 --- /dev/null +++ b/t/helper/test-mktemp.c @@ -0,0 +1,14 @@ +/* + * test-mktemp.c: code to exercise the creation of temporary files + */ +#include "git-compat-util.h" + +int main(int argc, char *argv[]) +{ + if (argc != 2) + usage("Expected 1 parameter defining the temporary file template"); + + xmkstemp(xstrdup(argv[1])); + + return 0; +} diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c new file mode 100644 index 0000000..2c8c8f1 --- /dev/null +++ b/t/helper/test-parse-options.c @@ -0,0 +1,104 @@ +#include "cache.h" +#include "parse-options.h" +#include "string-list.h" + +static int boolean = 0; +static int integer = 0; +static unsigned long magnitude = 0; +static unsigned long timestamp; +static int abbrev = 7; +static int verbose = 0, dry_run = 0, quiet = 0; +static char *string = NULL; +static char *file = NULL; +static int ambiguous; +static struct string_list list; + +static int length_callback(const struct option *opt, const char *arg, int unset) +{ + printf("Callback: \"%s\", %d\n", + (arg ? arg : "not set"), unset); + if (unset) + return 1; /* do not support unset */ + + *(int *)opt->value = strlen(arg); + return 0; +} + +static int number_callback(const struct option *opt, const char *arg, int unset) +{ + *(int *)opt->value = strtol(arg, NULL, 10); + return 0; +} + +int main(int argc, char **argv) +{ + const char *prefix = "prefix/"; + const char *usage[] = { + "test-parse-options ", + NULL + }; + struct option options[] = { + OPT_BOOL(0, "yes", &boolean, "get a boolean"), + OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"), + { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL, + "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, + OPT_COUNTUP('b', "boolean", &boolean, "increment by one"), + OPT_BIT('4', "or4", &boolean, + "bitwise-or boolean with ...0100", 4), + OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4), + OPT_GROUP(""), + OPT_INTEGER('i', "integer", &integer, "get a integer"), + OPT_INTEGER('j', NULL, &integer, "get a integer, too"), + OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"), + OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), + OPT_DATE('t', NULL, ×tamp, "get timestamp of