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 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 29 static int count_path_elems(const fs::path& p) { 30 int count = 0; 31 for (auto&& elem : p) { 32 if (elem != p.root_name() && elem != "/" && elem != "") 33 ++count; 34 } 35 return count; 36 } 37 38 static void signature_test() { 39 using fs::path; 40 const path p; 41 ((void)p); 42 std::error_code ec; 43 ((void)ec); 44 ASSERT_NOT_NOEXCEPT(proximate(p)); 45 ASSERT_NOT_NOEXCEPT(proximate(p, p)); 46 ASSERT_NOT_NOEXCEPT(proximate(p, ec)); 47 ASSERT_NOT_NOEXCEPT(proximate(p, p, ec)); 48 } 49 50 static void basic_test() { 51 using fs::path; 52 const path cwd = fs::current_path(); 53 const path parent_cwd = cwd.parent_path(); 54 const path curdir = cwd.filename(); 55 assert(!cwd.native().empty()); 56 int cwd_depth = count_path_elems(cwd); 57 path dot_dot_to_root; 58 for (int i = 0; i < cwd_depth; ++i) 59 dot_dot_to_root /= ".."; 60 path relative_cwd = cwd.native().substr(cwd.root_path().native().size()); 61 // clang-format off 62 struct { 63 fs::path input; 64 fs::path base; 65 fs::path expect; 66 } TestCases[] = { 67 {"", "", "."}, 68 {cwd, "a", ".."}, 69 {parent_cwd, "a", "../.."}, 70 {"a", cwd, "a"}, 71 {"a", parent_cwd, curdir / "a"}, 72 {"/", "a", dot_dot_to_root / ".."}, 73 {"/", "a/b", dot_dot_to_root / "../.."}, 74 {"/", "a/b/", dot_dot_to_root / "../.."}, 75 {"a", "/", relative_cwd / "a"}, 76 {"a/b", "/", relative_cwd / "a/b"}, 77 {"a", "/net", ".." / relative_cwd / "a"}, 78 #ifdef _WIN32 79 {"//foo/", "//foo", "//foo/"}, 80 {"//foo", "//foo/", "//foo"}, 81 #else 82 {"//foo/", "//foo", "."}, 83 {"//foo", "//foo/", "."}, 84 #endif 85 {"//foo", "//foo", "."}, 86 {"//foo/", "//foo/", "."}, 87 #ifdef _WIN32 88 {"//foo", "a", "//foo"}, 89 {"//foo/a", "//bar", "//foo/a"}, 90 {"//foo/a", "//bar/", "//foo/a"}, 91 {"//foo/a", "b", "//foo/a"}, 92 {"//foo/a", "/b", "//foo/a"}, 93 {"//foo/a", "//bar/b", "//foo/a"}, 94 // Using X: instead of C: to avoid influence from the CWD being under C: 95 {"X:/a", "X:/b", "../a"}, 96 {"X:/a", "X:b", "X:/a"}, 97 {"X:/a", "Y:/a", "X:/a"}, 98 {"X:/a", "Y:/b", "X:/a"}, 99 {"X:/a", "Y:b", "X:/a"}, 100 {"X:a", "X:/b", "X:a"}, 101 {"X:a", "X:b", "../a"}, 102 {"X:a", "Y:/a", "X:a"}, 103 {"X:a", "Y:/b", "X:a"}, 104 {"X:a", "Y:b", "X:a"}, 105 #else 106 {"//foo", "a", dot_dot_to_root / "../foo"}, 107 {"//foo/a", "//bar", "../foo/a"}, 108 {"//foo/a", "//bar/", "../foo/a"}, 109 {"//foo/a", "b", dot_dot_to_root / "../foo/a"}, 110 {"//foo/a", "/b", "../foo/a"}, 111 {"//foo/a", "//bar/b", "../../foo/a"}, 112 {"X:/a", "X:/b", "../a"}, 113 {"X:/a", "X:b", "../X:/a"}, 114 {"X:/a", "Y:/a", "../../X:/a"}, 115 {"X:/a", "Y:/b", "../../X:/a"}, 116 {"X:/a", "Y:b", "../X:/a"}, 117 {"X:a", "X:/b", "../../X:a"}, 118 {"X:a", "X:b", "../X:a"}, 119 {"X:a", "Y:/a", "../../X:a"}, 120 {"X:a", "Y:/b", "../../X:a"}, 121 {"X:a", "Y:b", "../X:a"}, 122 #endif 123 {"a", "a", "."}, 124 {"a/b", "a/b", "."}, 125 {"a/b/c/", "a/b/c/", "."}, 126 {"//foo/a/b", "//foo/a/b", "."}, 127 {"/a/d", "/a/b/c", "../../d"}, 128 {"/a/b/c", "/a/d", "../b/c"}, 129 {"a/b/c", "a", "b/c"}, 130 {"a/b/c", "a/b/c/x/y", "../.."}, 131 {"a/b/c", "a/b/c", "."}, 132 {"a/b", "c/d", "../../a/b"} 133 }; 134 // clang-format on 135 for (auto& TC : TestCases) { 136 std::error_code ec = GetTestEC(); 137 fs::path p = TC.input; 138 const fs::path output = fs::proximate(p, TC.base, ec); 139 fs::path expect = TC.expect; 140 expect.make_preferred(); 141 TEST_REQUIRE(!ec, 142 TEST_WRITE_CONCATENATED( 143 "Input: ", TC.input.string(), "\nBase: ", TC.base.string(), "\nExpected: ", expect.string())); 144 145 const path canon_input = fs::weakly_canonical(TC.input); 146 const path canon_base = fs::weakly_canonical(TC.base); 147 const path lexically_p = canon_input.lexically_proximate(canon_base); 148 TEST_REQUIRE( 149 PathEq(output, expect), 150 TEST_WRITE_CONCATENATED( 151 "Input: ", 152 TC.input.string(), 153 "\nBase: ", 154 TC.base.string(), 155 "\nExpected: ", 156 expect.string(), 157 "\nOutput: ", 158 output.string(), 159 "\nLex Prox: ", 160 lexically_p.string(), 161 "\nCanon Input: ", 162 canon_input.string(), 163 "\nCanon Base: ", 164 canon_base.string())); 165 } 166 } 167 168 int main(int, char**) { 169 signature_test(); 170 basic_test(); 171 172 return 0; 173 } 174