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