Merge branch 'mt/rot13-in-c'
Test portability improvements. * mt/rot13-in-c: tests: use the new C rot13-filter helper to avoid PERL prereq t0021: implementation the rot13-filter.pl script in C t0021: avoid grepping for a Perl-specific string at filter output
This commit is contained in:
commit
64cb4c34d1
1
Makefile
1
Makefile
@ -772,6 +772,7 @@ TEST_BUILTINS_OBJS += test-read-midx.o
|
||||
TEST_BUILTINS_OBJS += test-ref-store.o
|
||||
TEST_BUILTINS_OBJS += test-reftable.o
|
||||
TEST_BUILTINS_OBJS += test-regex.o
|
||||
TEST_BUILTINS_OBJS += test-rot13-filter.o
|
||||
TEST_BUILTINS_OBJS += test-repository.o
|
||||
TEST_BUILTINS_OBJS += test-revision-walking.o
|
||||
TEST_BUILTINS_OBJS += test-run-command.o
|
||||
|
@ -309,7 +309,8 @@ int write_packetized_from_fd_no_flush(int fd_in, int fd_out)
|
||||
return err;
|
||||
}
|
||||
|
||||
int write_packetized_from_buf_no_flush(const char *src_in, size_t len, int fd_out)
|
||||
int write_packetized_from_buf_no_flush_count(const char *src_in, size_t len,
|
||||
int fd_out, int *packet_counter)
|
||||
{
|
||||
int err = 0;
|
||||
size_t bytes_written = 0;
|
||||
@ -324,6 +325,8 @@ int write_packetized_from_buf_no_flush(const char *src_in, size_t len, int fd_ou
|
||||
break;
|
||||
err = packet_write_gently(fd_out, src_in + bytes_written, bytes_to_write);
|
||||
bytes_written += bytes_to_write;
|
||||
if (packet_counter)
|
||||
(*packet_counter)++;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -32,7 +32,13 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((f
|
||||
int packet_flush_gently(int fd);
|
||||
int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
|
||||
int write_packetized_from_fd_no_flush(int fd_in, int fd_out);
|
||||
int write_packetized_from_buf_no_flush(const char *src_in, size_t len, int fd_out);
|
||||
int write_packetized_from_buf_no_flush_count(const char *src_in, size_t len,
|
||||
int fd_out, int *packet_counter);
|
||||
static inline int write_packetized_from_buf_no_flush(const char *src_in,
|
||||
size_t len, int fd_out)
|
||||
{
|
||||
return write_packetized_from_buf_no_flush_count(src_in, len, fd_out, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stdio versions of packet_write functions. When mixing these with fd
|
||||
|
382
t/helper/test-rot13-filter.c
Normal file
382
t/helper/test-rot13-filter.c
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Example implementation for the Git filter protocol version 2
|
||||
* See Documentation/gitattributes.txt, section "Filter Protocol"
|
||||
*
|
||||
* Usage: test-tool rot13-filter [--always-delay] --log=<path> <capabilities>
|
||||
*
|
||||
* Log path defines a debug log file that the script writes to. The
|
||||
* subsequent arguments define a list of supported protocol capabilities
|
||||
* ("clean", "smudge", etc).
|
||||
*
|
||||
* When --always-delay is given all pathnames with the "can-delay" flag
|
||||
* that don't appear on the list bellow are delayed with a count of 1
|
||||
* (see more below).
|
||||
*
|
||||
* This implementation supports special test cases:
|
||||
* (1) If data with the pathname "clean-write-fail.r" is processed with
|
||||
* a "clean" operation then the write operation will die.
|
||||
* (2) If data with the pathname "smudge-write-fail.r" is processed with
|
||||
* a "smudge" operation then the write operation will die.
|
||||
* (3) If data with the pathname "error.r" is processed with any
|
||||
* operation then the filter signals that it cannot or does not want
|
||||
* to process the file.
|
||||
* (4) If data with the pathname "abort.r" is processed with any
|
||||
* operation then the filter signals that it cannot or does not want
|
||||
* to process the file and any file after that is processed with the
|
||||
* same command.
|
||||
* (5) If data with a pathname that is a key in the delay hash is
|
||||
* requested (e.g. "test-delay10.a") then the filter responds with
|
||||
* a "delay" status and sets the "requested" field in the delay hash.
|
||||
* The filter will signal the availability of this object after
|
||||
* "count" (field in delay hash) "list_available_blobs" commands.
|
||||
* (6) If data with the pathname "missing-delay.a" is processed that the
|
||||
* filter will drop the path from the "list_available_blobs" response.
|
||||
* (7) If data with the pathname "invalid-delay.a" is processed that the
|
||||
* filter will add the path "unfiltered" which was not delayed before
|
||||
* to the "list_available_blobs" response.
|
||||
*/
|
||||
|
||||
#include "test-tool.h"
|
||||
#include "pkt-line.h"
|
||||
#include "string-list.h"
|
||||
#include "strmap.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static FILE *logfile;
|
||||
static int always_delay, has_clean_cap, has_smudge_cap;
|
||||
static struct strmap delay = STRMAP_INIT;
|
||||
|
||||
static inline const char *str_or_null(const char *str)
|
||||
{
|
||||
return str ? str : "(null)";
|
||||
}
|
||||
|
||||
static char *rot13(char *str)
|
||||
{
|
||||
char *c;
|
||||
for (c = str; *c; c++)
|
||||
if (isalpha(*c))
|
||||
*c += tolower(*c) < 'n' ? 13 : -13;
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *get_value(char *buf, const char *key)
|
||||
{
|
||||
const char *orig_buf = buf;
|
||||
if (!buf ||
|
||||
!skip_prefix((const char *)buf, key, (const char **)&buf) ||
|
||||
!skip_prefix((const char *)buf, "=", (const char **)&buf) ||
|
||||
!*buf)
|
||||
die("expected key '%s', got '%s'", key, str_or_null(orig_buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a text packet, expecting that it is in the form "key=value" for
|
||||
* the given key. An EOF does not trigger any error and is reported
|
||||
* back to the caller with NULL. Die if the "key" part of "key=value" does
|
||||
* not match the given key, or the value part is empty.
|
||||
*/
|
||||
static char *packet_key_val_read(const char *key)
|
||||
{
|
||||
char *buf;
|
||||
if (packet_read_line_gently(0, NULL, &buf) < 0)
|
||||
return NULL;
|
||||
return xstrdup(get_value(buf, key));
|
||||
}
|
||||
|
||||
static inline void assert_remote_capability(struct strset *caps, const char *cap)
|
||||
{
|
||||
if (!strset_contains(caps, cap))
|
||||
die("required '%s' capability not available from remote", cap);
|
||||
}
|
||||
|
||||
static void read_capabilities(struct strset *remote_caps)
|
||||
{
|
||||
for (;;) {
|
||||
char *buf = packet_read_line(0, NULL);
|
||||
if (!buf)
|
||||
break;
|
||||
strset_add(remote_caps, get_value(buf, "capability"));
|
||||
}
|
||||
|
||||
assert_remote_capability(remote_caps, "clean");
|
||||
assert_remote_capability(remote_caps, "smudge");
|
||||
assert_remote_capability(remote_caps, "delay");
|
||||
}
|
||||
|
||||
static void check_and_write_capabilities(struct strset *remote_caps,
|
||||
const char **caps, int nr_caps)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < nr_caps; i++) {
|
||||
if (!strset_contains(remote_caps, caps[i]))
|
||||
die("our capability '%s' is not available from remote",
|
||||
caps[i]);
|
||||
packet_write_fmt(1, "capability=%s\n", caps[i]);
|
||||
}
|
||||
packet_flush(1);
|
||||
}
|
||||
|
||||
struct delay_entry {
|
||||
int requested, count;
|
||||
char *output;
|
||||
};
|
||||
|
||||
static void free_delay_entries(void)
|
||||
{
|
||||
struct hashmap_iter iter;
|
||||
struct strmap_entry *ent;
|
||||
|
||||
strmap_for_each_entry(&delay, &iter, ent) {
|
||||
struct delay_entry *delay_entry = ent->value;
|
||||
free(delay_entry->output);
|
||||
free(delay_entry);
|
||||
}
|
||||
strmap_clear(&delay, 0);
|
||||
}
|
||||
|
||||
static void add_delay_entry(char *pathname, int count, int requested)
|
||||
{
|
||||
struct delay_entry *entry = xcalloc(1, sizeof(*entry));
|
||||
entry->count = count;
|
||||
entry->requested = requested;
|
||||
if (strmap_put(&delay, pathname, entry))
|
||||
BUG("adding the same path twice to delay hash?");
|
||||
}
|
||||
|
||||
static void reply_list_available_blobs_cmd(void)
|
||||
{
|
||||
struct hashmap_iter iter;
|
||||
struct strmap_entry *ent;
|
||||
struct string_list_item *str_item;
|
||||
struct string_list paths = STRING_LIST_INIT_NODUP;
|
||||
|
||||
/* flush */
|
||||
if (packet_read_line(0, NULL))
|
||||
die("bad list_available_blobs end");
|
||||
|
||||
strmap_for_each_entry(&delay, &iter, ent) {
|
||||
struct delay_entry *delay_entry = ent->value;
|
||||
if (!delay_entry->requested)
|
||||
continue;
|
||||
delay_entry->count--;
|
||||
if (!strcmp(ent->key, "invalid-delay.a")) {
|
||||
/* Send Git a pathname that was not delayed earlier */
|
||||
packet_write_fmt(1, "pathname=unfiltered");
|
||||
}
|
||||
if (!strcmp(ent->key, "missing-delay.a")) {
|
||||
/* Do not signal Git that this file is available */
|
||||
} else if (!delay_entry->count) {
|
||||
string_list_append(&paths, ent->key);
|
||||
packet_write_fmt(1, "pathname=%s", ent->key);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print paths in sorted order. */
|
||||
string_list_sort(&paths);
|
||||
for_each_string_list_item(str_item, &paths)
|
||||
fprintf(logfile, " %s", str_item->string);
|
||||
string_list_clear(&paths, 0);
|
||||
|
||||
packet_flush(1);
|
||||
|
||||
fprintf(logfile, " [OK]\n");
|
||||
packet_write_fmt(1, "status=success");
|
||||
packet_flush(1);
|
||||
}
|
||||
|
||||
static void command_loop(void)
|
||||
{
|
||||
for (;;) {
|
||||
char *buf, *output;
|
||||
char *pathname;
|
||||
struct delay_entry *entry;
|
||||
struct strbuf input = STRBUF_INIT;
|
||||
char *command = packet_key_val_read("command");
|
||||
|
||||
if (!command) {
|
||||
fprintf(logfile, "STOP\n");
|
||||
break;
|
||||
}
|
||||
fprintf(logfile, "IN: %s", command);
|
||||
|
||||
if (!strcmp(command, "list_available_blobs")) {
|
||||
reply_list_available_blobs_cmd();
|
||||
free(command);
|
||||
continue;
|
||||
}
|
||||
|
||||
pathname = packet_key_val_read("pathname");
|
||||
if (!pathname)
|
||||
die("unexpected EOF while expecting pathname");
|
||||
fprintf(logfile, " %s", pathname);
|
||||
|
||||
/* Read until flush */
|
||||
while ((buf = packet_read_line(0, NULL))) {
|
||||
if (!strcmp(buf, "can-delay=1")) {
|
||||
entry = strmap_get(&delay, pathname);
|
||||
if (entry && !entry->requested)
|
||||
entry->requested = 1;
|
||||
else if (!entry && always_delay)
|
||||
add_delay_entry(pathname, 1, 1);
|
||||
} else if (starts_with(buf, "ref=") ||
|
||||
starts_with(buf, "treeish=") ||
|
||||
starts_with(buf, "blob=")) {
|
||||
fprintf(logfile, " %s", buf);
|
||||
} else {
|
||||
/*
|
||||
* In general, filters need to be graceful about
|
||||
* new metadata, since it's documented that we
|
||||
* can pass any key-value pairs, but for tests,
|
||||
* let's be a little stricter.
|
||||
*/
|
||||
die("Unknown message '%s'", buf);
|
||||
}
|
||||
}
|
||||
|
||||
read_packetized_to_strbuf(0, &input, 0);
|
||||
fprintf(logfile, " %"PRIuMAX" [OK] -- ", (uintmax_t)input.len);
|
||||
|
||||
entry = strmap_get(&delay, pathname);
|
||||
if (entry && entry->output) {
|
||||
output = entry->output;
|
||||
} else if (!strcmp(pathname, "error.r") || !strcmp(pathname, "abort.r")) {
|
||||
output = "";
|
||||
} else if (!strcmp(command, "clean") && has_clean_cap) {
|
||||
output = rot13(input.buf);
|
||||
} else if (!strcmp(command, "smudge") && has_smudge_cap) {
|
||||
output = rot13(input.buf);
|
||||
} else {
|
||||
die("bad command '%s'", command);
|
||||
}
|
||||
|
||||
if (!strcmp(pathname, "error.r")) {
|
||||
fprintf(logfile, "[ERROR]\n");
|
||||
packet_write_fmt(1, "status=error");
|
||||
packet_flush(1);
|
||||
} else if (!strcmp(pathname, "abort.r")) {
|
||||
fprintf(logfile, "[ABORT]\n");
|
||||
packet_write_fmt(1, "status=abort");
|
||||
packet_flush(1);
|
||||
} else if (!strcmp(command, "smudge") &&
|
||||
(entry = strmap_get(&delay, pathname)) &&
|
||||
entry->requested == 1) {
|
||||
fprintf(logfile, "[DELAYED]\n");
|
||||
packet_write_fmt(1, "status=delayed");
|
||||
packet_flush(1);
|
||||
entry->requested = 2;
|
||||
if (entry->output != output) {
|
||||
free(entry->output);
|
||||
entry->output = xstrdup(output);
|
||||
}
|
||||
} else {
|
||||
int i, nr_packets = 0;
|
||||
size_t output_len;
|
||||
const char *p;
|
||||
packet_write_fmt(1, "status=success");
|
||||
packet_flush(1);
|
||||
|
||||
if (skip_prefix(pathname, command, &p) &&
|
||||
!strcmp(p, "-write-fail.r")) {
|
||||
fprintf(logfile, "[WRITE FAIL]\n");
|
||||
die("%s write error", command);
|
||||
}
|
||||
|
||||
output_len = strlen(output);
|
||||
fprintf(logfile, "OUT: %"PRIuMAX" ", (uintmax_t)output_len);
|
||||
|
||||
if (write_packetized_from_buf_no_flush_count(output,
|
||||
output_len, 1, &nr_packets))
|
||||
die("failed to write buffer to stdout");
|
||||
packet_flush(1);
|
||||
|
||||
for (i = 0; i < nr_packets; i++)
|
||||
fprintf(logfile, ".");
|
||||
fprintf(logfile, " [OK]\n");
|
||||
|
||||
packet_flush(1);
|
||||
}
|
||||
free(pathname);
|
||||
strbuf_release(&input);
|
||||
free(command);
|
||||
}
|
||||
}
|
||||
|
||||
static void packet_initialize(void)
|
||||
{
|
||||
char *pkt_buf = packet_read_line(0, NULL);
|
||||
|
||||
if (!pkt_buf || strcmp(pkt_buf, "git-filter-client"))
|
||||
die("bad initialize: '%s'", str_or_null(pkt_buf));
|
||||
|
||||
pkt_buf = packet_read_line(0, NULL);
|
||||
if (!pkt_buf || strcmp(pkt_buf, "version=2"))
|
||||
die("bad version: '%s'", str_or_null(pkt_buf));
|
||||
|
||||
pkt_buf = packet_read_line(0, NULL);
|
||||
if (pkt_buf)
|
||||
die("bad version end: '%s'", pkt_buf);
|
||||
|
||||
packet_write_fmt(1, "git-filter-server");
|
||||
packet_write_fmt(1, "version=2");
|
||||
packet_flush(1);
|
||||
}
|
||||
|
||||
static const char *rot13_usage[] = {
|
||||
"test-tool rot13-filter [--always-delay] --log=<path> <capabilities>",
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd__rot13_filter(int argc, const char **argv)
|
||||
{
|
||||
int i, nr_caps;
|
||||
struct strset remote_caps = STRSET_INIT;
|
||||
const char *log_path = NULL;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_BOOL(0, "always-delay", &always_delay,
|
||||
"delay all paths with the can-delay flag"),
|
||||
OPT_STRING(0, "log", &log_path, "path",
|
||||
"path to the debug log file"),
|
||||
OPT_END()
|
||||
};
|
||||
nr_caps = parse_options(argc, argv, NULL, options, rot13_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (!log_path || !nr_caps)
|
||||
usage_with_options(rot13_usage, options);
|
||||
|
||||
logfile = fopen(log_path, "a");
|
||||
if (!logfile)
|
||||
die_errno("failed to open log file");
|
||||
|
||||
for (i = 0; i < nr_caps; i++) {
|
||||
if (!strcmp(argv[i], "smudge"))
|
||||
has_smudge_cap = 1;
|
||||
if (!strcmp(argv[i], "clean"))
|
||||
has_clean_cap = 1;
|
||||
}
|
||||
|
||||
add_delay_entry("test-delay10.a", 1, 0);
|
||||
add_delay_entry("test-delay11.a", 1, 0);
|
||||
add_delay_entry("test-delay20.a", 2, 0);
|
||||
add_delay_entry("test-delay10.b", 1, 0);
|
||||
add_delay_entry("missing-delay.a", 1, 0);
|
||||
add_delay_entry("invalid-delay.a", 1, 0);
|
||||
|
||||
fprintf(logfile, "START\n");
|
||||
packet_initialize();
|
||||
|
||||
read_capabilities(&remote_caps);
|
||||
check_and_write_capabilities(&remote_caps, argv, nr_caps);
|
||||
fprintf(logfile, "init handshake complete\n");
|
||||
strset_clear(&remote_caps);
|
||||
|
||||
command_loop();
|
||||
|
||||
if (fclose(logfile))
|
||||
die_errno("error closing logfile");
|
||||
free_delay_entries();
|
||||
return 0;
|
||||
}
|
@ -65,6 +65,7 @@ static struct test_cmd cmds[] = {
|
||||
{ "read-midx", cmd__read_midx },
|
||||
{ "ref-store", cmd__ref_store },
|
||||
{ "reftable", cmd__reftable },
|
||||
{ "rot13-filter", cmd__rot13_filter },
|
||||
{ "dump-reftable", cmd__dump_reftable },
|
||||
{ "regex", cmd__regex },
|
||||
{ "repository", cmd__repository },
|
||||
|
@ -54,6 +54,7 @@ int cmd__read_cache(int argc, const char **argv);
|
||||
int cmd__read_graph(int argc, const char **argv);
|
||||
int cmd__read_midx(int argc, const char **argv);
|
||||
int cmd__ref_store(int argc, const char **argv);
|
||||
int cmd__rot13_filter(int argc, const char **argv);
|
||||
int cmd__reftable(int argc, const char **argv);
|
||||
int cmd__regex(int argc, const char **argv);
|
||||
int cmd__repository(int argc, const char **argv);
|
||||
|
@ -17,9 +17,6 @@ tr \
|
||||
'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
|
||||
EOF
|
||||
|
||||
write_script rot13-filter.pl "$PERL_PATH" \
|
||||
<"$TEST_DIRECTORY"/t0021/rot13-filter.pl
|
||||
|
||||
generate_random_characters () {
|
||||
LEN=$1
|
||||
NAME=$2
|
||||
@ -365,8 +362,8 @@ test_expect_success 'diff does not reuse worktree files that need cleaning' '
|
||||
test_line_count = 0 count
|
||||
'
|
||||
|
||||
test_expect_success PERL 'required process filter should filter data' '
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
|
||||
test_expect_success 'required process filter should filter data' '
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
|
||||
test_config_global filter.protocol.required true &&
|
||||
rm -rf repo &&
|
||||
mkdir repo &&
|
||||
@ -450,8 +447,8 @@ test_expect_success PERL 'required process filter should filter data' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'required process filter should filter data for various subcommands' '
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
|
||||
test_expect_success 'required process filter should filter data for various subcommands' '
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
|
||||
test_config_global filter.protocol.required true &&
|
||||
(
|
||||
cd repo &&
|
||||
@ -561,9 +558,9 @@ test_expect_success PERL 'required process filter should filter data for various
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'required process filter takes precedence' '
|
||||
test_expect_success 'required process filter takes precedence' '
|
||||
test_config_global filter.protocol.clean false &&
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean" &&
|
||||
test_config_global filter.protocol.required true &&
|
||||
rm -rf repo &&
|
||||
mkdir repo &&
|
||||
@ -587,8 +584,8 @@ test_expect_success PERL 'required process filter takes precedence' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'required process filter should be used only for "clean" operation only' '
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
|
||||
test_expect_success 'required process filter should be used only for "clean" operation only' '
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean" &&
|
||||
rm -rf repo &&
|
||||
mkdir repo &&
|
||||
(
|
||||
@ -622,8 +619,8 @@ test_expect_success PERL 'required process filter should be used only for "clean
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'required process filter should process multiple packets' '
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
|
||||
test_expect_success 'required process filter should process multiple packets' '
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
|
||||
test_config_global filter.protocol.required true &&
|
||||
|
||||
rm -rf repo &&
|
||||
@ -687,8 +684,8 @@ test_expect_success PERL 'required process filter should process multiple packet
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'required process filter with clean error should fail' '
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
|
||||
test_expect_success 'required process filter with clean error should fail' '
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
|
||||
test_config_global filter.protocol.required true &&
|
||||
rm -rf repo &&
|
||||
mkdir repo &&
|
||||
@ -706,8 +703,8 @@ test_expect_success PERL 'required process filter with clean error should fail'
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'process filter should restart after unexpected write failure' '
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
|
||||
test_expect_success 'process filter should restart after unexpected write failure' '
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
|
||||
rm -rf repo &&
|
||||
mkdir repo &&
|
||||
(
|
||||
@ -735,7 +732,7 @@ test_expect_success PERL 'process filter should restart after unexpected write f
|
||||
rm -f debug.log &&
|
||||
git checkout --quiet --no-progress . 2>git-stderr.log &&
|
||||
|
||||
grep "smudge write error at" git-stderr.log &&
|
||||
grep "smudge write error" git-stderr.log &&
|
||||
test_i18ngrep "error: external filter" git-stderr.log &&
|
||||
|
||||
cat >expected.log <<-EOF &&
|
||||
@ -761,8 +758,8 @@ test_expect_success PERL 'process filter should restart after unexpected write f
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'process filter should not be restarted if it signals an error' '
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
|
||||
test_expect_success 'process filter should not be restarted if it signals an error' '
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
|
||||
rm -rf repo &&
|
||||
mkdir repo &&
|
||||
(
|
||||
@ -804,8 +801,8 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'process filter abort stops processing of all further files' '
|
||||
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
|
||||
test_expect_success 'process filter abort stops processing of all further files' '
|
||||
test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
|
||||
rm -rf repo &&
|
||||
mkdir repo &&
|
||||
(
|
||||
@ -861,10 +858,10 @@ test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'delayed checkout in process filter' '
|
||||
test_config_global filter.a.process "rot13-filter.pl a.log clean smudge delay" &&
|
||||
test_expect_success 'delayed checkout in process filter' '
|
||||
test_config_global filter.a.process "test-tool rot13-filter --log=a.log clean smudge delay" &&
|
||||
test_config_global filter.a.required true &&
|
||||
test_config_global filter.b.process "rot13-filter.pl b.log clean smudge delay" &&
|
||||
test_config_global filter.b.process "test-tool rot13-filter --log=b.log clean smudge delay" &&
|
||||
test_config_global filter.b.required true &&
|
||||
|
||||
rm -rf repo &&
|
||||
@ -940,8 +937,8 @@ test_expect_success PERL 'delayed checkout in process filter' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'missing file in delayed checkout' '
|
||||
test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
|
||||
test_expect_success 'missing file in delayed checkout' '
|
||||
test_config_global filter.bug.process "test-tool rot13-filter --log=bug.log clean smudge delay" &&
|
||||
test_config_global filter.bug.required true &&
|
||||
|
||||
rm -rf repo &&
|
||||
@ -960,8 +957,8 @@ test_expect_success PERL 'missing file in delayed checkout' '
|
||||
grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
|
||||
'
|
||||
|
||||
test_expect_success PERL 'invalid file in delayed checkout' '
|
||||
test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
|
||||
test_expect_success 'invalid file in delayed checkout' '
|
||||
test_config_global filter.bug.process "test-tool rot13-filter --log=bug.log clean smudge delay" &&
|
||||
test_config_global filter.bug.required true &&
|
||||
|
||||
rm -rf repo &&
|
||||
@ -990,10 +987,10 @@ do
|
||||
mode_prereq='UTF8_NFD_TO_NFC' ;;
|
||||
esac
|
||||
|
||||
test_expect_success PERL,SYMLINKS,$mode_prereq \
|
||||
test_expect_success SYMLINKS,$mode_prereq \
|
||||
"delayed checkout with $mode-collision don't write to the wrong place" '
|
||||
test_config_global filter.delay.process \
|
||||
"\"$TEST_ROOT/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
|
||||
"test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
|
||||
test_config_global filter.delay.required true &&
|
||||
|
||||
git init $mode-collision &&
|
||||
@ -1026,12 +1023,12 @@ do
|
||||
'
|
||||
done
|
||||
|
||||
test_expect_success PERL,SYMLINKS,CASE_INSENSITIVE_FS \
|
||||
test_expect_success SYMLINKS,CASE_INSENSITIVE_FS \
|
||||
"delayed checkout with submodule collision don't write to the wrong place" '
|
||||
git init collision-with-submodule &&
|
||||
(
|
||||
cd collision-with-submodule &&
|
||||
git config filter.delay.process "\"$TEST_ROOT/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
|
||||
git config filter.delay.process "test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
|
||||
git config filter.delay.required true &&
|
||||
|
||||
# We need Git to treat the submodule "a" and the
|
||||
@ -1062,11 +1059,11 @@ test_expect_success PERL,SYMLINKS,CASE_INSENSITIVE_FS \
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success PERL 'setup for progress tests' '
|
||||
test_expect_success 'setup for progress tests' '
|
||||
git init progress &&
|
||||
(
|
||||
cd progress &&
|
||||
git config filter.delay.process "rot13-filter.pl delay-progress.log clean smudge delay" &&
|
||||
git config filter.delay.process "test-tool rot13-filter --log=delay-progress.log clean smudge delay" &&
|
||||
git config filter.delay.required true &&
|
||||
|
||||
echo "*.a filter=delay" >.gitattributes &&
|
||||
@ -1132,12 +1129,12 @@ do
|
||||
'
|
||||
done
|
||||
|
||||
test_expect_success PERL 'delayed checkout correctly reports the number of updated entries' '
|
||||
test_expect_success 'delayed checkout correctly reports the number of updated entries' '
|
||||
rm -rf repo &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
git config filter.delay.process "../rot13-filter.pl delayed.log clean smudge delay" &&
|
||||
git config filter.delay.process "test-tool rot13-filter --log=delayed.log clean smudge delay" &&
|
||||
git config filter.delay.required true &&
|
||||
|
||||
echo "*.a filter=delay" >.gitattributes &&
|
||||
|
@ -1,247 +0,0 @@
|
||||
#
|
||||
# Example implementation for the Git filter protocol version 2
|
||||
# See Documentation/gitattributes.txt, section "Filter Protocol"
|
||||
#
|
||||
# Usage: rot13-filter.pl [--always-delay] <log path> <capabilities>
|
||||
#
|
||||
# Log path defines a debug log file that the script writes to. The
|
||||
# subsequent arguments define a list of supported protocol capabilities
|
||||
# ("clean", "smudge", etc).
|
||||
#
|
||||
# When --always-delay is given all pathnames with the "can-delay" flag
|
||||
# that don't appear on the list bellow are delayed with a count of 1
|
||||
# (see more below).
|
||||
#
|
||||
# This implementation supports special test cases:
|
||||
# (1) If data with the pathname "clean-write-fail.r" is processed with
|
||||
# a "clean" operation then the write operation will die.
|
||||
# (2) If data with the pathname "smudge-write-fail.r" is processed with
|
||||
# a "smudge" operation then the write operation will die.
|
||||
# (3) If data with the pathname "error.r" is processed with any
|
||||
# operation then the filter signals that it cannot or does not want
|
||||
# to process the file.
|
||||
# (4) If data with the pathname "abort.r" is processed with any
|
||||
# operation then the filter signals that it cannot or does not want
|
||||
# to process the file and any file after that is processed with the
|
||||
# same command.
|
||||
# (5) If data with a pathname that is a key in the DELAY hash is
|
||||
# requested (e.g. "test-delay10.a") then the filter responds with
|
||||
# a "delay" status and sets the "requested" field in the DELAY hash.
|
||||
# The filter will signal the availability of this object after
|
||||
# "count" (field in DELAY hash) "list_available_blobs" commands.
|
||||
# (6) If data with the pathname "missing-delay.a" is processed that the
|
||||
# filter will drop the path from the "list_available_blobs" response.
|
||||
# (7) If data with the pathname "invalid-delay.a" is processed that the
|
||||
# filter will add the path "unfiltered" which was not delayed before
|
||||
# to the "list_available_blobs" response.
|
||||
#
|
||||
|
||||
use 5.008;
|
||||
sub gitperllib {
|
||||
# Git assumes that all path lists are Unix-y colon-separated ones. But
|
||||
# when the Git for Windows executes the test suite, its MSYS2 Bash
|
||||
# calls git.exe, and colon-separated path lists are converted into
|
||||
# Windows-y semicolon-separated lists of *Windows* paths (which
|
||||
# naturally contain a colon after the drive letter, so splitting by
|
||||
# colons simply does not cut it).
|
||||
#
|
||||
# Detect semicolon-separated path list and handle them appropriately.
|
||||
|
||||
if ($ENV{GITPERLLIB} =~ /;/) {
|
||||
return split(/;/, $ENV{GITPERLLIB});
|
||||
}
|
||||
return split(/:/, $ENV{GITPERLLIB});
|
||||
}
|
||||
use lib (gitperllib());
|
||||
use strict;
|
||||
use warnings;
|
||||
use IO::File;
|
||||
use Git::Packet;
|
||||
|
||||
my $MAX_PACKET_CONTENT_SIZE = 65516;
|
||||
|
||||
my $always_delay = 0;
|
||||
if ( $ARGV[0] eq '--always-delay' ) {
|
||||
$always_delay = 1;
|
||||
shift @ARGV;
|
||||
}
|
||||
|
||||
my $log_file = shift @ARGV;
|
||||
my @capabilities = @ARGV;
|
||||
|
||||
open my $debug, ">>", $log_file or die "cannot open log file: $!";
|
||||
|
||||
my %DELAY = (
|
||||
'test-delay10.a' => { "requested" => 0, "count" => 1 },
|
||||
'test-delay11.a' => { "requested" => 0, "count" => 1 },
|
||||
'test-delay20.a' => { "requested" => 0, "count" => 2 },
|
||||
'test-delay10.b' => { "requested" => 0, "count" => 1 },
|
||||
'missing-delay.a' => { "requested" => 0, "count" => 1 },
|
||||
'invalid-delay.a' => { "requested" => 0, "count" => 1 },
|
||||
);
|
||||
|
||||
sub rot13 {
|
||||
my $str = shift;
|
||||
$str =~ y/A-Za-z/N-ZA-Mn-za-m/;
|
||||
return $str;
|
||||
}
|
||||
|
||||
print $debug "START\n";
|
||||
$debug->flush();
|
||||
|
||||
packet_initialize("git-filter", 2);
|
||||
|
||||
my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay");
|
||||
packet_check_and_write_capabilities(\%remote_caps, @capabilities);
|
||||
|
||||
print $debug "init handshake complete\n";
|
||||
$debug->flush();
|
||||
|
||||
while (1) {
|
||||
my ( $res, $command ) = packet_key_val_read("command");
|
||||
if ( $res == -1 ) {
|
||||
print $debug "STOP\n";
|
||||
exit();
|
||||
}
|
||||
print $debug "IN: $command";
|
||||
$debug->flush();
|
||||
|
||||
if ( $command eq "list_available_blobs" ) {
|
||||
# Flush
|
||||
packet_compare_lists([1, ""], packet_bin_read()) ||
|
||||
die "bad list_available_blobs end";
|
||||
|
||||
foreach my $pathname ( sort keys %DELAY ) {
|
||||
if ( $DELAY{$pathname}{"requested"} >= 1 ) {
|
||||
$DELAY{$pathname}{"count"} = $DELAY{$pathname}{"count"} - 1;
|
||||
if ( $pathname eq "invalid-delay.a" ) {
|
||||
# Send Git a pathname that was not delayed earlier
|
||||
packet_txt_write("pathname=unfiltered");
|
||||
}
|
||||
if ( $pathname eq "missing-delay.a" ) {
|
||||
# Do not signal Git that this file is available
|
||||
} elsif ( $DELAY{$pathname}{"count"} == 0 ) {
|
||||
print $debug " $pathname";
|
||||
packet_txt_write("pathname=$pathname");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packet_flush();
|
||||
|
||||
print $debug " [OK]\n";
|
||||
$debug->flush();
|
||||
packet_txt_write("status=success");
|
||||
packet_flush();
|
||||
} else {
|
||||
my ( $res, $pathname ) = packet_key_val_read("pathname");
|
||||
if ( $res == -1 ) {
|
||||
die "unexpected EOF while expecting pathname";
|
||||
}
|
||||
print $debug " $pathname";
|
||||
$debug->flush();
|
||||
|
||||
# Read until flush
|
||||
my ( $done, $buffer ) = packet_txt_read();
|
||||
while ( $buffer ne '' ) {
|
||||
if ( $buffer eq "can-delay=1" ) {
|
||||
if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
|
||||
$DELAY{$pathname}{"requested"} = 1;
|
||||
} elsif ( !exists $DELAY{$pathname} and $always_delay ) {
|
||||
$DELAY{$pathname} = { "requested" => 1, "count" => 1 };
|
||||
}
|
||||
} elsif ($buffer =~ /^(ref|treeish|blob)=/) {
|
||||
print $debug " $buffer";
|
||||
} else {
|
||||
# In general, filters need to be graceful about
|
||||
# new metadata, since it's documented that we
|
||||
# can pass any key-value pairs, but for tests,
|
||||
# let's be a little stricter.
|
||||
die "Unknown message '$buffer'";
|
||||
}
|
||||
|
||||
( $done, $buffer ) = packet_txt_read();
|
||||
}
|
||||
if ( $done == -1 ) {
|
||||
die "unexpected EOF after pathname '$pathname'";
|
||||
}
|
||||
|
||||
my $input = "";
|
||||
{
|
||||
binmode(STDIN);
|
||||
my $buffer;
|
||||
my $done = 0;
|
||||
while ( !$done ) {
|
||||
( $done, $buffer ) = packet_bin_read();
|
||||
$input .= $buffer;
|
||||
}
|
||||
if ( $done == -1 ) {
|
||||
die "unexpected EOF while reading input for '$pathname'";
|
||||
}
|
||||
print $debug " " . length($input) . " [OK] -- ";
|
||||
$debug->flush();
|
||||
}
|
||||
|
||||
my $output;
|
||||
if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
|
||||
$output = $DELAY{$pathname}{"output"}
|
||||
} elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
|
||||
$output = "";
|
||||
} elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
|
||||
$output = rot13($input);
|
||||
} elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
|
||||
$output = rot13($input);
|
||||
} else {
|
||||
die "bad command '$command'";
|
||||
}
|
||||
|
||||
if ( $pathname eq "error.r" ) {
|
||||
print $debug "[ERROR]\n";
|
||||
$debug->flush();
|
||||
packet_txt_write("status=error");
|
||||
packet_flush();
|
||||
} elsif ( $pathname eq "abort.r" ) {
|
||||
print $debug "[ABORT]\n";
|
||||
$debug->flush();
|
||||
packet_txt_write("status=abort");
|
||||
packet_flush();
|
||||
} elsif ( $command eq "smudge" and
|
||||
exists $DELAY{$pathname} and
|
||||
$DELAY{$pathname}{"requested"} == 1 ) {
|
||||
print $debug "[DELAYED]\n";
|
||||
$debug->flush();
|
||||
packet_txt_write("status=delayed");
|
||||
packet_flush();
|
||||
$DELAY{$pathname}{"requested"} = 2;
|
||||
$DELAY{$pathname}{"output"} = $output;
|
||||
} else {
|
||||
packet_txt_write("status=success");
|
||||
packet_flush();
|
||||
|
||||
if ( $pathname eq "${command}-write-fail.r" ) {
|
||||
print $debug "[WRITE FAIL]\n";
|
||||
$debug->flush();
|
||||
die "${command} write error";
|
||||
}
|
||||
|
||||
print $debug "OUT: " . length($output) . " ";
|
||||
$debug->flush();
|
||||
|
||||
while ( length($output) > 0 ) {
|
||||
my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
|
||||
packet_bin_write($packet);
|
||||
# dots represent the number of packets
|
||||
print $debug ".";
|
||||
if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
|
||||
$output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
|
||||
} else {
|
||||
$output = "";
|
||||
}
|
||||
}
|
||||
packet_flush();
|
||||
print $debug " [OK]\n";
|
||||
$debug->flush();
|
||||
packet_flush();
|
||||
}
|
||||
}
|
||||
}
|
@ -230,12 +230,9 @@ test_expect_success SYMLINKS 'parallel checkout checks for symlinks in leading d
|
||||
# check the final report including sequential, parallel, and delayed entries
|
||||
# all at the same time. So we must have finer control of the parallel checkout
|
||||
# variables.
|
||||
test_expect_success PERL '"git checkout ." report should not include failed entries' '
|
||||
write_script rot13-filter.pl "$PERL_PATH" \
|
||||
<"$TEST_DIRECTORY"/t0021/rot13-filter.pl &&
|
||||
|
||||
test_expect_success '"git checkout ." report should not include failed entries' '
|
||||
test_config_global filter.delay.process \
|
||||
"\"$(pwd)/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
|
||||
"test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
|
||||
test_config_global filter.delay.required true &&
|
||||
test_config_global filter.cat.clean cat &&
|
||||
test_config_global filter.cat.smudge cat &&
|
||||
|
@ -138,12 +138,9 @@ test_expect_success 'parallel-checkout and external filter' '
|
||||
# The delayed queue is independent from the parallel queue, and they should be
|
||||
# able to work together in the same checkout process.
|
||||
#
|
||||
test_expect_success PERL 'parallel-checkout and delayed checkout' '
|
||||
write_script rot13-filter.pl "$PERL_PATH" \
|
||||
<"$TEST_DIRECTORY"/t0021/rot13-filter.pl &&
|
||||
|
||||
test_expect_success 'parallel-checkout and delayed checkout' '
|
||||
test_config_global filter.delay.process \
|
||||
"\"$(pwd)/rot13-filter.pl\" --always-delay \"$(pwd)/delayed.log\" clean smudge delay" &&
|
||||
"test-tool rot13-filter --always-delay --log=\"$(pwd)/delayed.log\" clean smudge delay" &&
|
||||
test_config_global filter.delay.required true &&
|
||||
|
||||
echo "abcd" >original &&
|
||||
|
Loading…
Reference in New Issue
Block a user