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