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

Signed-off-by: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to