Git fork
1#!/bin/sh
2
3test_description='built-in file system watcher'
4
5. ./test-lib.sh
6
7if ! test_have_prereq FSMONITOR_DAEMON
8then
9 skip_all="fsmonitor--daemon is not supported on this platform"
10 test_done
11fi
12
13stop_daemon_delete_repo () {
14 r=$1 &&
15 test_might_fail git -C $r fsmonitor--daemon stop &&
16 rm -rf $1
17}
18
19start_daemon () {
20 r= tf= t2= tk= &&
21
22 while test "$#" -ne 0
23 do
24 case "$1" in
25 -C)
26 r="-C ${2?}"
27 shift
28 ;;
29 --tf)
30 tf="${2?}"
31 shift
32 ;;
33 --t2)
34 t2="${2?}"
35 shift
36 ;;
37 --tk)
38 tk="${2?}"
39 shift
40 ;;
41 -*)
42 BUG "error: unknown option: '$1'"
43 ;;
44 *)
45 BUG "error: unbound argument: '$1'"
46 ;;
47 esac
48 shift
49 done &&
50
51 (
52 if test -n "$tf"
53 then
54 GIT_TRACE_FSMONITOR="$tf"
55 export GIT_TRACE_FSMONITOR
56 fi &&
57
58 if test -n "$t2"
59 then
60 GIT_TRACE2_PERF="$t2"
61 export GIT_TRACE2_PERF
62 fi &&
63
64 if test -n "$tk"
65 then
66 GIT_TEST_FSMONITOR_TOKEN="$tk"
67 export GIT_TEST_FSMONITOR_TOKEN
68 fi &&
69
70 git $r fsmonitor--daemon start &&
71 git $r fsmonitor--daemon status
72 )
73}
74
75# Is a Trace2 data event present with the given catetory and key?
76# We do not care what the value is.
77#
78have_t2_data_event () {
79 c=$1 &&
80 k=$2 &&
81
82 grep -e '"event":"data".*"category":"'"$c"'".*"key":"'"$k"'"'
83}
84
85test_expect_success 'explicit daemon start and stop' '
86 test_when_finished "stop_daemon_delete_repo test_explicit" &&
87
88 git init test_explicit &&
89 start_daemon -C test_explicit &&
90
91 git -C test_explicit fsmonitor--daemon stop &&
92 test_must_fail git -C test_explicit fsmonitor--daemon status
93'
94
95test_expect_success 'implicit daemon start' '
96 test_when_finished "stop_daemon_delete_repo test_implicit" &&
97
98 git init test_implicit &&
99 test_must_fail git -C test_implicit fsmonitor--daemon status &&
100
101 # query will implicitly start the daemon.
102 #
103 # for test-script simplicity, we send a V1 timestamp rather than
104 # a V2 token. either way, the daemon response to any query contains
105 # a new V2 token. (the daemon may complain that we sent a V1 request,
106 # but this test case is only concerned with whether the daemon was
107 # implicitly started.)
108
109 GIT_TRACE2_EVENT="$PWD/.git/trace" \
110 test-tool -C test_implicit fsmonitor-client query --token 0 >actual &&
111 nul_to_q <actual >actual.filtered &&
112 grep "builtin:" actual.filtered &&
113
114 # confirm that a daemon was started in the background.
115 #
116 # since the mechanism for starting the background daemon is platform
117 # dependent, just confirm that the foreground command received a
118 # response from the daemon.
119
120 have_t2_data_event fsm_client query/response-length <.git/trace &&
121
122 git -C test_implicit fsmonitor--daemon status &&
123 git -C test_implicit fsmonitor--daemon stop &&
124 test_must_fail git -C test_implicit fsmonitor--daemon status
125'
126
127# Verify that the daemon has shutdown. Spin a few seconds to
128# make the test a little more robust during CI testing.
129#
130# We're looking for an implicit shutdown, such as when we delete or
131# rename the ".git" directory. Our delete/rename will cause a file
132# system event that the daemon will see and the daemon will
133# auto-shutdown as soon as it sees it. But this is racy with our `git
134# fsmonitor--daemon status` commands (and we cannot use a cookie file
135# here to help us). So spin a little and give the daemon a chance to
136# see the event. (This is primarily for underpowered CI build/test
137# machines (where it might take a moment to wake and reschedule the
138# daemon process) to avoid false alarms during test runs.)
139#
140IMPLICIT_TIMEOUT=5
141
142verify_implicit_shutdown () {
143 r=$1 &&
144
145 k=0 &&
146 while test "$k" -lt $IMPLICIT_TIMEOUT
147 do
148 git -C $r fsmonitor--daemon status || return 0
149
150 sleep 1
151 k=$(( $k + 1 ))
152 done &&
153
154 return 1
155}
156
157test_expect_success 'implicit daemon stop (delete .git)' '
158 test_when_finished "stop_daemon_delete_repo test_implicit_1" &&
159
160 git init test_implicit_1 &&
161
162 start_daemon -C test_implicit_1 &&
163
164 # deleting the .git directory will implicitly stop the daemon.
165 rm -rf test_implicit_1/.git &&
166
167 # [1] Create an empty .git directory so that the following Git
168 # command will stay relative to the `-C` directory.
169 #
170 # Without this, the Git command will override the requested
171 # -C argument and crawl out to the containing Git source tree.
172 # This would make the test result dependent upon whether we
173 # were using fsmonitor on our development worktree.
174 #
175 mkdir test_implicit_1/.git &&
176
177 verify_implicit_shutdown test_implicit_1
178'
179
180test_expect_success 'implicit daemon stop (rename .git)' '
181 test_when_finished "stop_daemon_delete_repo test_implicit_2" &&
182
183 git init test_implicit_2 &&
184
185 start_daemon -C test_implicit_2 &&
186
187 # renaming the .git directory will implicitly stop the daemon.
188 mv test_implicit_2/.git test_implicit_2/.xxx &&
189
190 # See [1] above.
191 #
192 mkdir test_implicit_2/.git &&
193
194 verify_implicit_shutdown test_implicit_2
195'
196
197# File systems on Windows may or may not have shortnames.
198# This is a volume-specific setting on modern systems.
199# "C:/" drives are required to have them enabled. Other
200# hard drives default to disabled.
201#
202# This is a crude test to see if shortnames are enabled
203# on the volume containing the test directory. It is
204# crude, but it does not require elevation like `fsutil`.
205#
206test_lazy_prereq SHORTNAMES '
207 mkdir .foo &&
208 test -d "FOO~1"
209'
210
211# Here we assume that the shortname of ".git" is "GIT~1".
212test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~1)' '
213 test_when_finished "stop_daemon_delete_repo test_implicit_1s" &&
214
215 git init test_implicit_1s &&
216
217 start_daemon -C test_implicit_1s &&
218
219 # renaming the .git directory will implicitly stop the daemon.
220 # this moves {.git, GIT~1} to {.gitxyz, GITXYZ~1}.
221 # the rename-from FS Event will contain the shortname.
222 #
223 mv test_implicit_1s/GIT~1 test_implicit_1s/.gitxyz &&
224
225 # See [1] above.
226 # this moves {.gitxyz, GITXYZ~1} to {.git, GIT~1}.
227 mv test_implicit_1s/.gitxyz test_implicit_1s/.git &&
228
229 verify_implicit_shutdown test_implicit_1s
230'
231
232# Here we first create a file with LONGNAME of "GIT~1" before
233# we create the repo. This will cause the shortname of ".git"
234# to be "GIT~2".
235test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~2)' '
236 test_when_finished "stop_daemon_delete_repo test_implicit_1s2" &&
237
238 mkdir test_implicit_1s2 &&
239 echo HELLO >test_implicit_1s2/GIT~1 &&
240 git init test_implicit_1s2 &&
241
242 test_path_is_file test_implicit_1s2/GIT~1 &&
243 test_path_is_dir test_implicit_1s2/GIT~2 &&
244
245 start_daemon -C test_implicit_1s2 &&
246
247 # renaming the .git directory will implicitly stop the daemon.
248 # the rename-from FS Event will contain the shortname.
249 #
250 mv test_implicit_1s2/GIT~2 test_implicit_1s2/.gitxyz &&
251
252 # See [1] above.
253 mv test_implicit_1s2/.gitxyz test_implicit_1s2/.git &&
254
255 verify_implicit_shutdown test_implicit_1s2
256'
257
258test_expect_success 'cannot start multiple daemons' '
259 test_when_finished "stop_daemon_delete_repo test_multiple" &&
260
261 git init test_multiple &&
262
263 start_daemon -C test_multiple &&
264
265 test_must_fail git -C test_multiple fsmonitor--daemon start 2>actual &&
266 grep "fsmonitor--daemon is already running" actual &&
267
268 git -C test_multiple fsmonitor--daemon stop &&
269 test_must_fail git -C test_multiple fsmonitor--daemon status
270'
271
272# These tests use the main repo in the trash directory
273
274test_expect_success 'setup' '
275 >tracked &&
276 >modified &&
277 >delete &&
278 >rename &&
279 mkdir dir1 &&
280 >dir1/tracked &&
281 >dir1/modified &&
282 >dir1/delete &&
283 >dir1/rename &&
284 mkdir dir2 &&
285 >dir2/tracked &&
286 >dir2/modified &&
287 >dir2/delete &&
288 >dir2/rename &&
289 mkdir dirtorename &&
290 >dirtorename/a &&
291 >dirtorename/b &&
292
293 cat >.gitignore <<-\EOF &&
294 .gitignore
295 expect*
296 actual*
297 flush*
298 trace*
299 EOF
300
301 mkdir -p T1/T2/T3/T4 &&
302 echo 1 >T1/F1 &&
303 echo 1 >T1/T2/F1 &&
304 echo 1 >T1/T2/T3/F1 &&
305 echo 1 >T1/T2/T3/T4/F1 &&
306 echo 2 >T1/F2 &&
307 echo 2 >T1/T2/F2 &&
308 echo 2 >T1/T2/T3/F2 &&
309 echo 2 >T1/T2/T3/T4/F2 &&
310
311 git -c core.fsmonitor=false add . &&
312 test_tick &&
313 git -c core.fsmonitor=false commit -m initial &&
314
315 git config core.fsmonitor true
316'
317
318# The test already explicitly stopped (or tried to stop) the daemon.
319# This is here in case something else fails first.
320#
321redundant_stop_daemon () {
322 test_might_fail git fsmonitor--daemon stop
323}
324
325test_expect_success 'update-index implicitly starts daemon' '
326 test_when_finished redundant_stop_daemon &&
327
328 test_must_fail git fsmonitor--daemon status &&
329
330 GIT_TRACE2_EVENT="$PWD/.git/trace_implicit_1" \
331 git update-index --fsmonitor &&
332
333 git fsmonitor--daemon status &&
334 test_might_fail git fsmonitor--daemon stop &&
335
336 # Confirm that the trace2 log contains a record of the
337 # daemon starting.
338 test_subcommand git fsmonitor--daemon start <.git/trace_implicit_1
339'
340
341test_expect_success 'status implicitly starts daemon' '
342 test_when_finished redundant_stop_daemon &&
343
344 test_must_fail git fsmonitor--daemon status &&
345
346 GIT_TRACE2_EVENT="$PWD/.git/trace_implicit_2" \
347 git status >actual &&
348
349 git fsmonitor--daemon status &&
350 test_might_fail git fsmonitor--daemon stop &&
351
352 # Confirm that the trace2 log contains a record of the
353 # daemon starting.
354 test_subcommand git fsmonitor--daemon start <.git/trace_implicit_2
355'
356
357edit_files () {
358 echo 1 >modified &&
359 echo 2 >dir1/modified &&
360 echo 3 >dir2/modified &&
361 >dir1/untracked
362}
363
364delete_files () {
365 rm -f delete &&
366 rm -f dir1/delete &&
367 rm -f dir2/delete
368}
369
370create_files () {
371 echo 1 >new &&
372 echo 2 >dir1/new &&
373 echo 3 >dir2/new
374}
375
376rename_files () {
377 mv rename renamed &&
378 mv dir1/rename dir1/renamed &&
379 mv dir2/rename dir2/renamed
380}
381
382file_to_directory () {
383 rm -f delete &&
384 mkdir delete &&
385 echo 1 >delete/new
386}
387
388directory_to_file () {
389 rm -rf dir1 &&
390 echo 1 >dir1
391}
392
393move_directory_contents_deeper() {
394 mkdir T1/_new_ &&
395 mv T1/[A-Z]* T1/_new_
396}
397
398move_directory_up() {
399 mv T1/T2/T3 T1
400}
401
402move_directory() {
403 mv T1/T2/T3 T1/T2/NewT3
404}
405
406# The next few test cases confirm that our fsmonitor daemon sees each type
407# of OS filesystem notification that we care about. At this layer we just
408# ensure we are getting the OS notifications and do not try to confirm what
409# is reported by `git status`.
410#
411# We run a simple query after modifying the filesystem just to introduce
412# a bit of a delay so that the trace logging from the daemon has time to
413# get flushed to disk.
414#
415# We `reset` and `clean` at the bottom of each test (and before stopping the
416# daemon) because these commands might implicitly restart the daemon.
417
418clean_up_repo_and_stop_daemon () {
419 git reset --hard HEAD &&
420 git clean -fd &&
421 test_might_fail git fsmonitor--daemon stop &&
422 rm -f .git/trace
423}
424
425test_expect_success 'edit some files' '
426 test_when_finished clean_up_repo_and_stop_daemon &&
427
428 start_daemon --tf "$PWD/.git/trace" &&
429
430 edit_files &&
431
432 test-tool fsmonitor-client query --token 0 &&
433
434 grep "^event: dir1/modified$" .git/trace &&
435 grep "^event: dir2/modified$" .git/trace &&
436 grep "^event: modified$" .git/trace &&
437 grep "^event: dir1/untracked$" .git/trace
438'
439
440test_expect_success 'create some files' '
441 test_when_finished clean_up_repo_and_stop_daemon &&
442
443 start_daemon --tf "$PWD/.git/trace" &&
444
445 create_files &&
446
447 test-tool fsmonitor-client query --token 0 &&
448
449 grep "^event: dir1/new$" .git/trace &&
450 grep "^event: dir2/new$" .git/trace &&
451 grep "^event: new$" .git/trace
452'
453
454test_expect_success 'delete some files' '
455 test_when_finished clean_up_repo_and_stop_daemon &&
456
457 start_daemon --tf "$PWD/.git/trace" &&
458
459 delete_files &&
460
461 test-tool fsmonitor-client query --token 0 &&
462
463 grep "^event: dir1/delete$" .git/trace &&
464 grep "^event: dir2/delete$" .git/trace &&
465 grep "^event: delete$" .git/trace
466'
467
468test_expect_success 'rename some files' '
469 test_when_finished clean_up_repo_and_stop_daemon &&
470
471 start_daemon --tf "$PWD/.git/trace" &&
472
473 rename_files &&
474
475 test-tool fsmonitor-client query --token 0 &&
476
477 grep "^event: dir1/rename$" .git/trace &&
478 grep "^event: dir2/rename$" .git/trace &&
479 grep "^event: rename$" .git/trace &&
480 grep "^event: dir1/renamed$" .git/trace &&
481 grep "^event: dir2/renamed$" .git/trace &&
482 grep "^event: renamed$" .git/trace
483'
484
485test_expect_success 'rename directory' '
486 test_when_finished clean_up_repo_and_stop_daemon &&
487
488 start_daemon --tf "$PWD/.git/trace" &&
489
490 mv dirtorename dirrenamed &&
491
492 test-tool fsmonitor-client query --token 0 &&
493
494 grep "^event: dirtorename/*$" .git/trace &&
495 grep "^event: dirrenamed/*$" .git/trace
496'
497
498test_expect_success 'file changes to directory' '
499 test_when_finished clean_up_repo_and_stop_daemon &&
500
501 start_daemon --tf "$PWD/.git/trace" &&
502
503 file_to_directory &&
504
505 test-tool fsmonitor-client query --token 0 &&
506
507 grep "^event: delete$" .git/trace &&
508 grep "^event: delete/new$" .git/trace
509'
510
511test_expect_success 'directory changes to a file' '
512 test_when_finished clean_up_repo_and_stop_daemon &&
513
514 start_daemon --tf "$PWD/.git/trace" &&
515
516 directory_to_file &&
517
518 test-tool fsmonitor-client query --token 0 &&
519
520 grep "^event: dir1$" .git/trace
521'
522
523# The next few test cases exercise the token-resync code. When filesystem
524# drops events (because of filesystem velocity or because the daemon isn't
525# polling fast enough), we need to discard the cached data (relative to the
526# current token) and start collecting events under a new token.
527#
528# the 'test-tool fsmonitor-client flush' command can be used to send a
529# "flush" message to a running daemon and ask it to do a flush/resync.
530
531test_expect_success 'flush cached data' '
532 test_when_finished "stop_daemon_delete_repo test_flush" &&
533
534 git init test_flush &&
535
536 start_daemon -C test_flush --tf "$PWD/.git/trace_daemon" --tk true &&
537
538 # The daemon should have an initial token with no events in _0 and
539 # then a few (probably platform-specific number of) events in _1.
540 # These should both have the same <token_id>.
541
542 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_0 &&
543 nul_to_q <actual_0 >actual_q0 &&
544
545 >test_flush/file_1 &&
546 >test_flush/file_2 &&
547
548 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_1 &&
549 nul_to_q <actual_1 >actual_q1 &&
550
551 grep "file_1" actual_q1 &&
552
553 # Force a flush. This will change the <token_id>, reset the <seq_nr>, and
554 # flush the file data. Then create some events and ensure that the file
555 # again appears in the cache. It should have the new <token_id>.
556
557 test-tool -C test_flush fsmonitor-client flush >flush_0 &&
558 nul_to_q <flush_0 >flush_q0 &&
559 grep "^builtin:test_00000002:0Q/Q$" flush_q0 &&
560
561 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_2 &&
562 nul_to_q <actual_2 >actual_q2 &&
563
564 grep "^builtin:test_00000002:0Q$" actual_q2 &&
565
566 >test_flush/file_3 &&
567
568 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_3 &&
569 nul_to_q <actual_3 >actual_q3 &&
570
571 grep "file_3" actual_q3
572'
573
574# The next few test cases create repos where the .git directory is NOT
575# inside the one of the working directory. That is, where .git is a file
576# that points to a directory elsewhere. This happens for submodules and
577# non-primary worktrees.
578
579test_expect_success 'setup worktree base' '
580 git init wt-base &&
581 echo 1 >wt-base/file1 &&
582 git -C wt-base add file1 &&
583 git -C wt-base commit -m "c1"
584'
585
586test_expect_success 'worktree with .git file' '
587 git -C wt-base worktree add ../wt-secondary &&
588
589 start_daemon -C wt-secondary \
590 --tf "$PWD/trace_wt_secondary" \
591 --t2 "$PWD/trace2_wt_secondary" &&
592
593 git -C wt-secondary fsmonitor--daemon stop &&
594 test_must_fail git -C wt-secondary fsmonitor--daemon status
595'
596
597# NEEDSWORK: Repeat one of the "edit" tests on wt-secondary and
598# confirm that we get the same events and behavior -- that is, that
599# fsmonitor--daemon correctly watches BOTH the working directory and
600# the external GITDIR directory and behaves the same as when ".git"
601# is a directory inside the working directory.
602
603test_expect_success 'cleanup worktrees' '
604 stop_daemon_delete_repo wt-secondary &&
605 stop_daemon_delete_repo wt-base
606'
607
608# The next few tests perform arbitrary/contrived file operations and
609# confirm that status is correct. That is, that the data (or lack of
610# data) from fsmonitor doesn't cause incorrect results. And doesn't
611# cause incorrect results when the untracked-cache is enabled.
612
613test_lazy_prereq UNTRACKED_CACHE '
614 git update-index --test-untracked-cache
615'
616
617test_expect_success 'Matrix: setup for untracked-cache,fsmonitor matrix' '
618 test_unconfig core.fsmonitor &&
619 git update-index --no-fsmonitor &&
620 test_might_fail git fsmonitor--daemon stop
621'
622
623matrix_clean_up_repo () {
624 git reset --hard HEAD &&
625 git clean -fd
626}
627
628matrix_try () {
629 uc=$1 &&
630 fsm=$2 &&
631 fn=$3 &&
632
633 if test $uc = true && test $fsm = false
634 then
635 # The untracked-cache is buggy when FSMonitor is
636 # DISABLED, so skip the tests for this matrix
637 # combination.
638 #
639 # We've observed random, occasional test failures on
640 # Windows and MacOS when the UC is turned on and FSM
641 # is turned off. These are rare, but they do happen
642 # indicating that it is probably a race condition within
643 # the untracked cache itself.
644 #
645 # It usually happens when a test does F/D trickery and
646 # then the NEXT test fails because of extra status
647 # output from stale UC data from the previous test.
648 #
649 # Since FSMonitor is not involved in the error, skip
650 # the tests for this matrix combination.
651 #
652 return 0
653 fi &&
654
655 test_expect_success "Matrix[uc:$uc][fsm:$fsm] $fn" '
656 matrix_clean_up_repo &&
657 $fn &&
658 if test $uc = false && test $fsm = false
659 then
660 git status --porcelain=v1 >.git/expect.$fn
661 else
662 git status --porcelain=v1 >.git/actual.$fn &&
663 test_cmp .git/expect.$fn .git/actual.$fn
664 fi
665 '
666}
667
668uc_values="false"
669test_have_prereq UNTRACKED_CACHE && uc_values="false true"
670for uc_val in $uc_values
671do
672 if test $uc_val = false
673 then
674 test_expect_success "Matrix[uc:$uc_val] disable untracked cache" '
675 git config core.untrackedcache false &&
676 git update-index --no-untracked-cache
677 '
678 else
679 test_expect_success "Matrix[uc:$uc_val] enable untracked cache" '
680 git config core.untrackedcache true &&
681 git update-index --untracked-cache
682 '
683 fi
684
685 fsm_values="false true"
686 for fsm_val in $fsm_values
687 do
688 if test $fsm_val = false
689 then
690 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor" '
691 test_unconfig core.fsmonitor &&
692 git update-index --no-fsmonitor &&
693 test_might_fail git fsmonitor--daemon stop
694 '
695 else
696 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] enable fsmonitor" '
697 git config core.fsmonitor true &&
698 git fsmonitor--daemon start &&
699 git update-index --fsmonitor
700 '
701 fi
702
703 matrix_try $uc_val $fsm_val edit_files
704 matrix_try $uc_val $fsm_val delete_files
705 matrix_try $uc_val $fsm_val create_files
706 matrix_try $uc_val $fsm_val rename_files
707 matrix_try $uc_val $fsm_val file_to_directory
708 matrix_try $uc_val $fsm_val directory_to_file
709
710 matrix_try $uc_val $fsm_val move_directory_contents_deeper
711 matrix_try $uc_val $fsm_val move_directory_up
712 matrix_try $uc_val $fsm_val move_directory
713
714 if test $fsm_val = true
715 then
716 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor at end" '
717 test_unconfig core.fsmonitor &&
718 git update-index --no-fsmonitor &&
719 test_might_fail git fsmonitor--daemon stop
720 '
721 fi
722 done
723done
724
725# Test Unicode UTF-8 characters in the pathname of the working
726# directory root. Use of "*A()" routines rather than "*W()" routines
727# on Windows can sometimes lead to odd failures.
728#
729u1=$(printf "u_c3_a6__\xC3\xA6")
730u2=$(printf "u_e2_99_ab__\xE2\x99\xAB")
731u_values="$u1 $u2"
732for u in $u_values
733do
734 test_expect_success "unicode in repo root path: $u" '
735 test_when_finished "stop_daemon_delete_repo $u" &&
736
737 git init "$u" &&
738 echo 1 >"$u"/file1 &&
739 git -C "$u" add file1 &&
740 git -C "$u" config core.fsmonitor true &&
741
742 start_daemon -C "$u" &&
743 git -C "$u" status >actual &&
744 grep "new file: file1" actual
745 '
746done
747
748# Test fsmonitor interaction with submodules.
749#
750# If we start the daemon in the super, it will see FS events for
751# everything in the working directory cone and this includes any
752# files/directories contained *within* the submodules.
753#
754# A `git status` at top level will get events for items within the
755# submodule and ignore them, since they aren't named in the index
756# of the super repo. This makes the fsmonitor response a little
757# noisy, but it doesn't alter the correctness of the state of the
758# super-proper.
759#
760# When we have submodules, `git status` normally does a recursive
761# status on each of the submodules and adds a summary row for any
762# dirty submodules. (See the "S..." bits in porcelain V2 output.)
763#
764# It is therefore important that the top level status not be tricked
765# by the FSMonitor response to skip those recursive calls. That is,
766# even if FSMonitor says that the mtime of the submodule directory
767# hasn't changed and it could be implicitly marked valid, we must
768# not take that shortcut. We need to force the recursion into the
769# submodule so that we get a summary of the status *within* the
770# submodule.
771
772create_super () {
773 super="$1" &&
774
775 git init "$super" &&
776 echo x >"$super/file_1" &&
777 echo y >"$super/file_2" &&
778 echo z >"$super/file_3" &&
779 mkdir "$super/dir_1" &&
780 echo a >"$super/dir_1/file_11" &&
781 echo b >"$super/dir_1/file_12" &&
782 mkdir "$super/dir_1/dir_2" &&
783 echo a >"$super/dir_1/dir_2/file_21" &&
784 echo b >"$super/dir_1/dir_2/file_22" &&
785 git -C "$super" add . &&
786 git -C "$super" commit -m "initial $super commit"
787}
788
789create_sub () {
790 sub="$1" &&
791
792 git init "$sub" &&
793 echo x >"$sub/file_x" &&
794 echo y >"$sub/file_y" &&
795 echo z >"$sub/file_z" &&
796 mkdir "$sub/dir_x" &&
797 echo a >"$sub/dir_x/file_a" &&
798 echo b >"$sub/dir_x/file_b" &&
799 mkdir "$sub/dir_x/dir_y" &&
800 echo a >"$sub/dir_x/dir_y/file_a" &&
801 echo b >"$sub/dir_x/dir_y/file_b" &&
802 git -C "$sub" add . &&
803 git -C "$sub" commit -m "initial $sub commit"
804}
805
806my_match_and_clean () {
807 git -C super --no-optional-locks status --porcelain=v2 >actual.with &&
808 git -C super --no-optional-locks -c core.fsmonitor=false \
809 status --porcelain=v2 >actual.without &&
810 test_cmp actual.with actual.without &&
811
812 git -C super --no-optional-locks diff-index --name-status HEAD >actual.with &&
813 git -C super --no-optional-locks -c core.fsmonitor=false \
814 diff-index --name-status HEAD >actual.without &&
815 test_cmp actual.with actual.without &&
816
817 git -C super/dir_1/dir_2/sub reset --hard &&
818 git -C super/dir_1/dir_2/sub clean -d -f
819}
820
821test_expect_success 'submodule setup' '
822 git config --global protocol.file.allow always
823'
824
825test_expect_success 'submodule always visited' '
826 test_when_finished "git -C super fsmonitor--daemon stop; \
827 rm -rf super; \
828 rm -rf sub" &&
829
830 create_super super &&
831 create_sub sub &&
832
833 git -C super submodule add ../sub ./dir_1/dir_2/sub &&
834 git -C super commit -m "add sub" &&
835
836 start_daemon -C super &&
837 git -C super config core.fsmonitor true &&
838 git -C super update-index --fsmonitor &&
839 git -C super status &&
840
841 # Now run pairs of commands w/ and w/o FSMonitor while we make
842 # some dirt in the submodule and confirm matching output.
843
844 # Completely clean status.
845 my_match_and_clean &&
846
847 # .M S..U
848 echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_u &&
849 my_match_and_clean &&
850
851 # .M S.M.
852 echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_m &&
853 git -C super/dir_1/dir_2/sub add . &&
854 my_match_and_clean &&
855
856 # .M S.M.
857 echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
858 git -C super/dir_1/dir_2/sub add . &&
859 my_match_and_clean &&
860
861 # .M SC..
862 echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
863 git -C super/dir_1/dir_2/sub add . &&
864 git -C super/dir_1/dir_2/sub commit -m "SC.." &&
865 my_match_and_clean
866'
867
868# If a submodule has a `sub/.git/` directory (rather than a file
869# pointing to the super's `.git/modules/sub`) and `core.fsmonitor`
870# turned on in the submodule and the daemon is not yet started in
871# the submodule, and someone does a `git submodule absorbgitdirs`
872# in the super, Git will recursively invoke `git submodule--helper`
873# to do the work and this may try to read the index. This will
874# try to start the daemon in the submodule.
875
876test_expect_success "submodule absorbgitdirs implicitly starts daemon" '
877 test_when_finished "rm -rf super; \
878 rm -rf sub; \
879 rm super-sub.trace" &&
880
881 create_super super &&
882 create_sub sub &&
883
884 # Copy rather than submodule add so that we get a .git dir.
885 cp -R ./sub ./super/dir_1/dir_2/sub &&
886
887 git -C super/dir_1/dir_2/sub config core.fsmonitor true &&
888
889 git -C super submodule add ../sub ./dir_1/dir_2/sub &&
890 git -C super commit -m "add sub" &&
891
892 test_path_is_dir super/dir_1/dir_2/sub/.git &&
893
894 cwd="$(cd super && pwd)" &&
895 cat >expect <<-EOF &&
896 Migrating git directory of '\''dir_1/dir_2/sub'\'' from
897 '\''$cwd/dir_1/dir_2/sub/.git'\'' to
898 '\''$cwd/.git/modules/dir_1/dir_2/sub'\''
899 EOF
900 GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
901 git -C super submodule absorbgitdirs >out 2>actual &&
902 test_cmp expect actual &&
903 test_must_be_empty out &&
904
905 # Confirm that the trace2 log contains a record of the
906 # daemon starting.
907 test_subcommand git fsmonitor--daemon start <super-sub.trace
908'
909
910start_git_in_background () {
911 git "$@" &
912 git_pid=$!
913 git_pgid=$(ps -o pgid= -p $git_pid)
914 nr_tries_left=10
915 while true
916 do
917 if test $nr_tries_left -eq 0
918 then
919 kill -- -$git_pgid
920 exit 1
921 fi
922 sleep 1
923 nr_tries_left=$(($nr_tries_left - 1))
924 done >/dev/null 2>&1 &
925 watchdog_pid=$!
926 wait $git_pid
927}
928
929stop_git () {
930 while kill -0 -- -$git_pgid
931 do
932 kill -- -$git_pgid
933 sleep 1
934 done
935}
936
937stop_watchdog () {
938 while kill -0 $watchdog_pid
939 do
940 kill $watchdog_pid
941 sleep 1
942 done
943}
944
945test_expect_success !MINGW "submodule implicitly starts daemon by pull" '
946 test_atexit "stop_watchdog" &&
947 test_when_finished "stop_git; rm -rf cloned super sub" &&
948
949 create_super super &&
950 create_sub sub &&
951
952 git -C super submodule add ../sub ./dir_1/dir_2/sub &&
953 git -C super commit -m "add sub" &&
954 git clone --recurse-submodules super cloned &&
955
956 git -C cloned/dir_1/dir_2/sub config core.fsmonitor true &&
957 set -m &&
958 start_git_in_background -C cloned pull --recurse-submodules
959'
960
961# On a case-insensitive file system, confirm that the daemon
962# notices when the .git directory is moved/renamed/deleted
963# regardless of how it is spelled in the FS event.
964# That is, does the FS event receive the spelling of the
965# operation or does it receive the spelling preserved with
966# the file/directory.
967#
968test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
969 test_when_finished "stop_daemon_delete_repo test_insensitive" &&
970
971 git init test_insensitive &&
972
973 start_daemon -C test_insensitive --tf "$PWD/insensitive.trace" &&
974
975 mkdir -p test_insensitive/abc/def &&
976 echo xyz >test_insensitive/ABC/DEF/xyz &&
977
978 test_path_is_dir test_insensitive/.git &&
979 test_path_is_dir test_insensitive/.GIT &&
980
981 # Rename .git using an alternate spelling to verify that
982 # the daemon detects it and automatically shuts down.
983 mv test_insensitive/.GIT test_insensitive/.FOO &&
984
985 # See [1] above.
986 mv test_insensitive/.FOO test_insensitive/.git &&
987
988 verify_implicit_shutdown test_insensitive &&
989
990 # Verify that events were reported using on-disk spellings of the
991 # directories and files that we touched. We may or may not get a
992 # trailing slash on modified directories.
993 #
994 grep -E "^event: abc/?$" ./insensitive.trace &&
995 grep -E "^event: abc/def/?$" ./insensitive.trace &&
996 grep -E "^event: abc/def/xyz$" ./insensitive.trace
997'
998
999# The variable "unicode_debug" is defined in the following library
1000# script to dump information about how the (OS, FS) handles Unicode
1001# composition. Uncomment the following line if you want to enable it.
1002#
1003# unicode_debug=true
1004
1005. "$TEST_DIRECTORY/lib-unicode-nfc-nfd.sh"
1006
1007# See if the OS or filesystem does NFC/NFD aliasing/munging.
1008#
1009# The daemon should err on the side of caution and send BOTH the
1010# NFC and NFD forms. It does not know the original spelling of
1011# the pathname (how the user thinks it should be spelled), so
1012# emit both and let the client decide (when necessary). This is
1013# similar to "core.precomposeUnicode".
1014#
1015test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
1016 test_when_finished "stop_daemon_delete_repo test_unicode" &&
1017
1018 git init test_unicode &&
1019
1020 start_daemon -C test_unicode --tf "$PWD/unicode.trace" &&
1021
1022 # Create a directory using an NFC spelling.
1023 #
1024 mkdir test_unicode/nfc &&
1025 mkdir test_unicode/nfc/c_${utf8_nfc} &&
1026
1027 # Create a directory using an NFD spelling.
1028 #
1029 mkdir test_unicode/nfd &&
1030 mkdir test_unicode/nfd/d_${utf8_nfd} &&
1031
1032 test-tool -C test_unicode fsmonitor-client query --token 0 &&
1033
1034 if test_have_prereq UNICODE_NFC_PRESERVED
1035 then
1036 # We should have seen NFC event from OS.
1037 # We should not have synthesized an NFD event.
1038 grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
1039 grep -E -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
1040 else
1041 # We should have seen NFD event from OS.
1042 # We should have synthesized an NFC event.
1043 grep -E "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
1044 grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
1045 fi &&
1046
1047 # We assume UNICODE_NFD_PRESERVED.
1048 # We should have seen explicit NFD from OS.
1049 # We should have synthesized an NFC event.
1050 grep -E "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
1051 grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
1052'
1053
1054test_expect_success 'split-index and FSMonitor work well together' '
1055 git init split-index &&
1056 test_when_finished "git -C \"$PWD/split-index\" \
1057 fsmonitor--daemon stop" &&
1058 (
1059 cd split-index &&
1060 git config core.splitIndex true &&
1061 # force split-index in most cases
1062 git config splitIndex.maxPercentChange 99 &&
1063 git config core.fsmonitor true &&
1064
1065 # Create the following commit topology:
1066 #
1067 # * merge three
1068 # |\
1069 # | * three
1070 # * | merge two
1071 # |\|
1072 # | * two
1073 # * | one
1074 # |/
1075 # * 5a5efd7 initial
1076
1077 test_commit initial &&
1078 test_commit two &&
1079 test_commit three &&
1080 git reset --hard initial &&
1081 test_commit one &&
1082 test_tick &&
1083 git merge two &&
1084 test_tick &&
1085 git merge three &&
1086
1087 git rebase --force-rebase -r one
1088 )
1089'
1090
1091# The FSMonitor daemon reports the OBSERVED pathname of modified files
1092# and thus contains the OBSERVED spelling on case-insensitive file
1093# systems. The daemon does not (and should not) load the .git/index
1094# file and therefore does not know the expected case-spelling. Since
1095# it is possible for the user to create files/subdirectories with the
1096# incorrect case, a modified file event for a tracked will not have
1097# the EXPECTED case. This can cause `index_name_pos()` to incorrectly
1098# report that the file is untracked. This causes the client to fail to
1099# mark the file as possibly dirty (keeping the CE_FSMONITOR_VALID bit
1100# set) so that `git status` will avoid inspecting it and thus not
1101# present in the status output.
1102#
1103# The setup is a little contrived.
1104#
1105test_expect_success CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
1106 test_when_finished "stop_daemon_delete_repo subdir_case_wrong" &&
1107
1108 git init subdir_case_wrong &&
1109 (
1110 cd subdir_case_wrong &&
1111 echo x >AAA &&
1112 echo x >BBB &&
1113
1114 mkdir dir1 &&
1115 echo x >dir1/file1 &&
1116 mkdir dir1/dir2 &&
1117 echo x >dir1/dir2/file2 &&
1118 mkdir dir1/dir2/dir3 &&
1119 echo x >dir1/dir2/dir3/file3 &&
1120
1121 echo x >yyy &&
1122 echo x >zzz &&
1123 git add . &&
1124 git commit -m "data" &&
1125
1126 # This will cause "dir1/" and everything under it
1127 # to be deleted.
1128 git sparse-checkout set --cone --sparse-index &&
1129
1130 # Create dir2 with the wrong case and then let Git
1131 # repopulate dir3 -- it will not correct the spelling
1132 # of dir2.
1133 mkdir dir1 &&
1134 mkdir dir1/DIR2 &&
1135 git sparse-checkout add dir1/dir2/dir3
1136 ) &&
1137
1138 start_daemon -C subdir_case_wrong --tf "$PWD/subdir_case_wrong.trace" &&
1139
1140 # Enable FSMonitor in the client. Run enough commands for
1141 # the .git/index to sync up with the daemon with everything
1142 # marked clean.
1143 git -C subdir_case_wrong config core.fsmonitor true &&
1144 git -C subdir_case_wrong update-index --fsmonitor &&
1145 git -C subdir_case_wrong status &&
1146
1147 # Make some files dirty so that FSMonitor gets FSEvents for
1148 # each of them.
1149 echo xx >>subdir_case_wrong/AAA &&
1150 echo xx >>subdir_case_wrong/dir1/DIR2/dir3/file3 &&
1151 echo xx >>subdir_case_wrong/zzz &&
1152
1153 GIT_TRACE_FSMONITOR="$PWD/subdir_case_wrong.log" \
1154 git -C subdir_case_wrong --no-optional-locks status --short \
1155 >"$PWD/subdir_case_wrong.out" &&
1156
1157 # "git status" should have gotten file events for each of
1158 # the 3 files.
1159 #
1160 # "dir2" should be in the observed case on disk.
1161 grep "fsmonitor_refresh_callback" \
1162 <"$PWD/subdir_case_wrong.log" \
1163 >"$PWD/subdir_case_wrong.log1" &&
1164
1165 grep -q "AAA.*pos 0" "$PWD/subdir_case_wrong.log1" &&
1166 grep -q "zzz.*pos 6" "$PWD/subdir_case_wrong.log1" &&
1167
1168 grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" &&
1169
1170 # Verify that we get a mapping event to correct the case.
1171 grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \
1172 "$PWD/subdir_case_wrong.log1" &&
1173
1174 # The refresh-callbacks should have caused "git status" to clear
1175 # the CE_FSMONITOR_VALID bit on each of those files and caused
1176 # the worktree scan to visit them and mark them as modified.
1177 grep -q " M AAA" "$PWD/subdir_case_wrong.out" &&
1178 grep -q " M zzz" "$PWD/subdir_case_wrong.out" &&
1179 grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out"
1180'
1181
1182test_expect_success CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
1183 test_when_finished "stop_daemon_delete_repo file_case_wrong" &&
1184
1185 git init file_case_wrong &&
1186 (
1187 cd file_case_wrong &&
1188 echo x >AAA &&
1189 echo x >BBB &&
1190
1191 mkdir dir1 &&
1192 mkdir dir1/dir2 &&
1193 mkdir dir1/dir2/dir3 &&
1194 echo x >dir1/dir2/dir3/FILE-3-B &&
1195 echo x >dir1/dir2/dir3/XXXX-3-X &&
1196 echo x >dir1/dir2/dir3/file-3-a &&
1197 echo x >dir1/dir2/dir3/yyyy-3-y &&
1198 mkdir dir1/dir2/dir4 &&
1199 echo x >dir1/dir2/dir4/FILE-4-A &&
1200 echo x >dir1/dir2/dir4/XXXX-4-X &&
1201 echo x >dir1/dir2/dir4/file-4-b &&
1202 echo x >dir1/dir2/dir4/yyyy-4-y &&
1203
1204 echo x >yyy &&
1205 echo x >zzz &&
1206 git add . &&
1207 git commit -m "data"
1208 ) &&
1209
1210 start_daemon -C file_case_wrong --tf "$PWD/file_case_wrong.trace" &&
1211
1212 # Enable FSMonitor in the client. Run enough commands for
1213 # the .git/index to sync up with the daemon with everything
1214 # marked clean.
1215 git -C file_case_wrong config core.fsmonitor true &&
1216 git -C file_case_wrong update-index --fsmonitor &&
1217 git -C file_case_wrong status &&
1218
1219 # Make some files dirty so that FSMonitor gets FSEvents for
1220 # each of them.
1221 echo xx >>file_case_wrong/AAA &&
1222 echo xx >>file_case_wrong/zzz &&
1223
1224 # Rename some files so that FSMonitor sees a create and delete
1225 # FSEvent for each. (A simple "mv foo FOO" is not portable
1226 # between macOS and Windows. It works on both platforms, but makes
1227 # the test messy, since (1) one platform updates "ctime" on the
1228 # moved file and one does not and (2) it causes a directory event
1229 # on one platform and not on the other which causes additional
1230 # scanning during "git status" which causes a "H" vs "h" discrepancy
1231 # in "git ls-files -f".) So old-school it and move it out of the
1232 # way and copy it to the case-incorrect name so that we get fresh
1233 # "ctime" and "mtime" values.
1234
1235 mv file_case_wrong/dir1/dir2/dir3/file-3-a file_case_wrong/dir1/dir2/dir3/ORIG &&
1236 cp file_case_wrong/dir1/dir2/dir3/ORIG file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
1237 rm file_case_wrong/dir1/dir2/dir3/ORIG &&
1238 mv file_case_wrong/dir1/dir2/dir4/FILE-4-A file_case_wrong/dir1/dir2/dir4/ORIG &&
1239 cp file_case_wrong/dir1/dir2/dir4/ORIG file_case_wrong/dir1/dir2/dir4/file-4-a &&
1240 rm file_case_wrong/dir1/dir2/dir4/ORIG &&
1241
1242 # Run status enough times to fully sync.
1243 #
1244 # The first instance should get the create and delete FSEvents
1245 # for each pair. Status should update the index with a new FSM
1246 # token (so the next invocation will not see data for these
1247 # events).
1248
1249 GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try1.log" \
1250 git -C file_case_wrong status --short \
1251 >"$PWD/file_case_wrong-try1.out" &&
1252 grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try1.log" &&
1253 grep -q "fsmonitor_refresh_callback.*file-3-a.*pos 4" "$PWD/file_case_wrong-try1.log" &&
1254 grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos 6" "$PWD/file_case_wrong-try1.log" &&
1255 grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try1.log" &&
1256
1257 # FSM refresh will have invalidated the FSM bit and cause a regular
1258 # (real) scan of these tracked files, so they should have "H" status.
1259 # (We will not see a "h" status until the next refresh (on the next
1260 # command).)
1261
1262 git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf1.out" &&
1263 grep -q "H dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf1.out" &&
1264 grep -q "H dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf1.out" &&
1265
1266
1267 # Try the status again. We assume that the above status command
1268 # advanced the token so that the next one will not see those events.
1269
1270 GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try2.log" \
1271 git -C file_case_wrong status --short \
1272 >"$PWD/file_case_wrong-try2.out" &&
1273 ! grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos" "$PWD/file_case_wrong-try2.log" &&
1274 ! grep -q "fsmonitor_refresh_callback.*file-3-a.*pos" "$PWD/file_case_wrong-try2.log" &&
1275 ! grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos" "$PWD/file_case_wrong-try2.log" &&
1276 ! grep -q "fsmonitor_refresh_callback.*file-4-a.*pos" "$PWD/file_case_wrong-try2.log" &&
1277
1278 # FSM refresh saw nothing, so it will mark all files as valid,
1279 # so they should now have "h" status.
1280
1281 git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf2.out" &&
1282 grep -q "h dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf2.out" &&
1283 grep -q "h dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf2.out" &&
1284
1285
1286 # We now have files with clean content, but with case-incorrect
1287 # file names. Modify them to see if status properly reports
1288 # them.
1289
1290 echo xx >>file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
1291 echo xx >>file_case_wrong/dir1/dir2/dir4/file-4-a &&
1292
1293 GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try3.log" \
1294 git -C file_case_wrong --no-optional-locks status --short \
1295 >"$PWD/file_case_wrong-try3.out" &&
1296
1297 # Verify that we get a mapping event to correct the case.
1298 grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \
1299 "$PWD/file_case_wrong-try3.log" &&
1300 grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \
1301 "$PWD/file_case_wrong-try3.log" &&
1302
1303 # FSEvents are in observed case.
1304 grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" &&
1305 grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" &&
1306
1307 # The refresh-callbacks should have caused "git status" to clear
1308 # the CE_FSMONITOR_VALID bit on each of those files and caused
1309 # the worktree scan to visit them and mark them as modified.
1310 grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" &&
1311 grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out"
1312'
1313
1314test_done