Subject: [PATCH v3 25/26] file-watcher: tests for the client side

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Mon, 3 Feb 2014 11:29:13 +0700

Similar to t7513, system inotify is taken out to give us better
controlled environment.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 file-watcher-lib.c                   |  21 ++--
 t/t7514-file-watcher-lib.sh (new +x) | 190 +++++++++++++++++++++++++++++++++++
 test-file-watcher.c                  |  15 +++
 3 files changed, 219 insertions(+), 7 deletions(-)
 create mode 100755 t/t7514-file-watcher-lib.sh

diff --git a/file-watcher-lib.c b/file-watcher-lib.c
index 93afb52..8cc3b73 100644
--- a/file-watcher-lib.c
+++ b/file-watcher-lib.c
@@ -23,7 +23,8 @@ static int connect_watcher(const char *path)
 	return fd;
 }
 
-static void reset_watches(struct index_state *istate, int disconnect)
+static void reset_watches(struct index_state *istate, int disconnect,
+			  const char *reason)
 {
 	int i, changed = 0;
 	if (istate->updated_entries) {
@@ -45,6 +46,8 @@ static void reset_watches(struct index_state *istate, int disconnect)
 		close(istate->watcher);
 		istate->watcher = -1;
 	}
+	trace_printf_key("GIT_TRACE_WATCHER", "reset%s: %s\n",
+			 disconnect ? "/disconnect" : "", reason);
 }
 
 static void mark_ce_valid(struct index_state *istate)
@@ -53,15 +56,16 @@ static void mark_ce_valid(struct index_state *istate)
 	char *line, *end;
 	int i, len;
 	unsigned long n;
+	trace_printf_key("GIT_TRACE_WATCHER", "mark_ce_valid\n");
 	if (packet_write_timeout(istate->watcher, WAIT_TIME, "get-changed") <= 0 ||
 	    !(line = packet_read_line_timeout(istate->watcher, WAIT_TIME, &len)) ||
 	    !starts_with(line, "changed ")) {
-		reset_watches(istate, 1);
+		reset_watches(istate, 1, "invalid get-changed response");
 		return;
 	}
 	n = strtoul(line + 8, &end, 10);
 	if (end != line + len) {
-		reset_watches(istate, 1);
+		reset_watches(istate, 1, "invalid get-changed response");
 		return;
 	}
 	if (!n)
@@ -69,7 +73,7 @@ static void mark_ce_valid(struct index_state *istate)
 	strbuf_grow(&sb, n);
 	if (read_in_full_timeout(istate->watcher, sb.buf, n, WAIT_TIME) != n) {
 		strbuf_release(&sb);
-		reset_watches(istate, 1);
+		reset_watches(istate, 1, "invalid get-changed payload");
 		return;
 	}
 	line = sb.buf;
@@ -131,7 +135,7 @@ void open_watcher(struct index_state *istate)
 	char *msg;
 
 	if (!get_git_work_tree()) {
-		reset_watches(istate, 1);
+		reset_watches(istate, 1, "no worktree");
 		return;
 	}
 
@@ -165,10 +169,11 @@ void open_watcher(struct index_state *istate)
 	}
 
 	istate->watcher = connect_watcher(watcher_path);
+	trace_printf_key("GIT_TRACE_WATCHER", "open watcher %d\n", istate->watcher);
 	if (packet_write_timeout(istate->watcher, WAIT_TIME, "hello") <= 0 ||
 	    (msg = packet_read_line_timeout(istate->watcher, WAIT_TIME, NULL)) == NULL ||
 	    strcmp(msg, "hello")) {
-		reset_watches(istate, 1);
+		reset_watches(istate, 1, "invalid hello response");
 		return;
 	}
 
@@ -177,7 +182,7 @@ void open_watcher(struct index_state *istate)
 				 get_git_work_tree()) <= 0 ||
 	    (msg = packet_read_line_timeout(istate->watcher, WAIT_TIME, NULL)) == NULL ||
 	    strcmp(msg, "ok")) {
-		reset_watches(istate, 0);
+		reset_watches(istate, 0, "inconsistent");
 		istate->update_watches = 1;
 		return;
 	}
