]>
Commit | Line | Data |
---|---|---|
6a7bc9d1 MT |
1 | #!/bin/sh |
2 | ||
3 | test_description="path collisions during parallel checkout | |
4 | ||
5 | Parallel checkout must detect path collisions to: | |
6 | ||
7 | 1) Avoid racily writing to different paths that represent the same file on disk. | |
8 | 2) Report the colliding entries on clone. | |
9 | ||
10 | The tests in this file exercise parallel checkout's collision detection code in | |
11 | both these mechanics. | |
12 | " | |
13 | ||
b7bcdbdb | 14 | TEST_PASSES_SANITIZE_LEAK=true |
6a7bc9d1 MT |
15 | . ./test-lib.sh |
16 | . "$TEST_DIRECTORY/lib-parallel-checkout.sh" | |
17 | ||
18 | TEST_ROOT="$PWD" | |
19 | ||
20 | test_expect_success CASE_INSENSITIVE_FS 'setup' ' | |
21 | empty_oid=$(git hash-object -w --stdin </dev/null) && | |
22 | cat >objs <<-EOF && | |
23 | 100644 $empty_oid FILE_X | |
24 | 100644 $empty_oid FILE_x | |
25 | 100644 $empty_oid file_X | |
26 | 100644 $empty_oid file_x | |
27 | EOF | |
28 | git update-index --index-info <objs && | |
29 | git commit -m "colliding files" && | |
30 | git tag basename_collision && | |
31 | ||
32 | write_script "$TEST_ROOT"/logger_script <<-\EOF | |
33 | echo "$@" >>filter.log | |
34 | EOF | |
35 | ' | |
36 | ||
37 | test_workers_in_event_trace () | |
38 | { | |
39 | test $1 -eq $(grep ".event.:.child_start..*checkout--worker" $2 | wc -l) | |
40 | } | |
41 | ||
42 | test_expect_success CASE_INSENSITIVE_FS 'worker detects basename collision' ' | |
43 | GIT_TRACE2_EVENT="$(pwd)/trace" git \ | |
44 | -c checkout.workers=2 -c checkout.thresholdForParallelism=0 \ | |
45 | checkout . && | |
46 | ||
47 | test_workers_in_event_trace 2 trace && | |
48 | collisions=$(grep -i "category.:.pcheckout.,.key.:.collision/basename.,.value.:.file_x.}" trace | wc -l) && | |
49 | test $collisions -eq 3 | |
50 | ' | |
51 | ||
52 | test_expect_success CASE_INSENSITIVE_FS 'worker detects dirname collision' ' | |
53 | test_config filter.logger.smudge "\"$TEST_ROOT/logger_script\" %f" && | |
54 | empty_oid=$(git hash-object -w --stdin </dev/null) && | |
55 | ||
56 | # By setting a filter command to "a", we make it ineligible for parallel | |
57 | # checkout, and thus it is checked out *first*. This way we can ensure | |
58 | # that "A/B" and "A/C" will both collide with the regular file "a". | |
59 | # | |
60 | attr_oid=$(echo "a filter=logger" | git hash-object -w --stdin) && | |
61 | ||
62 | cat >objs <<-EOF && | |
63 | 100644 $empty_oid A/B | |
64 | 100644 $empty_oid A/C | |
65 | 100644 $empty_oid a | |
66 | 100644 $attr_oid .gitattributes | |
67 | EOF | |
68 | git rm -rf . && | |
69 | git update-index --index-info <objs && | |
70 | ||
71 | rm -f trace filter.log && | |
72 | GIT_TRACE2_EVENT="$(pwd)/trace" git \ | |
73 | -c checkout.workers=2 -c checkout.thresholdForParallelism=0 \ | |
74 | checkout . && | |
75 | ||
76 | # Check that "a" (and only "a") was filtered | |
77 | echo a >expected.log && | |
78 | test_cmp filter.log expected.log && | |
79 | ||
80 | # Check that it used the right number of workers and detected the collisions | |
81 | test_workers_in_event_trace 2 trace && | |
82 | grep "category.:.pcheckout.,.key.:.collision/dirname.,.value.:.A/B.}" trace && | |
83 | grep "category.:.pcheckout.,.key.:.collision/dirname.,.value.:.A/C.}" trace | |
84 | ' | |
85 | ||
86 | test_expect_success SYMLINKS,CASE_INSENSITIVE_FS 'do not follow symlinks colliding with leading dir' ' | |
87 | empty_oid=$(git hash-object -w --stdin </dev/null) && | |
88 | symlink_oid=$(echo "./e" | git hash-object -w --stdin) && | |
89 | mkdir e && | |
90 | ||
91 | cat >objs <<-EOF && | |
92 | 120000 $symlink_oid D | |
93 | 100644 $empty_oid d/x | |
94 | 100644 $empty_oid e/y | |
95 | EOF | |
96 | git rm -rf . && | |
97 | git update-index --index-info <objs && | |
98 | ||
99 | set_checkout_config 2 0 && | |
100 | test_checkout_workers 2 git checkout . && | |
101 | test_path_is_dir e && | |
102 | test_path_is_missing e/x | |
103 | ' | |
104 | ||
105 | # The two following tests check that parallel checkout correctly reports | |
106 | # colliding entries on clone. The sequential code detects a collision by | |
107 | # calling lstat() before trying to open(O_CREAT) a file. (Note that this only | |
108 | # works for clone.) Then, to find the pair of a colliding item k, it searches | |
109 | # cache_entry[0, k-1]. This is not sufficient in parallel checkout because: | |
110 | # | |
111 | # - A colliding file may be created between the lstat() and open() calls; | |
112 | # - A colliding entry might appear in the second half of the cache_entry array. | |
113 | # | |
114 | test_expect_success CASE_INSENSITIVE_FS 'collision report on clone (w/ racy file creation)' ' | |
115 | git reset --hard basename_collision && | |
116 | set_checkout_config 2 0 && | |
117 | test_checkout_workers 2 git clone . clone-repo 2>stderr && | |
118 | ||
119 | grep FILE_X stderr && | |
120 | grep FILE_x stderr && | |
121 | grep file_X stderr && | |
122 | grep file_x stderr && | |
123 | grep "the following paths have collided" stderr | |
124 | ' | |
125 | ||
126 | # This test ensures that the collision report code is correctly looking for | |
127 | # colliding peers in the second half of the cache_entry array. This is done by | |
128 | # defining a smudge command for the *last* array entry, which makes it | |
129 | # non-eligible for parallel-checkout. Thus, it is checked out *first*, before | |
130 | # spawning the workers. | |
131 | # | |
132 | # Note: this test doesn't work on Windows because, on this system, the | |
133 | # collision report code uses strcmp() to find the colliding pairs when | |
134 | # core.ignoreCase is false. And we need this setting for this test so that only | |
135 | # 'file_x' matches the pattern of the filter attribute. But the test works on | |
136 | # OSX, where the colliding pairs are found using inode. | |
137 | # | |
138 | test_expect_success CASE_INSENSITIVE_FS,!MINGW,!CYGWIN \ | |
139 | 'collision report on clone (w/ colliding peer after the detected entry)' ' | |
140 | ||
141 | test_config_global filter.logger.smudge "\"$TEST_ROOT/logger_script\" %f" && | |
142 | git reset --hard basename_collision && | |
143 | echo "file_x filter=logger" >.gitattributes && | |
144 | git add .gitattributes && | |
145 | git commit -m "filter for file_x" && | |
146 | ||
147 | rm -rf clone-repo && | |
148 | set_checkout_config 2 0 && | |
149 | test_checkout_workers 2 \ | |
150 | git -c core.ignoreCase=false clone . clone-repo 2>stderr && | |
151 | ||
152 | grep FILE_X stderr && | |
153 | grep FILE_x stderr && | |
154 | grep file_X stderr && | |
155 | grep file_x stderr && | |
156 | grep "the following paths have collided" stderr && | |
157 | ||
158 | # Check that only "file_x" was filtered | |
159 | echo file_x >expected.log && | |
160 | test_cmp clone-repo/filter.log expected.log | |
161 | ' | |
162 | ||
163 | test_done |