xref: /llvm-project/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp (revision c352fa7407122ee62d990d6b82551650149f98d4)
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
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_include.h"
20 #include <cassert>
21 #include <cstdio>
22 
23 #include "test_macros.h"
24 #include "count_new.h"
25 #include "filesystem_test_helper.h"
26 #include "../../class.path/path_helper.h"
27 
28 static int count_path_elems(const fs::path& p) {
29   int count = 0;
30   for (auto&& elem : p) {
31     if (elem != p.root_name() && elem != "/" && elem != "")
32       ++count;
33   }
34   return count;
35 }
36 
37 
38 static void signature_test()
39 {
40     using fs::path;
41     const path p; ((void)p);
42     std::error_code ec; ((void)ec);
43     ASSERT_NOT_NOEXCEPT(proximate(p));
44     ASSERT_NOT_NOEXCEPT(proximate(p, p));
45     ASSERT_NOT_NOEXCEPT(proximate(p, ec));
46     ASSERT_NOT_NOEXCEPT(proximate(p, p, ec));
47 }
48 
49 static void basic_test() {
50   using fs::path;
51   const path cwd = fs::current_path();
52   const path parent_cwd = cwd.parent_path();
53   const path curdir = cwd.filename();
54   assert(!cwd.native().empty());
55   int cwd_depth = count_path_elems(cwd);
56   path dot_dot_to_root;
57   for (int i=0; i < cwd_depth; ++i)
58     dot_dot_to_root /= "..";
59   path relative_cwd = cwd.native().substr(cwd.root_path().native().size());
60   // clang-format off
61   struct {
62     fs::path input;
63     fs::path base;
64     fs::path expect;
65   } TestCases[] = {
66       {"", "", "."},
67       {cwd, "a", ".."},
68       {parent_cwd, "a", "../.."},
69       {"a", cwd, "a"},
70       {"a", parent_cwd, curdir / "a"},
71       {"/", "a", dot_dot_to_root / ".."},
72       {"/", "a/b", dot_dot_to_root / "../.."},
73       {"/", "a/b/", dot_dot_to_root / "../.."},
74       {"a", "/", relative_cwd / "a"},
75       {"a/b", "/", relative_cwd / "a/b"},
76       {"a", "/net", ".." / relative_cwd / "a"},
77 #ifdef _WIN32
78       {"//foo/", "//foo", "//foo/"},
79       {"//foo", "//foo/", "//foo"},
80 #else
81       {"//foo/", "//foo", "."},
82       {"//foo", "//foo/", "."},
83 #endif
84       {"//foo", "//foo", "."},
85       {"//foo/", "//foo/", "."},
86 #ifdef _WIN32
87       {"//foo", "a", "//foo"},
88       {"//foo/a", "//bar", "//foo/a"},
89       {"//foo/a", "//bar/", "//foo/a"},
90       {"//foo/a", "b", "//foo/a"},
91       {"//foo/a", "/b", "//foo/a"},
92       {"//foo/a", "//bar/b", "//foo/a"},
93       // Using X: instead of C: to avoid influence from the CWD being under C:
94       {"X:/a", "X:/b", "../a"},
95       {"X:/a", "X:b", "X:/a"},
96       {"X:/a", "Y:/a", "X:/a"},
97       {"X:/a", "Y:/b", "X:/a"},
98       {"X:/a", "Y:b", "X:/a"},
99       {"X:a", "X:/b", "X:a"},
100       {"X:a", "X:b", "../a"},
101       {"X:a", "Y:/a", "X:a"},
102       {"X:a", "Y:/b", "X:a"},
103       {"X:a", "Y:b", "X:a"},
104 #else
105       {"//foo", "a", dot_dot_to_root / "../foo"},
106       {"//foo/a", "//bar", "../foo/a"},
107       {"//foo/a", "//bar/", "../foo/a"},
108       {"//foo/a", "b", dot_dot_to_root / "../foo/a"},
109       {"//foo/a", "/b", "../foo/a"},
110       {"//foo/a", "//bar/b", "../../foo/a"},
111       {"X:/a", "X:/b", "../a"},
112       {"X:/a", "X:b", "../X:/a"},
113       {"X:/a", "Y:/a", "../../X:/a"},
114       {"X:/a", "Y:/b", "../../X:/a"},
115       {"X:/a", "Y:b", "../X:/a"},
116       {"X:a", "X:/b", "../../X:a"},
117       {"X:a", "X:b", "../X:a"},
118       {"X:a", "Y:/a", "../../X:a"},
119       {"X:a", "Y:/b", "../../X:a"},
120       {"X:a", "Y:b", "../X:a"},
121 #endif
122       {"a", "a", "."},
123       {"a/b", "a/b", "."},
124       {"a/b/c/", "a/b/c/", "."},
125       {"//foo/a/b", "//foo/a/b", "."},
126       {"/a/d", "/a/b/c", "../../d"},
127       {"/a/b/c", "/a/d", "../b/c"},
128       {"a/b/c", "a", "b/c"},
129       {"a/b/c", "a/b/c/x/y", "../.."},
130       {"a/b/c", "a/b/c", "."},
131       {"a/b", "c/d", "../../a/b"}
132   };
133   // clang-format on
134   int ID = 0;
135   for (auto& TC : TestCases) {
136     ++ID;
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     if (ec) {
143       assert(!ec);
144       std::fprintf(stderr, "TEST CASE #%d FAILED:\n"
145                   "  Input: '%s'\n"
146                   "  Base: '%s'\n"
147                   "  Expected: '%s'\n",
148         ID, TC.input.string().c_str(), TC.base.string().c_str(),
149         expect.string().c_str());
150     } else if (!PathEq(output, expect)) {
151       assert(PathEq(output, expect));
152 
153       const path canon_input = fs::weakly_canonical(TC.input);
154       const path canon_base = fs::weakly_canonical(TC.base);
155       const path lexically_p = canon_input.lexically_proximate(canon_base);
156       std::fprintf(stderr, "TEST CASE #%d FAILED:\n"
157                   "  Input: '%s'\n"
158                   "  Base: '%s'\n"
159                   "  Expected: '%s'\n"
160                   "  Output: '%s'\n"
161                   "  Lex Prox: '%s'\n"
162                   "  Canon Input: '%s'\n"
163                   "  Canon Base: '%s'\n",
164         ID, TC.input.string().c_str(), TC.base.string().c_str(),
165         expect.string().c_str(), output.string().c_str(),
166         lexically_p.string().c_str(), canon_input.string().c_str(),
167         canon_base.string().c_str());
168     }
169   }
170 }
171 
172 int main(int, char**) {
173   signature_test();
174   basic_test();
175 
176   return 0;
177 }
178