@@ -265,6 +270,7 @@ void watch_entries(struct index_state *istate)
 			nr++;
 	if (nr < watch_lowerlimit)
 		return;
+	trace_printf_key("GIT_TRACE_WATCHER", "watch %d\n", nr);
 	sorted = xmalloc(sizeof(*sorted) * nr);
 	for (i = nr = 0; i < istate->cache_nr; i++)
 		if (ce_watchable(istate->cache[i], now))
@@ -280,6 +286,7 @@ void close_watcher(struct index_state *istate, const unsigned char *sha1)
 	int len, i, nr;
 	if (istate->watcher <= 0)
 		return;
+	trace_printf_key("GIT_TRACE_WATCHER", "close watcher\n");
 	if (packet_write_timeout(istate->watcher, WAIT_TIME,
 				 "new-index %s", sha1_to_hex(sha1)) <= 0)
 		goto done;
diff --git a/t/t7514-file-watcher-lib.sh b/t/t7514-file-watcher-lib.sh
new file mode 100755
index 0000000..8dabb13
--- /dev/null
+++ b/t/t7514-file-watcher-lib.sh
@@ -0,0 +1,190 @@
+#!/bin/sh
+
+test_description='File watcher daemon and client tests'
+
+. ./test-lib.sh
+
+if git file-watcher --check-support && test_have_prereq POSIXPERM; then
+	: # good
+else
+	skip_all="file-watcher not supported on this system"
+	test_done
+fi
+
+kill_it() {
+	test-file-watcher "$1" <<EOF >/dev/null
+<die
+>see you
+EOF
+}
+
+GIT_TEST_WATCHER=2
+GIT_TEST_WATCHER_PATH="$TRASH_DIRECTORY"
+export GIT_TEST_WATCHER GIT_TEST_WATCHER_PATH
+
+test_expect_success 'setup' '
+	chmod 700 . &&
+	mkdir foo bar &&
+	touch abc foo/def bar/ghi &&
+	git add . &&
+	git file-watcher --detach . &&
+	cat <<EOF >expect &&
+<test-mode
+>test mode on
+EOF
+	test-file-watcher . <expect >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'initial opening sequence' '
+	SIG=`test-file-watcher --index-signature .git/index` &&
+	rm actual &&
+	GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git ls-files >/dev/null &&
+	cat <<EOF >expect &&
+packet:          git> hello
+packet:          git< hello
+packet:          git> index $SIG $TRASH_DIRECTORY
+packet:          git< inconsistent
+packet:          git> watch 20
+packet:          git< watched 3
+EOF
+	test_cmp expect actual &&
+
+	# second time gives the same result because file-watch has not
+	# received new-index
+	GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual2" git ls-files >/dev/null &&
+	test_cmp expect actual2
+'
+
+test_expect_success 'full sequence' '
+	SIG=`test-file-watcher --index-signature .git/index` &&
+	rm actual &&
+	GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git status >/dev/null &&
+	SIG2=`test-file-watcher --index-signature .git/index` &&
+	cat <<EOF >expect &&
+packet:          git> hello
+packet:          git< hello
+packet:          git> index $SIG $TRASH_DIRECTORY
+packet:          git< inconsistent
+packet:          git> watch 20
+packet:          git< watched 3
+packet:          git> new-index $SIG2
+packet:          git> unchange 0
+EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'full sequence after file-watcher is active' '
+	SIG=`test-file-watcher --index-signature .git/index` &&
+	rm actual &&
+	GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git ls-files -v >paths.actual &&
+	cat <<EOF >expect &&
+packet:          git> hello
+packet:          git< hello
+packet:          git> index $SIG $TRASH_DIRECTORY
+packet:          git< ok
+packet:          git> get-changed
+packet:          git< changed 0
+EOF
+	test_cmp expect actual &&
+	cat <<EOF >paths.expect &&
+w abc
+w bar/ghi
+w foo/def
+EOF
+	test_cmp paths.expect paths.actual
+'
+
+test_expect_success 'inject a file change' '
+	echo modified >bar/ghi &&
+	SIG=`test-file-watcher --index-signature .git/index` &&
+	cat >expect <<EOF &&
+<hello
+>hello
+<index $SIG $TRASH_DIRECTORY
+>ok
+<inotify 2 IN_MODIFY ghi
+<dump changes
+>>changed
+>>bar/ghi
+EOF
+	test-file-watcher . >actual <expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git obtains the changes' '
+	SIG=`test-file-watcher --index-signature .git/index` &&
+	rm actual &&
+	GIT_TEST_WATCHER=1 GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git ls-files -v >paths.actual &&
+	cat <<EOF >expect &&
+packet:          git> hello
+packet:          git< hello
+packet:          git> index $SIG $TRASH_DIRECTORY
+packet:          git< ok
+packet:          git> get-changed
+packet:          git< changed 8
+EOF
+	test_cmp expect actual &&
+	cat <<EOF >paths.expect &&
+w abc
+H bar/ghi
+w foo/def
+EOF
+	test_cmp paths.expect paths.actual
+'
+
+test_expect_success 'sync file-watcher after index update' '
+	SIG=`test-file-watcher --index-signature .git/index` &&
+	rm actual &&
+	GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git status --porcelain | grep -vF "??" >paths.actual &&
+	SIG2=`test-file-watcher --index-signature .git/index` &&
+	cat <<EOF >expect &&
+packet:          git> hello
+packet:          git< hello
+packet:          git> index $SIG $TRASH_DIRECTORY
+packet:          git< ok
+packet:          git> get-changed
+packet:          git< changed 8
+packet:          git> watch 8
+packet:          git< watched 1
+packet:          git> new-index $SIG2
+packet:          git> unchange 8
+EOF
+	test_cmp expect actual &&
+	cat <<EOF >paths.expect &&
+A  abc
+AM bar/ghi
+A  foo/def
+EOF
+	test_cmp paths.expect paths.actual
+'
+
+test_expect_success 'make sure file-watcher cleans its changed list' '
+	SIG=`test-file-watcher --index-signature .git/index` &&
+	rm actual &&
+	GIT_TEST_WATCHER=1 GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git ls-files -v >paths.actual &&
+	cat <<EOF >expect &&
+packet:          git> hello
+packet:          git< hello
+packet:          git> index $SIG $TRASH_DIRECTORY
+packet:          git< ok
+packet:          git> get-changed
+packet:          git< changed 0
+EOF
+	test_cmp expect actual &&
+	cat <<EOF >paths.expect &&
+w abc
+H bar/ghi
+w foo/def
+EOF
+	test_cmp paths.expect paths.actual
+'
+
+test_expect_success 'closing the daemon' '
+	test-file-watcher . <<EOF >/dev/null
+<die
+>see you
+EOF
+'
+
+test_done
diff --git a/test-file-watcher.c b/test-file-watcher.c
index ffff198..77037e1 100644
--- a/test-file-watcher.c
+++ b/test-file-watcher.c
@@ -11,6 +11,21 @@ int main(int ac, char **av)
 	int last_command_is_reply = 0;
 	int fd;
 
+	if (!strcmp(av[1], "--index-signature")) {
+		struct stat st;
+		void *mmap;
+		if (lstat(av[2], &st))
+			die_errno("lstat");
+		fd = open(av[2], O_RDONLY);
+		if (fd < 0)
+			die_errno("open");
+		mmap = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+		if (mmap == MAP_FAILED)
+			die_errno("mmap");
+		printf("%s\n", sha1_to_hex((unsigned char*)mmap + st.st_size - 20));
+		return 0;
+	}
+
 	strbuf_addf(&sb, "%s/socket", av[1]);
 	fd = unix_stream_connect(sb.buf);
 	if (fd < 0)
-- 
1.8.5.2.240.g8478abd