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