1 // Copyright (C) 2005 Nathaniel Smith <njs@pobox.com>
2 // 2008 Stephen Leake <stephen_leake@stephe-leake.org>
3 //
4 // This program is made available under the GNU GPL version 2.0 or
5 // greater. See the accompanying file COPYING for details.
6 //
7 // This program is distributed WITHOUT ANY WARRANTY; without even the
8 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9 // PURPOSE.
10
11 #include "../../../src/base.hh"
12 #include "../unit_tests.hh"
13 #include "../randomizer.hh"
14
15 #include "../../../src/paths.cc"
16
17 using std::logic_error;
18
UNIT_TEST(path_component)19 UNIT_TEST(path_component)
20 {
21 char const * const baddies[] = {".",
22 "..",
23 "/foo",
24 "\\foo",
25 "foo/bar",
26 "foo\\bar",
27 0 };
28
29 // these would not be okay in a full file_path, but are okay here.
30 char const * const goodies[] = {"c:foo",
31 "_mtn",
32 "_mtN",
33 "_mTn",
34 "_Mtn",
35 "_MTn",
36 "_MtN",
37 "_MTN",
38 0 };
39
40
41 for (char const * const * c = baddies; *c; ++c)
42 {
43 // the comparison prevents the compiler from eliminating the
44 // expression.
45 UNIT_TEST_CHECK_THROW((path_component(*c)()) == *c, logic_error);
46 }
47 for (char const * const *c = goodies; *c; ++c)
48 {
49 path_component p(*c);
50 UNIT_TEST_CHECK_THROW(file_path() / p, logic_error);
51 }
52
53 UNIT_TEST_CHECK_THROW(file_path_internal("foo") / path_component(),
54 logic_error);
55 }
56
57
UNIT_TEST(file_path_internal)58 UNIT_TEST(file_path_internal)
59 {
60 char const * const baddies[] = {"/foo",
61 "foo//bar",
62 "foo/../bar",
63 "../bar",
64 "_MTN",
65 "_MTN/blah",
66 "foo/bar/",
67 "foo/bar/.",
68 "foo/bar/./",
69 "foo/./bar",
70 "./foo",
71 ".",
72 "..",
73 "c:\\foo",
74 "c:foo",
75 "c:/foo",
76 // some baddies made bad by a security kluge --
77 // see the comment in in_bookkeeping_dir
78 "_mtn",
79 "_mtN",
80 "_mTn",
81 "_Mtn",
82 "_MTn",
83 "_MtN",
84 "_mTN",
85 "_mtn/foo",
86 "_mtN/foo",
87 "_mTn/foo",
88 "_Mtn/foo",
89 "_MTn/foo",
90 "_MtN/foo",
91 "_mTN/foo",
92 0 };
93 initial_rel_path.unset();
94 initial_rel_path.set(string(), true);
95 for (char const * const * c = baddies; *c; ++c)
96 {
97 UNIT_TEST_CHECK_THROW(file_path_internal(*c), logic_error);
98 }
99 initial_rel_path.unset();
100 initial_rel_path.set("blah/blah/blah", true);
101 for (char const * const * c = baddies; *c; ++c)
102 {
103 UNIT_TEST_CHECK_THROW(file_path_internal(*c), logic_error);
104 }
105
106 UNIT_TEST_CHECK(file_path().empty());
107 UNIT_TEST_CHECK(file_path_internal("").empty());
108
109 char const * const goodies[] = {"",
110 "a",
111 "foo",
112 "foo/bar/baz",
113 "foo/bar.baz",
114 "foo/with-hyphen/bar",
115 "foo/with_underscore/bar",
116 "foo/with,other+@weird*%#$=stuff/bar",
117 ".foo/bar",
118 "..foo/bar",
119 "_MTNfoo/bar",
120 "foo:bar",
121 0 };
122
123 for (int i = 0; i < 2; ++i)
124 {
125 initial_rel_path.unset();
126 initial_rel_path.set(i ? string()
127 : string("blah/blah/blah"),
128 true);
129 for (char const * const * c = goodies; *c; ++c)
130 {
131 file_path fp = file_path_internal(*c);
132 UNIT_TEST_CHECK(fp.as_internal() == *c);
133 UNIT_TEST_CHECK(file_path_internal(fp.as_internal()) == fp);
134 }
135 }
136
137 initial_rel_path.unset();
138 }
139
check_fp_normalizes_to(char const * before,char const * after)140 static void check_fp_normalizes_to(char const * before, char const * after)
141 {
142 L(FL("check_fp_normalizes_to: '%s' -> '%s'") % before % after);
143 file_path fp = file_path_external(utf8(before));
144 L(FL(" (got: %s)") % fp);
145 UNIT_TEST_CHECK(fp.as_internal() == after);
146 UNIT_TEST_CHECK(file_path_internal(fp.as_internal()) == fp);
147 // we compare after to the external form too, since as far as we know
148 // relative normalized posix paths are always good win32 paths too
149 UNIT_TEST_CHECK(fp.as_external() == after);
150 }
151
UNIT_TEST(file_path_external_null_prefix)152 UNIT_TEST(file_path_external_null_prefix)
153 {
154 initial_rel_path.unset();
155 initial_rel_path.set(string(), true);
156
157 char const * const baddies[] = {"/foo",
158 "../bar",
159 "_MTN/blah",
160 "_MTN",
161 "//blah",
162 "\\foo",
163 "..",
164 "c:\\foo",
165 "c:foo",
166 "c:/foo",
167 // some baddies made bad by a security kluge --
168 // see the comment in in_bookkeeping_dir
169 "_mtn",
170 "_mtN",
171 "_mTn",
172 "_Mtn",
173 "_MTn",
174 "_MtN",
175 "_mTN",
176 "_mtn/foo",
177 "_mtN/foo",
178 "_mTn/foo",
179 "_Mtn/foo",
180 "_MTn/foo",
181 "_MtN/foo",
182 "_mTN/foo",
183 0 };
184 for (char const * const * c = baddies; *c; ++c)
185 {
186 L(FL("test_file_path_external_null_prefix: trying baddie: %s") % *c);
187 UNIT_TEST_CHECK_THROW(file_path_external(utf8(*c)), recoverable_failure);
188 }
189
190 check_fp_normalizes_to("a", "a");
191 check_fp_normalizes_to("foo", "foo");
192 check_fp_normalizes_to("foo/bar", "foo/bar");
193 check_fp_normalizes_to("foo/bar/baz", "foo/bar/baz");
194 check_fp_normalizes_to("foo/bar.baz", "foo/bar.baz");
195 check_fp_normalizes_to("foo/with-hyphen/bar", "foo/with-hyphen/bar");
196 check_fp_normalizes_to("foo/with_underscore/bar", "foo/with_underscore/bar");
197 check_fp_normalizes_to(".foo/bar", ".foo/bar");
198 check_fp_normalizes_to("..foo/bar", "..foo/bar");
199 check_fp_normalizes_to(".", "");
200 check_fp_normalizes_to("", "");
201 #ifndef WIN32
202 check_fp_normalizes_to("foo:bar", "foo:bar");
203 #endif
204 check_fp_normalizes_to("foo/with,other+@weird*%#$=stuff/bar",
205 "foo/with,other+@weird*%#$=stuff/bar");
206
207 check_fp_normalizes_to("foo//bar", "foo/bar");
208 check_fp_normalizes_to("foo/../bar", "bar");
209 check_fp_normalizes_to("foo/bar/", "foo/bar");
210 check_fp_normalizes_to("foo/bar/.", "foo/bar");
211 check_fp_normalizes_to("foo/bar/./", "foo/bar");
212 check_fp_normalizes_to("foo/./bar/", "foo/bar");
213 check_fp_normalizes_to("./foo", "foo");
214 check_fp_normalizes_to("foo///.//", "foo");
215
216 initial_rel_path.unset();
217 }
218
UNIT_TEST(file_path_external_prefix__MTN)219 UNIT_TEST(file_path_external_prefix__MTN)
220 {
221 initial_rel_path.unset();
222 initial_rel_path.set(string("_MTN"), true);
223
224 UNIT_TEST_CHECK_THROW(file_path_external(utf8("foo")), recoverable_failure);
225 UNIT_TEST_CHECK_THROW(file_path_external(utf8(".")), recoverable_failure);
226 UNIT_TEST_CHECK_THROW(file_path_external(utf8("./blah")), recoverable_failure);
227 check_fp_normalizes_to("..", "");
228 check_fp_normalizes_to("../foo", "foo");
229 }
230
UNIT_TEST(file_path_external_prefix_a_b)231 UNIT_TEST(file_path_external_prefix_a_b)
232 {
233 initial_rel_path.unset();
234 initial_rel_path.set(string("a/b"), true);
235
236 char const * const baddies[] = {"/foo",
237 "../../../bar",
238 "../../..",
239 "../../_MTN",
240 "../../_MTN/foo",
241 "//blah",
242 "\\foo",
243 "c:\\foo",
244 #ifdef WIN32
245 "c:foo",
246 "c:/foo",
247 #endif
248 // some baddies made bad by a security kluge --
249 // see the comment in in_bookkeeping_dir
250 "../../_mtn",
251 "../../_mtN",
252 "../../_mTn",
253 "../../_Mtn",
254 "../../_MTn",
255 "../../_MtN",
256 "../../_mTN",
257 "../../_mtn/foo",
258 "../../_mtN/foo",
259 "../../_mTn/foo",
260 "../../_Mtn/foo",
261 "../../_MTn/foo",
262 "../../_MtN/foo",
263 "../../_mTN/foo",
264 0 };
265 for (char const * const * c = baddies; *c; ++c)
266 {
267 L(FL("test_file_path_external_prefix_a_b: trying baddie: %s") % *c);
268 UNIT_TEST_CHECK_THROW(file_path_external(utf8(*c)), recoverable_failure);
269 }
270
271 check_fp_normalizes_to("foo", "a/b/foo");
272 check_fp_normalizes_to("a", "a/b/a");
273 check_fp_normalizes_to("foo/bar", "a/b/foo/bar");
274 check_fp_normalizes_to("foo/bar/baz", "a/b/foo/bar/baz");
275 check_fp_normalizes_to("foo/bar.baz", "a/b/foo/bar.baz");
276 check_fp_normalizes_to("foo/with-hyphen/bar", "a/b/foo/with-hyphen/bar");
277 check_fp_normalizes_to("foo/with_underscore/bar", "a/b/foo/with_underscore/bar");
278 check_fp_normalizes_to(".foo/bar", "a/b/.foo/bar");
279 check_fp_normalizes_to("..foo/bar", "a/b/..foo/bar");
280 check_fp_normalizes_to(".", "a/b");
281 check_fp_normalizes_to("", "a/b");
282 #ifndef WIN32
283 check_fp_normalizes_to("foo:bar", "a/b/foo:bar");
284 #endif
285 check_fp_normalizes_to("foo/with,other+@weird*%#$=stuff/bar",
286 "a/b/foo/with,other+@weird*%#$=stuff/bar");
287 check_fp_normalizes_to("foo//bar", "a/b/foo/bar");
288 check_fp_normalizes_to("foo/../bar", "a/b/bar");
289 check_fp_normalizes_to("foo/bar/", "a/b/foo/bar");
290 check_fp_normalizes_to("foo/bar/.", "a/b/foo/bar");
291 check_fp_normalizes_to("foo/bar/./", "a/b/foo/bar");
292 check_fp_normalizes_to("foo/./bar/", "a/b/foo/bar");
293 check_fp_normalizes_to("./foo", "a/b/foo");
294 check_fp_normalizes_to("foo///.//", "a/b/foo");
295 // things that would have been bad without the initial_rel_path:
296 check_fp_normalizes_to("../foo", "a/foo");
297 check_fp_normalizes_to("..", "a");
298 check_fp_normalizes_to("../..", "");
299 check_fp_normalizes_to("_MTN/foo", "a/b/_MTN/foo");
300 check_fp_normalizes_to("_MTN", "a/b/_MTN");
301 #ifndef WIN32
302 check_fp_normalizes_to("c:foo", "a/b/c:foo");
303 check_fp_normalizes_to("c:/foo", "a/b/c:/foo");
304 #endif
305
306 initial_rel_path.unset();
307 }
308
UNIT_TEST(basename)309 UNIT_TEST(basename)
310 {
311 struct t
312 {
313 char const * in;
314 char const * out;
315 };
316 // file_paths cannot be absolute, but may be the empty string.
317 struct t const fp_cases[] = {
318 { "", "" },
319 { "foo", "foo" },
320 { "foo/bar", "bar" },
321 { "foo/bar/baz", "baz" },
322 { 0, 0 }
323 };
324 // bookkeeping_paths cannot be absolute and must start with the
325 // bookkeeping_root_component.
326 struct t const bp_cases[] = {
327 { "_MTN", "_MTN" },
328 { "_MTN/foo", "foo" },
329 { "_MTN/foo/bar", "bar" },
330 { 0, 0 }
331 };
332
333 // system_paths must be absolute. this relies on the setting of
334 // initial_abs_path below. note that most of the cases whose full paths
335 // vary between Unix and Windows will still have the same basenames.
336 struct t const sp_cases[] = {
337 { "/", "" },
338 { "//", "" },
339 { "foo", "foo" },
340 { "/foo", "foo" },
341 { "//foo", "foo" },
342 { "~/foo", "foo" },
343 { "c:/foo", "foo" },
344 { "foo/bar", "bar" },
345 { "/foo/bar", "bar" },
346 { "//foo/bar", "bar" },
347 { "~/foo/bar", "bar" },
348 { "c:/foo/bar", "bar" },
349 #ifdef WIN32
350 { "c:/", "" },
351 { "c:foo", "foo" },
352 #else
353 { "c:/", "c:" },
354 { "c:foo", "c:foo" },
355 #endif
356 { 0, 0 }
357 };
358
359 UNIT_TEST_CHECKPOINT("file_path basenames");
360 for (struct t const *p = fp_cases; p->in; p++)
361 {
362 file_path fp = file_path_internal(p->in);
363 path_component pc(fp.basename());
364 UNIT_TEST_CHECK_MSG(pc == path_component(p->out),
365 FL("basename('%s') = '%s' (expect '%s')")
366 % p->in % pc % p->out);
367 }
368
369 UNIT_TEST_CHECKPOINT("bookkeeping_path basenames");
370 for (struct t const *p = bp_cases; p->in; p++)
371 {
372 bookkeeping_path fp(p->in);
373 path_component pc(fp.basename());
374 UNIT_TEST_CHECK_MSG(pc == path_component(p->out),
375 FL("basename('%s') = '%s' (expect '%s')")
376 % p->in % pc % p->out);
377 }
378
379
380 UNIT_TEST_CHECKPOINT("system_path basenames");
381
382 initial_abs_path.unset();
383 initial_abs_path.set(system_path("/a/b"), true);
384
385 for (struct t const *p = sp_cases; p->in; p++)
386 {
387 system_path fp(p->in);
388 path_component pc(fp.basename());
389 UNIT_TEST_CHECK_MSG(pc == path_component(p->out),
390 FL("basename('%s') = '%s' (expect '%s')")
391 % p->in % pc % p->out);
392 }
393
394 // any_path::basename() should return exactly the same thing that
395 // the corresponding specialized basename() does, but with type any_path.
396 UNIT_TEST_CHECKPOINT("any_path basenames");
397 for (struct t const *p = fp_cases; p->in; p++)
398 {
399 any_path ap(file_path_internal(p->in));
400 path_component pc(ap.basename());
401 UNIT_TEST_CHECK_MSG(pc == path_component(p->out),
402 FL("basename('%s') = '%s' (expect '%s')")
403 % p->in % pc % p->out);
404 }
405 for (struct t const *p = bp_cases; p->in; p++)
406 {
407 any_path ap(bookkeeping_path(p->in));
408 path_component pc(ap.basename());
409 UNIT_TEST_CHECK_MSG(pc == path_component(p->out),
410 FL("basename('%s') = '%s' (expect '%s')")
411 % p->in % pc % p->out);
412 }
413 for (struct t const *p = sp_cases; p->in; p++)
414 {
415 any_path ap(system_path(p->in));
416 path_component pc(ap.basename());
417 UNIT_TEST_CHECK_MSG(pc == path_component(p->out),
418 FL("basename('%s') = '%s' (expect '%s')")
419 % p->in % pc % p->out);
420 }
421
422 initial_abs_path.unset();
423 }
424
UNIT_TEST(dirname)425 UNIT_TEST(dirname)
426 {
427 struct t
428 {
429 char const * in;
430 char const * out;
431 };
432 // file_paths cannot be absolute, but may be the empty string.
433 struct t const fp_cases[] = {
434 { "", "" },
435 { "foo", "" },
436 { "foo/bar", "foo" },
437 { "foo/bar/baz", "foo/bar" },
438 { 0, 0 }
439 };
440
441 // system_paths must be absolute. this relies on the setting of
442 // initial_abs_path below.
443 struct t const sp_cases[] = {
444 { "/", "/" },
445 { "//", "//" },
446 { "foo", "/a/b" },
447 { "/foo", "/" },
448 { "//foo", "//" },
449 { "~/foo", "~" },
450 { "foo/bar", "/a/b/foo" },
451 { "/foo/bar", "/foo" },
452 { "//foo/bar", "//foo" },
453 { "~/foo/bar", "~/foo" },
454 #ifdef WIN32
455 { "c:", "c:" },
456 { "c:foo", "c:" },
457 { "c:/", "c:/" },
458 { "c:/foo", "c:/" },
459 { "c:/foo/bar", "c:/foo" },
460 #else
461 { "c:", "/a/b" },
462 { "c:foo", "/a/b" },
463 { "c:/", "/a/b" },
464 { "c:/foo", "/a/b/c:" },
465 { "c:/foo/bar", "/a/b/c:/foo" },
466 #endif
467 { 0, 0 }
468 };
469
470 initial_abs_path.unset();
471
472 UNIT_TEST_CHECKPOINT("file_path dirnames");
473 for (struct t const *p = fp_cases; p->in; p++)
474 {
475 file_path fp = file_path_internal(p->in);
476 file_path dn = fp.dirname();
477 UNIT_TEST_CHECK_MSG(dn == file_path_internal(p->out),
478 FL("dirname('%s') = '%s' (expect '%s')")
479 % p->in % dn % p->out);
480 }
481
482
483 initial_abs_path.set(system_path("/a/b"), true);
484 UNIT_TEST_CHECKPOINT("system_path dirnames");
485 for (struct t const *p = sp_cases; p->in; p++)
486 {
487 system_path fp(p->in);
488 system_path dn(fp.dirname());
489
490 UNIT_TEST_CHECK_MSG(dn == system_path(p->out),
491 FL("dirname('%s') = '%s' (expect '%s')")
492 % p->in % dn % p->out);
493 }
494
495 // any_path::dirname() should return exactly the same thing that
496 // the corresponding specialized dirname() does, but with type any_path.
497 UNIT_TEST_CHECKPOINT("any_path dirnames");
498 for (struct t const *p = fp_cases; p->in; p++)
499 {
500 any_path ap(file_path_internal(p->in));
501 any_path dn(ap.dirname());
502 any_path rf(file_path_internal(p->out));
503 UNIT_TEST_CHECK_MSG(dn.as_internal() == rf.as_internal(),
504 FL("dirname('%s') = '%s' (expect '%s')")
505 % p->in % dn % rf);
506 }
507 for (struct t const *p = sp_cases; p->in; p++)
508 {
509 any_path ap(system_path(p->in));
510 any_path dn(ap.dirname());
511 any_path rf(system_path(p->out));
512 UNIT_TEST_CHECK_MSG(dn.as_internal() == rf.as_internal(),
513 FL("dirname('%s') = '%s' (expect '%s')")
514 % p->in % dn % rf);
515 }
516
517 initial_abs_path.unset();
518 }
519
UNIT_TEST(depth)520 UNIT_TEST(depth)
521 {
522 char const * const cases[] = {"", "foo", "foo/bar", "foo/bar/baz", 0};
523 for (unsigned int i = 0; cases[i]; i++)
524 {
525 file_path fp = file_path_internal(cases[i]);
526 unsigned int d = fp.depth();
527 UNIT_TEST_CHECK_MSG(d == i,
528 FL("depth('%s') = %d (expect %d)") % fp % d % i);
529 }
530 }
531
check_bk_normalizes_to(char const * before,char const * after)532 static void check_bk_normalizes_to(char const * before, char const * after)
533 {
534 bookkeeping_path bp(bookkeeping_root / before);
535 L(FL("normalizing %s to %s (got %s)") % before % after % bp);
536 UNIT_TEST_CHECK(bp.as_external() == after);
537 UNIT_TEST_CHECK(bookkeeping_path(bp.as_internal(),
538 origin::internal).as_internal() == bp.as_internal());
539 }
540
UNIT_TEST(bookkeeping)541 UNIT_TEST(bookkeeping)
542 {
543 char const * const baddies[] = {"/foo",
544 "foo//bar",
545 "foo/../bar",
546 "../bar",
547 "foo/bar/",
548 "foo/bar/.",
549 "foo/bar/./",
550 "foo/./bar",
551 "./foo",
552 ".",
553 "..",
554 "c:\\foo",
555 "c:foo",
556 "c:/foo",
557 "",
558 "a:b",
559 0 };
560 string tmp_path_string;
561
562 for (char const * const * c = baddies; *c; ++c)
563 {
564 L(FL("test_bookkeeping_path baddie: trying '%s'") % *c);
565 UNIT_TEST_CHECK_THROW(bookkeeping_path(tmp_path_string.assign(*c),
566 origin::internal),
567 logic_error);
568 UNIT_TEST_CHECK_THROW(bookkeeping_root / *c, logic_error);
569 }
570
571 // these are legitimate as things to append to bookkeeping_root, but
572 // not as bookkeeping_paths in themselves.
573 UNIT_TEST_CHECK_THROW(bookkeeping_path("a"), logic_error);
574 UNIT_TEST_CHECK_NOT_THROW(bookkeeping_root / "a", logic_error);
575 UNIT_TEST_CHECK_THROW(bookkeeping_path("foo/bar"), logic_error);
576 UNIT_TEST_CHECK_NOT_THROW(bookkeeping_root / "foo/bar", logic_error);
577
578 check_bk_normalizes_to("a", "_MTN/a");
579 check_bk_normalizes_to("foo", "_MTN/foo");
580 check_bk_normalizes_to("foo/bar", "_MTN/foo/bar");
581 check_bk_normalizes_to("foo/bar/baz", "_MTN/foo/bar/baz");
582 }
583
check_system_normalizes_to(char const * before,char const * after)584 static void check_system_normalizes_to(char const * before, char const * after)
585 {
586 system_path sp(before);
587 L(FL("normalizing '%s' to '%s' (got '%s')") % before % after % sp);
588 UNIT_TEST_CHECK(sp.as_external() == after);
589 UNIT_TEST_CHECK(system_path(sp.as_internal(),
590 origin::internal).as_internal() == sp.as_internal());
591 }
592
UNIT_TEST(system)593 UNIT_TEST(system)
594 {
595 initial_abs_path.unset();
596 initial_abs_path.set(system_path("/a/b"), true);
597
598 UNIT_TEST_CHECK_THROW(system_path(""), unrecoverable_failure);
599
600 check_system_normalizes_to("foo", "/a/b/foo");
601 check_system_normalizes_to("foo/bar", "/a/b/foo/bar");
602 check_system_normalizes_to("/foo/bar", "/foo/bar");
603 check_system_normalizes_to("//foo/bar", "//foo/bar");
604 #ifdef WIN32
605 check_system_normalizes_to("c:foo", "c:foo");
606 check_system_normalizes_to("c:/foo", "c:/foo");
607 check_system_normalizes_to("c:\\foo", "c:/foo");
608 #else
609 check_system_normalizes_to("c:foo", "/a/b/c:foo");
610 check_system_normalizes_to("c:/foo", "/a/b/c:/foo");
611 check_system_normalizes_to("c:\\foo", "/a/b/c:\\foo");
612 check_system_normalizes_to("foo:bar", "/a/b/foo:bar");
613 #endif
614 // we require that system_path normalize out ..'s, because of the following
615 // case:
616 // /work mkdir newdir
617 // /work$ cd newdir
618 // /work/newdir$ monotone setup --db=../foo.db
619 // Now they have either "/work/foo.db" or "/work/newdir/../foo.db" in
620 // _MTN/options
621 // /work/newdir$ cd ..
622 // /work$ mv newdir newerdir # better name
623 // Oops, now, if we stored the version with ..'s in, this workspace
624 // is broken.
625 check_system_normalizes_to("../foo", "/a/foo");
626 check_system_normalizes_to("foo/..", "/a/b");
627 check_system_normalizes_to("/foo/bar/..", "/foo");
628 check_system_normalizes_to("/foo/..", "/");
629 // can't do particularly interesting checking of tilde expansion, but at
630 // least we can check that it's doing _something_...
631 string tilde_expanded = system_path("~/foo").as_external();
632 #ifdef WIN32
633 UNIT_TEST_CHECK(tilde_expanded[1] == ':');
634 #else
635 UNIT_TEST_CHECK(tilde_expanded[0] == '/');
636 #endif
637 // This test should not be performed, tildes are allowed in the middle
638 // of paths, and we have already shown in the previous test that what
639 // we expected happened (or didn't).
640 //UNIT_TEST_CHECK(tilde_expanded.find('~') == string::npos);
641
642 // on Windows, ~name is not expanded
643 #ifdef WIN32
644 UNIT_TEST_CHECK(system_path("~this_user_does_not_exist_anywhere")
645 .as_external()
646 == "/a/b/~this_user_does_not_exist_anywhere");
647 #else
648 UNIT_TEST_CHECK_THROW(system_path("~this_user_does_not_exist_anywhere"),
649 recoverable_failure);
650 #endif
651
652 // finally, make sure that the copy-from-any_path constructor works right
653 // in particular, it should interpret the paths it gets as being relative to
654 // the project root, not the initial path
655 working_root.unset();
656 working_root.set(system_path("/working/root"), true);
657 initial_rel_path.unset();
658 initial_rel_path.set(string("rel/initial"), true);
659
660 UNIT_TEST_CHECK(system_path(system_path("foo/bar")).as_internal() == "/a/b/foo/bar");
661 UNIT_TEST_CHECK(!working_root.used);
662 UNIT_TEST_CHECK(system_path(system_path("/foo/bar")).as_internal() == "/foo/bar");
663 UNIT_TEST_CHECK(!working_root.used);
664 UNIT_TEST_CHECK(system_path(file_path_internal("foo/bar"), false).as_internal()
665 == "/working/root/foo/bar");
666 UNIT_TEST_CHECK(!working_root.used);
667 UNIT_TEST_CHECK(system_path(file_path_internal("foo/bar")).as_internal()
668 == "/working/root/foo/bar");
669 UNIT_TEST_CHECK(working_root.used);
670 UNIT_TEST_CHECK(system_path(file_path_external(utf8("foo/bar"))).as_external()
671 == "/working/root/rel/initial/foo/bar");
672 file_path a_file_path;
673 UNIT_TEST_CHECK(system_path(a_file_path).as_external()
674 == "/working/root");
675 UNIT_TEST_CHECK(system_path(bookkeeping_path("_MTN/foo/bar")).as_internal()
676 == "/working/root/_MTN/foo/bar");
677 UNIT_TEST_CHECK(system_path(bookkeeping_root).as_internal()
678 == "/working/root/_MTN");
679 initial_abs_path.unset();
680 working_root.unset();
681 initial_rel_path.unset();
682 }
683
UNIT_TEST(access_tracker)684 UNIT_TEST(access_tracker)
685 {
686 access_tracker<int> a;
687 UNIT_TEST_CHECK_THROW(a.get(), logic_error);
688 a.set(1, false);
689 UNIT_TEST_CHECK_THROW(a.set(2, false), logic_error);
690 a.set(2, true);
691 UNIT_TEST_CHECK_THROW(a.set(3, false), logic_error);
692 UNIT_TEST_CHECK(a.get() == 2);
693 UNIT_TEST_CHECK_THROW(a.set(3, true), logic_error);
694 a.unset();
695 a.may_not_initialize();
696 UNIT_TEST_CHECK_THROW(a.set(1, false), logic_error);
697 UNIT_TEST_CHECK_THROW(a.set(2, true), logic_error);
698 a.unset();
699 a.set(1, false);
700 UNIT_TEST_CHECK_THROW(a.may_not_initialize(), logic_error);
701 }
702
test_path_less_than(string const & left,string const & right)703 static void test_path_less_than(string const & left, string const & right)
704 {
705 MM(left);
706 MM(right);
707 file_path left_fp = file_path_internal(left);
708 file_path right_fp = file_path_internal(right);
709 I(left_fp < right_fp);
710 }
711
test_path_equal(string const & left,string const & right)712 static void test_path_equal(string const & left, string const & right)
713 {
714 MM(left);
715 MM(right);
716 file_path left_fp = file_path_internal(left);
717 file_path right_fp = file_path_internal(right);
718 I(left_fp == right_fp);
719 }
720
UNIT_TEST(ordering)721 UNIT_TEST(ordering)
722 {
723 // this ordering is very important:
724 // -- it is used to determine the textual form of csets and manifests
725 // (in particular, it cannot be changed)
726 // -- it is used to determine in what order cset operations can be applied
727 // (in particular, foo must sort before foo/bar, so that we can use it
728 // to do top-down and bottom-up traversals of a set of paths).
729 test_path_less_than("a", "b");
730 test_path_less_than("a", "c");
731 test_path_less_than("ab", "ac");
732 test_path_less_than("a", "ab");
733 test_path_less_than("", "a");
734 test_path_less_than("", ".foo");
735 test_path_less_than("foo", "foo/bar");
736 // . is before / asciibetically, so sorting by strings will give the wrong
737 // answer on this:
738 test_path_less_than("foo/bar", "foo.bar");
739
740 // path_components used to be interned strings, and we used the default sort
741 // order, which meant that in practice path components would sort in the
742 // _order they were first used in the program_. So let's put in a test that
743 // would catch this sort of brokenness.
744 test_path_less_than("fallanopic_not_otherwise_mentioned", "xyzzy");
745 test_path_less_than("fallanoooo_not_otherwise_mentioned_and_smaller",
746 "fallanopic_not_otherwise_mentioned");
747 }
748
UNIT_TEST(ordering_random)749 UNIT_TEST(ordering_random)
750 {
751 char x[4] = {0,0,0,0};
752 char y[4] = {0,0,0,0};
753 u8 a, b, c, d;
754 const int ntrials = 1000;
755 int i;
756 randomizer rng;
757
758 // use of numbers is intentional; these strings are defined to be UTF-8.
759
760 UNIT_TEST_CHECKPOINT("a and b");
761 for (i = 0; i < ntrials; i++)
762 {
763 do a = rng.uniform(0x7f - 0x20) + 0x20;
764 while (a == 0x5c || a == 0x2f || a == 0x2e); // '\\', '/', '.'
765
766 do b = rng.uniform(0x7f - 0x20) + 0x20;
767 while (b == 0x5c || b == 0x2f || b == 0x2e); // '\\', '/', '.'
768
769 x[0] = a;
770 y[0] = b;
771 if (a < b)
772 test_path_less_than(x, y);
773 else if (a > b)
774 test_path_less_than(y, x);
775 else
776 test_path_equal(x, y);
777 }
778
779 UNIT_TEST_CHECKPOINT("ab and cd");
780 for (i = 0; i < ntrials; i++)
781 {
782 do
783 {
784 do a = rng.uniform(0x7f - 0x20) + 0x20;
785 while (a == 0x5c || a == 0x2f); // '\\', '/'
786
787 do b = rng.uniform(0x7f - 0x20) + 0x20;
788 while (b == 0x5c || b == 0x2f || b == 0x3a); // '\\', '/', ':'
789 }
790 while (a == 0x2e && b == 0x2e); // ".."
791
792 do
793 {
794 do c = rng.uniform(0x7f - 0x20) + 0x20;
795 while (c == 0x5c || c == 0x2f); // '\\', '/'
796
797 do d = rng.uniform(0x7f - 0x20) + 0x20;
798 while (d == 0x5c || d == 0x2f || d == 0x3a); // '\\', '/', ':'
799 }
800 while (c == 0x2e && d == 0x2e); // ".."
801
802 x[0] = a;
803 x[1] = b;
804 y[0] = c;
805 y[1] = d;
806
807 if (a < c || (a == c && b < d))
808 test_path_less_than(x, y);
809 else if (a > c || (a == c && b > d))
810 test_path_less_than(y, x);
811 else
812 test_path_equal(x, y);
813 }
814
815 UNIT_TEST_CHECKPOINT("a and b/c");
816 x[1] = 0;
817 y[1] = '/';
818 for (i = 0; i < ntrials; i++)
819 {
820 do a = rng.uniform(0x7f - 0x20) + 0x20;
821 while (a == 0x5c || a == 0x2f || a == 0x2e); // '\\', '/', '.'
822
823 do b = rng.uniform(0x7f - 0x20) + 0x20;
824 while (b == 0x5c || b == 0x2f || b == 0x2e); // '\\', '/', '.'
825
826 do c = rng.uniform(0x7f - 0x20) + 0x20;
827 while (c == 0x5c || c == 0x2f || c == 0x2e); // '\\', '/', '.'
828
829 x[0] = a;
830 y[0] = b;
831 y[2] = c;
832
833 // only the order of a and b matters. 1 sorts before 1/2.
834 if (a <= b)
835 test_path_less_than(x, y);
836 else
837 test_path_less_than(y, x);
838 }
839
840 UNIT_TEST_CHECKPOINT("ab and c/d");
841 for (i = 0; i < ntrials; i++)
842 {
843 do
844 {
845 do a = rng.uniform(0x7f - 0x20) + 0x20;
846 while (a == 0x5c || a == 0x2f); // '\\', '/'
847
848 do b = rng.uniform(0x7f - 0x20) + 0x20;
849 while (b == 0x5c || b == 0x2f || b == 0x3a); // '\\', '/', ':'
850 }
851 while (a == 0x2e && b == 0x2e); // ".."
852
853 do c = rng.uniform(0x7f - 0x20) + 0x20;
854 while (c == 0x5c || c == 0x2f || c == 0x2e); // '\\', '/', '.'
855
856 do d = rng.uniform(0x7f - 0x20) + 0x20;
857 while (d == 0x5c || d == 0x2f || d == 0x2e); // '\\', '/', '.'
858
859
860 x[0] = a;
861 x[1] = b;
862 y[0] = c;
863 y[2] = d;
864
865 // only the order of a and c matters,
866 // but this time, 12 sorts after 1/2.
867 if (a < c)
868 test_path_less_than(x, y);
869 else
870 test_path_less_than(y, x);
871 }
872
873
874 UNIT_TEST_CHECKPOINT("a/b and c/d");
875 x[1] = '/';
876 for (i = 0; i < ntrials; i++)
877 {
878 do a = rng.uniform(0x7f - 0x20) + 0x20;
879 while (a == 0x5c || a == 0x2f || a == 0x2e); // '\\', '/', '.'
880
881 do b = rng.uniform(0x7f - 0x20) + 0x20;
882 while (b == 0x5c || b == 0x2f || b == 0x2e); // '\\', '/', '.'
883
884 do c = rng.uniform(0x7f - 0x20) + 0x20;
885 while (c == 0x5c || c == 0x2f || c == 0x2e); // '\\', '/', '.'
886
887 do d = rng.uniform(0x7f - 0x20) + 0x20;
888 while (d == 0x5c || d == 0x2f || d == 0x2e); // '\\', '/', '.'
889
890 x[0] = a;
891 x[2] = b;
892 y[0] = c;
893 y[2] = d;
894
895 if (a < c || (a == c && b < d))
896 test_path_less_than(x, y);
897 else if (a > c || (a == c && b > d))
898 test_path_less_than(y, x);
899 else
900 test_path_equal(x, y);
901 }
902 }
903
UNIT_TEST(test_internal_string_is_bookkeeping_path)904 UNIT_TEST(test_internal_string_is_bookkeeping_path)
905 {
906 char const * const yes[] = {"_MTN",
907 "_MTN/foo",
908 "_mtn/Foo",
909 0 };
910 char const * const no[] = {"foo/_MTN",
911 "foo/bar",
912 0 };
913 for (char const * const * c = yes; *c; ++c)
914 UNIT_TEST_CHECK(bookkeeping_path
915 ::internal_string_is_bookkeeping_path(utf8(std::string(*c),
916 origin::internal)));
917 for (char const * const * c = no; *c; ++c)
918 UNIT_TEST_CHECK(!bookkeeping_path
919 ::internal_string_is_bookkeeping_path(utf8(std::string(*c),
920 origin::internal)));
921 }
922
UNIT_TEST(test_external_string_is_bookkeeping_path_prefix_none)923 UNIT_TEST(test_external_string_is_bookkeeping_path_prefix_none)
924 {
925 initial_rel_path.unset();
926 initial_rel_path.set(string(), true);
927
928 char const * const yes[] = {"_MTN",
929 "_MTN/foo",
930 "_mtn/Foo",
931 "_MTN/foo/..",
932 0 };
933 char const * const no[] = {"foo/_MTN",
934 "foo/bar",
935 "_MTN/..",
936 0 };
937 for (char const * const * c = yes; *c; ++c)
938 UNIT_TEST_CHECK(bookkeeping_path
939 ::external_string_is_bookkeeping_path(utf8(std::string(*c),
940 origin::internal)));
941 for (char const * const * c = no; *c; ++c)
942 UNIT_TEST_CHECK(!bookkeeping_path
943 ::external_string_is_bookkeeping_path(utf8(std::string(*c),
944 origin::internal)));
945 }
946
UNIT_TEST(test_external_string_is_bookkeeping_path_prefix_a_b)947 UNIT_TEST(test_external_string_is_bookkeeping_path_prefix_a_b)
948 {
949 initial_rel_path.unset();
950 initial_rel_path.set(string("a/b"), true);
951
952 char const * const yes[] = {"../../_MTN",
953 "../../_MTN/foo",
954 "../../_mtn/Foo",
955 "../../_MTN/foo/..",
956 "../../foo/../_MTN/foo",
957 0 };
958 char const * const no[] = {"foo/_MTN",
959 "foo/bar",
960 "_MTN",
961 "../../foo/_MTN",
962 0 };
963 for (char const * const * c = yes; *c; ++c)
964 UNIT_TEST_CHECK(bookkeeping_path
965 ::external_string_is_bookkeeping_path(utf8(std::string(*c),
966 origin::internal)));
967 for (char const * const * c = no; *c; ++c)
968 UNIT_TEST_CHECK(!bookkeeping_path
969 ::external_string_is_bookkeeping_path(utf8(std::string(*c),
970 origin::internal)));
971 }
972
UNIT_TEST(test_external_string_is_bookkeeping_path_prefix__MTN)973 UNIT_TEST(test_external_string_is_bookkeeping_path_prefix__MTN)
974 {
975 initial_rel_path.unset();
976 initial_rel_path.set(string("_MTN"), true);
977
978 char const * const yes[] = {".",
979 "foo",
980 "../_MTN/foo/..",
981 "../_mtn/foo",
982 "../foo/../_MTN/foo",
983 0 };
984 char const * const no[] = {"../foo",
985 "../foo/bar",
986 "../foo/_MTN",
987 #ifdef WIN32
988 "c:/foo/foo", // don't throw informative_failure exception
989 #else
990 "/foo/foo", // don't throw informative_failure exception
991 #endif
992 0 };
993 for (char const * const * c = yes; *c; ++c)
994 UNIT_TEST_CHECK(bookkeeping_path
995 ::external_string_is_bookkeeping_path(utf8(std::string(*c),
996 origin::internal)));
997 for (char const * const * c = no; *c; ++c)
998 UNIT_TEST_CHECK(!bookkeeping_path
999 ::external_string_is_bookkeeping_path(utf8(std::string(*c),
1000 origin::internal)));
1001 }
1002
UNIT_TEST(find_old_new_path_for)1003 UNIT_TEST(find_old_new_path_for)
1004 {
1005 map<file_path, file_path> renames;
1006 file_path foo = file_path_internal("foo");
1007 file_path foo_bar = file_path_internal("foo/bar");
1008 file_path foo_baz = file_path_internal("foo/baz");
1009 file_path quux = file_path_internal("quux");
1010 file_path quux_baz = file_path_internal("quux/baz");
1011 I(foo == find_old_path_for(renames, foo));
1012 I(foo == find_new_path_for(renames, foo));
1013 I(foo_bar == find_old_path_for(renames, foo_bar));
1014 I(foo_bar == find_new_path_for(renames, foo_bar));
1015 I(quux == find_old_path_for(renames, quux));
1016 I(quux == find_new_path_for(renames, quux));
1017 renames.insert(make_pair(foo, quux));
1018 renames.insert(make_pair(foo_bar, foo_baz));
1019 I(quux == find_old_path_for(renames, foo));
1020 I(foo == find_new_path_for(renames, quux));
1021 I(quux_baz == find_old_path_for(renames, foo_baz));
1022 I(foo_baz == find_new_path_for(renames, quux_baz));
1023 I(foo_baz == find_old_path_for(renames, foo_bar));
1024 I(foo_bar == find_new_path_for(renames, foo_baz));
1025 }
1026
1027 // Local Variables:
1028 // mode: C++
1029 // fill-column: 76
1030 // c-file-style: "gnu"
1031 // indent-tabs-mode: nil
1032 // End:
1033 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
1034