Blob


1 /*
2 * Copyright (c) 2018 Stefan Sperling <stsp@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <sys/param.h>
18 #include <sys/queue.h>
19 #include <sys/limits.h>
20 #include <sys/stat.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <util.h>
29 #include <err.h>
30 #include <unistd.h>
32 #include "got_error.h"
33 #include "got_object.h"
34 #include "got_reference.h"
35 #include "got_repository.h"
36 #include "got_worktree.h"
37 #include "got_opentemp.h"
39 #include "got_lib_worktree.h"
40 #include "got_lib_path.h"
42 #define GOT_REPO_PATH "../../../"
44 static int verbose;
46 void
47 test_printf(char *fmt, ...)
48 {
49 va_list ap;
51 if (!verbose)
52 return;
54 va_start(ap, fmt);
55 vprintf(fmt, ap);
56 va_end(ap);
57 }
59 static int
60 remove_got_dir(const char *worktree_path)
61 {
62 char *path;
64 if (asprintf(&path, "%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR) == -1)
65 return 0;
66 rmdir(path);
67 free(path);
68 return 1;
69 }
71 static int
72 remove_meta_file(const char *worktree_path, const char *name)
73 {
74 char *path;
76 if (asprintf(&path, "%s/%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR,
77 name) == -1)
78 return 0;
79 unlink(path);
80 free(path);
81 return 1;
82 }
84 static int
85 remove_worktree(const char *worktree_path)
86 {
87 if (!remove_meta_file(worktree_path, GOT_WORKTREE_HEAD_REF))
88 return 0;
89 if (!remove_meta_file(worktree_path, GOT_WORKTREE_BASE_COMMIT))
90 return 0;
91 if (!remove_meta_file(worktree_path, GOT_WORKTREE_FILE_INDEX))
92 return 0;
93 if (!remove_meta_file(worktree_path, GOT_WORKTREE_REPOSITORY))
94 return 0;
95 if (!remove_meta_file(worktree_path, GOT_WORKTREE_PATH_PREFIX))
96 return 0;
97 if (!remove_meta_file(worktree_path, GOT_WORKTREE_LOCK))
98 return 0;
99 if (!remove_meta_file(worktree_path, GOT_WORKTREE_FORMAT))
100 return 0;
101 if (!remove_got_dir(worktree_path))
102 return 0;
103 if (rmdir(worktree_path) == -1)
104 return 0;
105 return 1;
108 static int
109 read_meta_file(char **content, const char *path)
111 FILE *f;
112 size_t len;
113 const char delim[3] = {'\0', '\0', '\0'};
114 int ret = 0;
116 f = fopen(path, "r");
117 if (f == NULL)
118 return errno;
120 *content = fparseln(f, &len, NULL, delim, 0);
121 if (*content == NULL)
122 ret = errno;
123 fclose(f);
124 return ret;
127 static int
128 check_meta_file_exists(const char *worktree_path, const char *name)
130 struct stat sb;
131 char *path;
132 int ret = 0;
134 if (asprintf(&path, "%s/%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR,
135 name) == -1)
136 return 0;
137 if (stat(path, &sb) == 0)
138 ret = 1;
139 if (verbose) {
140 char *content;
141 if (read_meta_file(&content, path) == 0) {
142 test_printf("%s:\t%s\n", name, content);
143 free(content);
146 free(path);
147 return ret;
150 static int
151 worktree_init(const char *repo_path)
153 const struct got_error *err;
154 struct got_repository *repo = NULL;
155 struct got_reference *head_ref = NULL;
156 char worktree_path[PATH_MAX];
157 int ok = 0;
159 err = got_repo_open(&repo, repo_path);
160 if (err != NULL || repo == NULL)
161 goto done;
162 err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
163 if (err != NULL || head_ref == NULL)
164 goto done;
166 strlcpy(worktree_path, "worktree-XXXXXX", sizeof(worktree_path));
167 if (mkdtemp(worktree_path) == NULL)
168 goto done;
170 err = got_worktree_init(worktree_path, head_ref, "/", repo);
171 if (err != NULL)
172 goto done;
174 /* Ensure required files were created. */
175 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_HEAD_REF))
176 goto done;
177 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_BASE_COMMIT))
178 goto done;
179 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_LOCK))
180 goto done;
181 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_FILE_INDEX))
182 goto done;
183 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_REPOSITORY))
184 goto done;
185 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_PATH_PREFIX))
186 goto done;
187 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_FORMAT))
188 goto done;
190 if (!remove_worktree(worktree_path))
191 goto done;
192 ok = 1;
193 done:
194 if (head_ref)
195 got_ref_close(head_ref);
196 if (repo)
197 got_repo_close(repo);
198 return ok;
201 static int
202 obstruct_meta_file(char **path, const char *worktree_path, const char *name)
204 FILE *f;
205 char *s = "This file should not be here\n";
206 int ret = 1;
208 if (asprintf(path, "%s/%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR,
209 name) == -1)
210 return 0;
211 f = fopen(*path, "w+");
212 if (f == NULL) {
213 free(*path);
214 return 0;
216 if (fwrite(s, 1, strlen(s), f) != strlen(s)) {
217 free(*path);
218 ret = 0;
220 fclose(f);
221 return ret;
224 static int
225 obstruct_meta_file_and_init(int *ok, struct got_repository *repo,
226 const char *worktree_path, char *name)
228 const struct got_error *err;
229 char *path;
230 int ret = 0;
231 struct got_reference *head_ref = NULL;
233 if (!obstruct_meta_file(&path, worktree_path, GOT_WORKTREE_FILE_INDEX))
234 return 0;
236 err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
237 if (err != NULL || head_ref == NULL)
238 return 0;
240 err = got_worktree_init(worktree_path, head_ref, "/", repo);
241 if (err != NULL && err->code == GOT_ERR_ERRNO && errno == EEXIST) {
242 (*ok)++;
243 ret = 1;
245 unlink(path);
246 free(path);
247 got_ref_close(head_ref);
248 return ret;
251 static int
252 worktree_init_exists(const char *repo_path)
254 const struct got_error *err;
255 struct got_repository *repo = NULL;
256 char worktree_path[PATH_MAX];
257 char *gotpath = NULL;
258 int ok = 0;
260 err = got_repo_open(&repo, repo_path);
261 if (err != NULL || repo == NULL)
262 goto done;
263 strlcpy(worktree_path, "worktree-XXXXXX", sizeof(worktree_path));
264 if (mkdtemp(worktree_path) == NULL)
265 goto done;
266 if (mkdir(worktree_path, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST)
267 goto done;
269 if (asprintf(&gotpath, "%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR)
270 == -1)
271 goto done;
272 if (mkdir(gotpath, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST)
273 goto done;
275 /* Create files which got_worktree_init() will try to create as well. */
276 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
277 GOT_WORKTREE_HEAD_REF))
278 goto done;
279 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
280 GOT_WORKTREE_BASE_COMMIT))
281 goto done;
282 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
283 GOT_WORKTREE_LOCK))
284 goto done;
285 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
286 GOT_WORKTREE_FILE_INDEX))
287 goto done;
288 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
289 GOT_WORKTREE_REPOSITORY))
290 goto done;
291 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
292 GOT_WORKTREE_PATH_PREFIX))
293 goto done;
294 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
295 GOT_WORKTREE_FORMAT))
296 goto done;
298 done:
299 if (repo)
300 got_repo_close(repo);
301 free(gotpath);
302 if (ok == 7)
303 remove_worktree(worktree_path);
304 return (ok == 7);
307 static void
308 process_cb(void *arg, const char *path)
312 static int
313 worktree_checkout(const char *repo_path)
315 const struct got_error *err;
316 struct got_repository *repo = NULL;
317 struct got_reference *head_ref = NULL;
318 struct got_worktree *worktree = NULL;
319 char *makefile_path = NULL, *cfile_path = NULL;
320 char worktree_path[PATH_MAX];
321 int ok = 0;
322 struct stat sb;
324 err = got_repo_open(&repo, repo_path);
325 if (err != NULL || repo == NULL)
326 goto done;
327 err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
328 if (err != NULL || head_ref == NULL)
329 goto done;
331 strlcpy(worktree_path, "worktree-XXXXXX", sizeof(worktree_path));
332 if (mkdtemp(worktree_path) == NULL)
333 goto done;
335 err = got_worktree_init(worktree_path, head_ref, "/regress/worktree",
336 repo);
337 if (err != NULL)
338 goto done;
340 err = got_worktree_open(&worktree, worktree_path);
341 if (err != NULL)
342 goto done;
344 err = got_worktree_checkout_files(worktree, repo, process_cb, NULL,
345 NULL, NULL);
346 if (err != NULL)
347 goto done;
349 test_printf("checked out %s\n", worktree_path);
351 /* The work tree should contain a Makefile and worktree_test.c. */
352 if (asprintf(&makefile_path, "%s/Makefile", worktree_path) == -1)
353 goto done;
354 if (stat(makefile_path, &sb) != 0)
355 goto done;
356 else
357 unlink(makefile_path);
358 if (asprintf(&cfile_path, "%s/worktree_test.c", worktree_path) == -1)
359 goto done;
360 if (stat(cfile_path, &sb) != 0)
361 goto done;
362 else
363 unlink(cfile_path);
365 if (!remove_worktree(worktree_path))
366 goto done;
368 ok = 1;
369 done:
370 if (worktree)
371 got_worktree_close(worktree);
372 if (head_ref)
373 got_ref_close(head_ref);
374 if (repo)
375 got_repo_close(repo);
376 free(makefile_path);
377 free(cfile_path);
378 return ok;
381 #define RUN_TEST(expr, name) \
382 { test_ok = (expr); \
383 printf("test %s %s\n", (name), test_ok ? "ok" : "failed"); \
384 failure = (failure || !test_ok); }
387 void
388 usage(void)
390 fprintf(stderr, "usage: worktree_test [-v] [REPO_PATH]\n");
393 int
394 main(int argc, char *argv[])
396 int test_ok = 0, failure = 0;
397 const char *repo_path;
398 int ch;
400 #ifndef PROFILE
401 if (pledge("stdio rpath wpath cpath flock proc exec sendfd", NULL)
402 == -1)
403 err(1, "pledge");
404 #endif
406 while ((ch = getopt(argc, argv, "v")) != -1) {
407 switch (ch) {
408 case 'v':
409 verbose = 1;
410 break;
411 default:
412 usage();
413 return 1;
416 argc -= optind;
417 argv += optind;
419 if (argc == 0)
420 repo_path = GOT_REPO_PATH;
421 else if (argc == 1)
422 repo_path = argv[0];
423 else {
424 usage();
425 return 1;
428 RUN_TEST(worktree_init(repo_path), "init");
429 RUN_TEST(worktree_init_exists(repo_path), "init exists");
430 RUN_TEST(worktree_checkout(repo_path), "checkout");
432 return failure ? 1 : 0;