]>
Commit | Line | Data |
---|---|---|
77596fcf | 1 | #include "clar_libgit2.h" |
ff475375 | 2 | #include "../checkout/checkout_helpers.h" |
77596fcf CMN |
3 | |
4 | #include "buffer.h" | |
26432a9c | 5 | #include "index.h" |
2cf33fee | 6 | #include "repository.h" |
77596fcf CMN |
7 | |
8 | static git_repository *g_repo; | |
9 | ||
27133caf | 10 | void test_index_racy__initialize(void) |
77596fcf CMN |
11 | { |
12 | cl_git_pass(git_repository_init(&g_repo, "diff_racy", false)); | |
13 | } | |
14 | ||
27133caf | 15 | void test_index_racy__cleanup(void) |
77596fcf | 16 | { |
6c5eaead CMN |
17 | git_repository_free(g_repo); |
18 | g_repo = NULL; | |
19 | ||
20 | cl_fixture_cleanup("diff_racy"); | |
77596fcf CMN |
21 | } |
22 | ||
27133caf | 23 | void test_index_racy__diff(void) |
77596fcf CMN |
24 | { |
25 | git_index *index; | |
26 | git_diff *diff; | |
27 | git_buf path = GIT_BUF_INIT; | |
28 | ||
29 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); | |
30 | cl_git_mkfile(path.ptr, "A"); | |
31 | ||
32 | /* Put 'A' into the index */ | |
33 | cl_git_pass(git_repository_index(&index, g_repo)); | |
34 | cl_git_pass(git_index_add_bypath(index, "A")); | |
35 | cl_git_pass(git_index_write(index)); | |
36 | ||
37 | cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL)); | |
38 | cl_assert_equal_i(0, git_diff_num_deltas(diff)); | |
6c5eaead | 39 | git_diff_free(diff); |
77596fcf CMN |
40 | |
41 | /* Change its contents quickly, so we get the same timestamp */ | |
42 | cl_git_mkfile(path.ptr, "B"); | |
43 | ||
44 | cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL)); | |
45 | cl_assert_equal_i(1, git_diff_num_deltas(diff)); | |
6c5eaead CMN |
46 | |
47 | git_index_free(index); | |
48 | git_diff_free(diff); | |
ac3d33df | 49 | git_buf_dispose(&path); |
77596fcf | 50 | } |
ff475375 | 51 | |
27133caf | 52 | void test_index_racy__write_index_just_after_file(void) |
ff475375 CMN |
53 | { |
54 | git_index *index; | |
55 | git_diff *diff; | |
56 | git_buf path = GIT_BUF_INIT; | |
35439f59 | 57 | struct p_timeval times[2]; |
ff475375 CMN |
58 | |
59 | /* Make sure we do have a timestamp */ | |
60 | cl_git_pass(git_repository_index(&index, g_repo)); | |
61 | cl_git_pass(git_index_write(index)); | |
ff475375 CMN |
62 | |
63 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); | |
64 | cl_git_mkfile(path.ptr, "A"); | |
26432a9c | 65 | /* Force the file's timestamp to be a second after we wrote the index */ |
0226f7dd AR |
66 | times[0].tv_sec = index->stamp.mtime.tv_sec + 1; |
67 | times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; | |
68 | times[1].tv_sec = index->stamp.mtime.tv_sec + 1; | |
69 | times[1].tv_usec = index->stamp.mtime.tv_nsec / 1000; | |
26432a9c | 70 | cl_git_pass(p_utimes(path.ptr, times)); |
ff475375 CMN |
71 | |
72 | /* | |
73 | * Put 'A' into the index, the size field will be filled, | |
74 | * because the index' on-disk timestamp does not match the | |
26432a9c | 75 | * file's timestamp. |
ff475375 | 76 | */ |
ff475375 CMN |
77 | cl_git_pass(git_index_add_bypath(index, "A")); |
78 | cl_git_pass(git_index_write(index)); | |
79 | ||
ff475375 | 80 | cl_git_mkfile(path.ptr, "B"); |
26432a9c | 81 | /* |
91f0d186 | 82 | * Pretend this index' modification happened a second after the |
26432a9c CMN |
83 | * file update, and rewrite the file in that same second. |
84 | */ | |
0226f7dd AR |
85 | times[0].tv_sec = index->stamp.mtime.tv_sec + 2; |
86 | times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; | |
87 | times[1].tv_sec = index->stamp.mtime.tv_sec + 2; | |
88 | times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; | |
26432a9c CMN |
89 | |
90 | cl_git_pass(p_utimes(git_index_path(index), times)); | |
91 | cl_git_pass(p_utimes(path.ptr, times)); | |
92 | ||
93 | cl_git_pass(git_index_read(index, true)); | |
ff475375 CMN |
94 | |
95 | cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL)); | |
96 | cl_assert_equal_i(1, git_diff_num_deltas(diff)); | |
26432a9c | 97 | |
ac3d33df | 98 | git_buf_dispose(&path); |
26432a9c CMN |
99 | git_diff_free(diff); |
100 | git_index_free(index); | |
ff475375 | 101 | } |
6e611f7c | 102 | |
956f4da8 ET |
103 | |
104 | static void setup_race(void) | |
6e611f7c | 105 | { |
6e611f7c | 106 | git_buf path = GIT_BUF_INIT; |
956f4da8 | 107 | git_index *index; |
8edadbf9 ET |
108 | git_index_entry *entry; |
109 | struct stat st; | |
6e611f7c CMN |
110 | |
111 | /* Make sure we do have a timestamp */ | |
24fa21f3 | 112 | cl_git_pass(git_repository_index__weakptr(&index, g_repo)); |
6e611f7c CMN |
113 | cl_git_pass(git_index_write(index)); |
114 | ||
115 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); | |
116 | ||
8edadbf9 ET |
117 | cl_git_mkfile(path.ptr, "A"); |
118 | cl_git_pass(git_index_add_bypath(index, "A")); | |
6e611f7c | 119 | |
8edadbf9 ET |
120 | cl_git_mkfile(path.ptr, "B"); |
121 | cl_git_pass(git_index_write(index)); | |
6e611f7c | 122 | |
8edadbf9 | 123 | cl_git_mkfile(path.ptr, ""); |
6e611f7c | 124 | |
8edadbf9 ET |
125 | cl_git_pass(p_stat(path.ptr, &st)); |
126 | cl_assert(entry = (git_index_entry *)git_index_get_bypath(index, "A", 0)); | |
6e611f7c | 127 | |
8edadbf9 | 128 | /* force a race */ |
ac3d33df JK |
129 | entry->mtime.seconds = (int32_t)st.st_mtime; |
130 | entry->mtime.nanoseconds = (int32_t)st.st_mtime_nsec; | |
6e611f7c | 131 | |
ac3d33df | 132 | git_buf_dispose(&path); |
956f4da8 ET |
133 | } |
134 | ||
135 | void test_index_racy__smudges_index_entry_on_save(void) | |
136 | { | |
137 | git_index *index; | |
138 | const git_index_entry *entry; | |
139 | ||
140 | setup_race(); | |
141 | ||
142 | /* write the index, which will smudge anything that had the same timestamp | |
143 | * as the index when the index was loaded. that way future loads of the | |
144 | * index (with the new timestamp) will know that these files were not | |
145 | * clean. | |
146 | */ | |
147 | ||
148 | cl_git_pass(git_repository_index__weakptr(&index, g_repo)); | |
149 | cl_git_pass(git_index_write(index)); | |
150 | ||
151 | cl_assert(entry = git_index_get_bypath(index, "A", 0)); | |
6e611f7c | 152 | cl_assert_equal_i(0, entry->file_size); |
956f4da8 ET |
153 | } |
154 | ||
155 | void test_index_racy__detects_diff_of_change_in_identical_timestamp(void) | |
156 | { | |
157 | git_index *index; | |
158 | git_diff *diff; | |
159 | ||
160 | cl_git_pass(git_repository_index__weakptr(&index, g_repo)); | |
161 | ||
162 | setup_race(); | |
6e611f7c CMN |
163 | |
164 | cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL)); | |
165 | cl_assert_equal_i(1, git_diff_num_deltas(diff)); | |
24fa21f3 | 166 | |
24fa21f3 | 167 | git_diff_free(diff); |
6e611f7c | 168 | } |
d1101263 ET |
169 | |
170 | static void setup_uptodate_files(void) | |
171 | { | |
172 | git_buf path = GIT_BUF_INIT; | |
173 | git_index *index; | |
4afe536b | 174 | const git_index_entry *a_entry; |
a7bd157e | 175 | git_index_entry new_entry = {{0}}; |
d1101263 ET |
176 | |
177 | cl_git_pass(git_repository_index(&index, g_repo)); | |
178 | ||
179 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); | |
180 | cl_git_mkfile(path.ptr, "A"); | |
181 | ||
182 | /* Put 'A' into the index */ | |
183 | cl_git_pass(git_index_add_bypath(index, "A")); | |
184 | ||
4afe536b ET |
185 | cl_assert((a_entry = git_index_get_bypath(index, "A", 0))); |
186 | ||
d1101263 ET |
187 | /* Put 'B' into the index */ |
188 | new_entry.path = "B"; | |
189 | new_entry.mode = GIT_FILEMODE_BLOB; | |
4afe536b | 190 | git_oid_cpy(&new_entry.id, &a_entry->id); |
d1101263 ET |
191 | cl_git_pass(git_index_add(index, &new_entry)); |
192 | ||
193 | /* Put 'C' into the index */ | |
194 | new_entry.path = "C"; | |
195 | new_entry.mode = GIT_FILEMODE_BLOB; | |
22a2d3d5 | 196 | cl_git_pass(git_index_add_from_buffer(index, &new_entry, "hello!\n", 7)); |
d1101263 ET |
197 | |
198 | git_index_free(index); | |
ac3d33df | 199 | git_buf_dispose(&path); |
d1101263 ET |
200 | } |
201 | ||
202 | void test_index_racy__adding_to_index_is_uptodate(void) | |
203 | { | |
204 | git_index *index; | |
205 | const git_index_entry *entry; | |
206 | ||
207 | setup_uptodate_files(); | |
208 | ||
209 | cl_git_pass(git_repository_index(&index, g_repo)); | |
210 | ||
211 | /* ensure that they're all uptodate */ | |
212 | cl_assert((entry = git_index_get_bypath(index, "A", 0))); | |
ac3d33df | 213 | cl_assert_equal_i(GIT_INDEX_ENTRY_UPTODATE, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
d1101263 ET |
214 | |
215 | cl_assert((entry = git_index_get_bypath(index, "B", 0))); | |
ac3d33df | 216 | cl_assert_equal_i(GIT_INDEX_ENTRY_UPTODATE, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
d1101263 ET |
217 | |
218 | cl_assert((entry = git_index_get_bypath(index, "C", 0))); | |
ac3d33df | 219 | cl_assert_equal_i(GIT_INDEX_ENTRY_UPTODATE, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
d1101263 ET |
220 | |
221 | cl_git_pass(git_index_write(index)); | |
222 | ||
223 | git_index_free(index); | |
224 | } | |
225 | ||
226 | void test_index_racy__reading_clears_uptodate_bit(void) | |
227 | { | |
228 | git_index *index; | |
229 | const git_index_entry *entry; | |
230 | ||
231 | setup_uptodate_files(); | |
956f4da8 | 232 | |
d1101263 ET |
233 | cl_git_pass(git_repository_index(&index, g_repo)); |
234 | cl_git_pass(git_index_write(index)); | |
235 | ||
236 | cl_git_pass(git_index_read(index, true)); | |
237 | ||
238 | /* ensure that no files are uptodate */ | |
239 | cl_assert((entry = git_index_get_bypath(index, "A", 0))); | |
ac3d33df | 240 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
d1101263 ET |
241 | |
242 | cl_assert((entry = git_index_get_bypath(index, "B", 0))); | |
ac3d33df | 243 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
d1101263 ET |
244 | |
245 | cl_assert((entry = git_index_get_bypath(index, "C", 0))); | |
ac3d33df | 246 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
d1101263 ET |
247 | |
248 | git_index_free(index); | |
249 | } | |
c30051f0 ET |
250 | |
251 | void test_index_racy__read_tree_clears_uptodate_bit(void) | |
252 | { | |
253 | git_index *index; | |
254 | git_tree *tree; | |
255 | const git_index_entry *entry; | |
256 | git_oid id; | |
257 | ||
258 | setup_uptodate_files(); | |
259 | ||
260 | cl_git_pass(git_repository_index(&index, g_repo)); | |
261 | cl_git_pass(git_index_write_tree_to(&id, index, g_repo)); | |
262 | cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); | |
263 | cl_git_pass(git_index_read_tree(index, tree)); | |
264 | ||
265 | /* ensure that no files are uptodate */ | |
266 | cl_assert((entry = git_index_get_bypath(index, "A", 0))); | |
ac3d33df | 267 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
c30051f0 ET |
268 | |
269 | cl_assert((entry = git_index_get_bypath(index, "B", 0))); | |
ac3d33df | 270 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
c30051f0 ET |
271 | |
272 | cl_assert((entry = git_index_get_bypath(index, "C", 0))); | |
ac3d33df | 273 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
c30051f0 ET |
274 | |
275 | git_tree_free(tree); | |
276 | git_index_free(index); | |
277 | } | |
5f32c506 ET |
278 | |
279 | void test_index_racy__read_index_smudges(void) | |
280 | { | |
281 | git_index *index, *newindex; | |
282 | const git_index_entry *entry; | |
283 | ||
284 | /* if we are reading an index into our new index, ensure that any | |
285 | * racy entries in the index that we're reading are smudged so that | |
286 | * we don't propagate their timestamps without further investigation. | |
287 | */ | |
288 | setup_race(); | |
289 | ||
290 | cl_git_pass(git_repository_index(&index, g_repo)); | |
291 | cl_git_pass(git_index_new(&newindex)); | |
292 | cl_git_pass(git_index_read_index(newindex, index)); | |
293 | ||
294 | cl_assert(entry = git_index_get_bypath(newindex, "A", 0)); | |
295 | cl_assert_equal_i(0, entry->file_size); | |
296 | ||
297 | git_index_free(index); | |
298 | git_index_free(newindex); | |
299 | } | |
300 | ||
301 | void test_index_racy__read_index_clears_uptodate_bit(void) | |
302 | { | |
303 | git_index *index, *newindex; | |
304 | const git_index_entry *entry; | |
5f32c506 ET |
305 | |
306 | setup_uptodate_files(); | |
307 | ||
308 | cl_git_pass(git_repository_index(&index, g_repo)); | |
309 | cl_git_pass(git_index_new(&newindex)); | |
310 | cl_git_pass(git_index_read_index(newindex, index)); | |
311 | ||
312 | /* ensure that files brought in from the other index are not uptodate */ | |
313 | cl_assert((entry = git_index_get_bypath(newindex, "A", 0))); | |
ac3d33df | 314 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
5f32c506 ET |
315 | |
316 | cl_assert((entry = git_index_get_bypath(newindex, "B", 0))); | |
ac3d33df | 317 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
5f32c506 ET |
318 | |
319 | cl_assert((entry = git_index_get_bypath(newindex, "C", 0))); | |
ac3d33df | 320 | cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE)); |
5f32c506 ET |
321 | |
322 | git_index_free(index); | |
323 | git_index_free(newindex); | |
324 | } |