1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14
10 // UNSUPPORTED: no-filesystem
11 // UNSUPPORTED: availability-filesystem-missing
12 
13 // <filesystem>
14 
15 // path proximate(const path& p, error_code &ec)
16 // path proximate(const path& p, const path& base = current_path())
17 // path proximate(const path& p, const path& base, error_code& ec);
18 
19 #include <filesystem>
20 #include <cassert>
21 
22 #include "assert_macros.h"
23 #include "concat_macros.h"
24 #include "test_macros.h"
25 #include "count_new.h"
26 #include "filesystem_test_helper.h"
27 #include "../../class.path/path_helper.h"
28 namespace fs = std::filesystem;
29 
count_path_elems(const fs::path & p)30 static int count_path_elems(const fs::path& p) {
31   int count = 0;
32   for (auto&& elem : p) {
33     if (elem != p.root_name() && elem != "/" && elem != "")
34       ++count;
35   }
36   return count;
37 }
38 
signature_test()39 static void signature_test() {
40   using fs::path;
41   const path p;
42   ((void)p);
43   std::error_code ec;
44   ((void)ec);
45   ASSERT_NOT_NOEXCEPT(proximate(p));
46   ASSERT_NOT_NOEXCEPT(proximate(p, p));
47   ASSERT_NOT_NOEXCEPT(proximate(p, ec));
48   ASSERT_NOT_NOEXCEPT(proximate(p, p, ec));
49 }
50 
basic_test()51 static void basic_test() {
52   using fs::path;
53   const path cwd        = fs::current_path();
54   const path parent_cwd = cwd.parent_path();
55   const path curdir     = cwd.filename();
56   assert(!cwd.native().empty());
57   int cwd_depth = count_path_elems(cwd);
58   path dot_dot_to_root;
59   for (int i = 0; i < cwd_depth; ++i)
60     dot_dot_to_root /= "..";
61   path relative_cwd = cwd.native().substr(cwd.root_path().native().size());
62   // clang-format off
63   struct {
64     fs::path input;
65     fs::path base;
66     fs::path expect;
67   } TestCases[] = {
68       {"", "", "."},
69       {cwd, "a", ".."},
70       {parent_cwd, "a", "../.."},
71       {"a", cwd, "a"},
72       {"a", parent_cwd, curdir / "a"},
73       {"/", "a", dot_dot_to_root / ".."},
74       {"/", "a/b", dot_dot_to_root / "../.."},
75       {"/", "a/b/", dot_dot_to_root / "../.."},
76       {"a", "/", relative_cwd / "a"},
77       {"a/b", "/", relative_cwd / "a/b"},
78       {"a", "/net", ".." / relative_cwd / "a"},
79 #ifdef _WIN32
80       {"//foo/", "//foo", "//foo/"},
81       {"//foo", "//foo/", "//foo"},
82 #else
83       {"//foo/", "//foo", "."},
84       {"//foo", "//foo/", "."},
85 #endif
86       {"//foo", "//foo", "."},
87       {"//foo/", "//foo/", "."},
88 #ifdef _WIN32
89       {"//foo", "a", "//foo"},
90       {"//foo/a", "//bar", "//foo/a"},
91       {"//foo/a", "//bar/", "//foo/a"},
92       {"//foo/a", "b", "//foo/a"},
93       {"//foo/a", "/b", "//foo/a"},
94       {"//foo/a", "//bar/b", "//foo/a"},
95       // Using X: instead of C: to avoid influence from the CWD being under C:
96       {"X:/a", "X:/b", "../a"},
97       {"X:/a", "X:b", "X:/a"},
98       {"X:/a", "Y:/a", "X:/a"},
99       {"X:/a", "Y:/b", "X:/a"},
100       {"X:/a", "Y:b", "X:/a"},
101       {"X:a", "X:/b", "X:a"},
102       {"X:a", "X:b", "../a"},
103       {"X:a", "Y:/a", "X:a"},
104       {"X:a", "Y:/b", "X:a"},
105       {"X:a", "Y:b", "X:a"},
106 #else
107       {"//foo", "a", dot_dot_to_root / "../foo"},
108       {"//foo/a", "//bar", "../foo/a"},
109       {"//foo/a", "//bar/", "../foo/a"},
110       {"//foo/a", "b", dot_dot_to_root / "../foo/a"},
111       {"//foo/a", "/b", "../foo/a"},
112       {"//foo/a", "//bar/b", "../../foo/a"},
113       {"X:/a", "X:/b", "../a"},
114       {"X:/a", "X:b", "../X:/a"},
115       {"X:/a", "Y:/a", "../../X:/a"},
116       {"X:/a", "Y:/b", "../../X:/a"},
117       {"X:/a", "Y:b", "../X:/a"},
118       {"X:a", "X:/b", "../../X:a"},
119       {"X:a", "X:b", "../X:a"},
120       {"X:a", "Y:/a", "../../X:a"},
121       {"X:a", "Y:/b", "../../X:a"},
122       {"X:a", "Y:b", "../X:a"},
123 #endif
124       {"a", "a", "."},
125       {"a/b", "a/b", "."},
126       {"a/b/c/", "a/b/c/", "."},
127       {"//foo/a/b", "//foo/a/b", "."},
128       {"/a/d", "/a/b/c", "../../d"},
129       {"/a/b/c", "/a/d", "../b/c"},
130       {"a/b/c", "a", "b/c"},
131       {"a/b/c", "a/b/c/x/y", "../.."},
132       {"a/b/c", "a/b/c", "."},
133       {"a/b", "c/d", "../../a/b"}
134   };
135   // clang-format on
136   for (auto& TC : TestCases) {
137     std::error_code ec    = GetTestEC();
138     fs::path p            = TC.input;
139     const fs::path output = fs::proximate(p, TC.base, ec);
140     fs::path expect       = TC.expect;
141     expect.make_preferred();
142     TEST_REQUIRE(!ec,
143                  TEST_WRITE_CONCATENATED(
144                      "Input: ", TC.input.string(), "\nBase: ", TC.base.string(), "\nExpected: ", expect.string()));
145 
146     const path canon_input = fs::weakly_canonical(TC.input);
147     const path canon_base  = fs::weakly_canonical(TC.base);
148     const path lexically_p = canon_input.lexically_proximate(canon_base);
149     TEST_REQUIRE(
150         PathEq(output, expect),
151         TEST_WRITE_CONCATENATED(
152             "Input: ",
153             TC.input.string(),
154             "\nBase: ",
155             TC.base.string(),
156             "\nExpected: ",
157             expect.string(),
158             "\nOutput: ",
159             output.string(),
160             "\nLex Prox: ",
161             lexically_p.string(),
162             "\nCanon Input: ",
163             canon_input.string(),
164             "\nCanon Base: ",
165             canon_base.string()));
166   }
167 }
168 
main(int,char **)169 int main(int, char**) {
170   signature_test();
171   basic_test();
172 
173   return 0;
174 }
175