| 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: availability-filesystem-missing |
| 11 | |
| 12 | // <filesystem> |
| 13 | |
| 14 | // class path |
| 15 | |
| 16 | // int compare(path const&) const noexcept; |
| 17 | // int compare(string_type const&) const; |
| 18 | // int compare(value_type const*) const; |
| 19 | // |
| 20 | // bool operator==(path const&, path const&) noexcept; |
| 21 | // bool operator!=(path const&, path const&) noexcept; |
| 22 | // bool operator< (path const&, path const&) noexcept; |
| 23 | // bool operator<=(path const&, path const&) noexcept; |
| 24 | // bool operator> (path const&, path const&) noexcept; |
| 25 | // bool operator>=(path const&, path const&) noexcept; |
| 26 | // strong_ordering operator<=>(path const&, path const&) noexcept; |
| 27 | // |
| 28 | // size_t hash_value(path const&) noexcept; |
| 29 | // template<> struct hash<filesystem::path>; |
| 30 | |
| 31 | #include <filesystem> |
| 32 | #include <cassert> |
| 33 | #include <string> |
| 34 | #include <type_traits> |
| 35 | #include <vector> |
| 36 | |
| 37 | #include "assert_macros.h" |
| 38 | #include "count_new.h" |
| 39 | #include "test_comparisons.h" |
| 40 | #include "test_iterators.h" |
| 41 | #include "test_macros.h" |
| 42 | namespace fs = std::filesystem; |
| 43 | |
| 44 | struct PathCompareTest { |
| 45 | const char* LHS; |
| 46 | const char* RHS; |
| 47 | int expect; |
| 48 | }; |
| 49 | |
| 50 | #define LONGA \ |
| 51 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \ |
| 52 | "AAAAAAAA" |
| 53 | #define LONGB \ |
| 54 | "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" \ |
| 55 | "BBBBBBBB" |
| 56 | #define LONGC \ |
| 57 | "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" \ |
| 58 | "CCCCCCCC" |
| 59 | #define LONGD \ |
| 60 | "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" \ |
| 61 | "DDDDDDDD" |
| 62 | const PathCompareTest CompareTestCases[] = { |
| 63 | {.LHS: "" , .RHS: "" , .expect: 0}, |
| 64 | {.LHS: "a" , .RHS: "" , .expect: 1}, |
| 65 | {.LHS: "" , .RHS: "a" , .expect: -1}, |
| 66 | {.LHS: "a/b/c" , .RHS: "a/b/c" , .expect: 0}, |
| 67 | {.LHS: "b/a/c" , .RHS: "a/b/c" , .expect: 1}, |
| 68 | {.LHS: "a/b/c" , .RHS: "b/a/c" , .expect: -1}, |
| 69 | {.LHS: "a/b" , .RHS: "a/b/c" , .expect: -1}, |
| 70 | {.LHS: "a/b/c" , .RHS: "a/b" , .expect: 1}, |
| 71 | {.LHS: "a/b/" , .RHS: "a/b/." , .expect: -1}, |
| 72 | {.LHS: "a/b/" , .RHS: "a/b" , .expect: 1}, |
| 73 | {.LHS: "a/b//////" , .RHS: "a/b/////." , .expect: -1}, |
| 74 | {.LHS: "a/.././b" , .RHS: "a///..//.////b" , .expect: 0}, |
| 75 | {.LHS: "//foo//bar///baz////" , .RHS: "//foo/bar/baz/" , .expect: 0}, // duplicate separators |
| 76 | {.LHS: "///foo/bar" , .RHS: "/foo/bar" , .expect: 0}, // "///" is not a root directory |
| 77 | {.LHS: "/foo/bar/" , .RHS: "/foo/bar" , .expect: 1}, // trailing separator |
| 78 | {.LHS: "foo" , .RHS: "/foo" , .expect: -1}, // if !this->has_root_directory() and p.has_root_directory(), a value less than 0. |
| 79 | {.LHS: "/foo" , .RHS: "foo" , .expect: 1}, // if this->has_root_directory() and !p.has_root_directory(), a value greater than 0. |
| 80 | #ifdef _WIN32 |
| 81 | {"C:/a" , "C:\\a" , 0}, |
| 82 | #else |
| 83 | {.LHS: "C:/a" , .RHS: "C:\\a" , .expect: -1}, |
| 84 | #endif |
| 85 | {.LHS: ("//" LONGA "////" LONGB "/" LONGC "///" LONGD), .RHS: ("//" LONGA "/" LONGB "/" LONGC "/" LONGD), .expect: 0}, |
| 86 | {.LHS: (LONGA "/" LONGB "/" LONGC), .RHS: (LONGA "/" LONGB "/" LONGB), .expect: 1} |
| 87 | |
| 88 | }; |
| 89 | #undef LONGA |
| 90 | #undef LONGB |
| 91 | #undef LONGC |
| 92 | #undef LONGD |
| 93 | |
| 94 | static inline int normalize_ret(int ret) { return ret < 0 ? -1 : (ret > 0 ? 1 : 0); } |
| 95 | |
| 96 | void test_compare_basic() { |
| 97 | using namespace fs; |
| 98 | for (auto const& TC : CompareTestCases) { |
| 99 | const path p1(TC.LHS); |
| 100 | const path p2(TC.RHS); |
| 101 | std::string RHS(TC.RHS); |
| 102 | const path::string_type R(RHS.begin(), RHS.end()); |
| 103 | const std::basic_string_view<path::value_type> RV(R); |
| 104 | const path::value_type* Ptr = R.c_str(); |
| 105 | const int E = TC.expect; |
| 106 | { // compare(...) functions |
| 107 | DisableAllocationGuard g; // none of these operations should allocate |
| 108 | |
| 109 | // check runtime results |
| 110 | int ret1 = normalize_ret(ret: p1.compare(p: p2)); |
| 111 | int ret2 = normalize_ret(ret: p1.compare(s: R)); |
| 112 | int ret3 = normalize_ret(ret: p1.compare(s: Ptr)); |
| 113 | int ret4 = normalize_ret(ret: p1.compare(s: RV)); |
| 114 | |
| 115 | g.release(); |
| 116 | assert(ret1 == ret2); |
| 117 | assert(ret1 == ret3); |
| 118 | assert(ret1 == ret4); |
| 119 | assert(ret1 == E); |
| 120 | |
| 121 | // check signatures |
| 122 | ASSERT_NOEXCEPT(p1.compare(p: p2)); |
| 123 | } |
| 124 | { // comparison operators |
| 125 | DisableAllocationGuard g; // none of these operations should allocate |
| 126 | |
| 127 | // check signatures |
| 128 | AssertComparisonsAreNoexcept<path>(); |
| 129 | AssertComparisonsReturnBool<path>(); |
| 130 | #if TEST_STD_VER > 17 |
| 131 | AssertOrderAreNoexcept<path>(); |
| 132 | AssertOrderReturn<std::strong_ordering, path>(); |
| 133 | #endif |
| 134 | |
| 135 | // check comarison results |
| 136 | assert(testComparisons(p1, p2, /*isEqual*/ E == 0, /*isLess*/ E < 0)); |
| 137 | #if TEST_STD_VER > 17 |
| 138 | assert(testOrder(p1, p2, E <=> 0)); |
| 139 | #endif |
| 140 | } |
| 141 | { // check hash values |
| 142 | auto h1 = hash_value(p: p1); |
| 143 | auto h2 = hash_value(p: p2); |
| 144 | assert((h1 == h2) == (p1 == p2)); |
| 145 | // check signature |
| 146 | ASSERT_SAME_TYPE(std::size_t, decltype(hash_value(p1))); |
| 147 | ASSERT_NOEXCEPT(hash_value(p: p1)); |
| 148 | } |
| 149 | { // check std::hash |
| 150 | auto h1 = std::hash<fs::path>()(p1); |
| 151 | auto h2 = std::hash<fs::path>()(p2); |
| 152 | assert((h1 == h2) == (p1 == p2)); |
| 153 | // check signature |
| 154 | ASSERT_SAME_TYPE(std::size_t, decltype(std::hash<fs::path>()(p1))); |
| 155 | ASSERT_NOEXCEPT(std::hash<fs::path>()(p1)); |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | int CompareElements(std::vector<std::string> const& LHS, std::vector<std::string> const& RHS) { |
| 161 | bool IsLess = std::lexicographical_compare(first1: LHS.begin(), last1: LHS.end(), first2: RHS.begin(), last2: RHS.end()); |
| 162 | if (IsLess) |
| 163 | return -1; |
| 164 | |
| 165 | bool IsGreater = std::lexicographical_compare(first1: RHS.begin(), last1: RHS.end(), first2: LHS.begin(), last2: LHS.end()); |
| 166 | if (IsGreater) |
| 167 | return 1; |
| 168 | |
| 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | void test_compare_elements() { |
| 173 | struct { |
| 174 | std::vector<std::string> LHSElements; |
| 175 | std::vector<std::string> RHSElements; |
| 176 | int Expect; |
| 177 | } TestCases[] = { |
| 178 | {.LHSElements: {"a" }, .RHSElements: {"a" }, .Expect: 0}, |
| 179 | {.LHSElements: {"a" }, .RHSElements: {"b" }, .Expect: -1}, |
| 180 | {.LHSElements: {"b" }, .RHSElements: {"a" }, .Expect: 1}, |
| 181 | {.LHSElements: {"a" , "b" , "c" }, .RHSElements: {"a" , "b" , "c" }, .Expect: 0}, |
| 182 | {.LHSElements: {"a" , "b" , "c" }, .RHSElements: {"a" , "b" , "d" }, .Expect: -1}, |
| 183 | {.LHSElements: {"a" , "b" , "d" }, .RHSElements: {"a" , "b" , "c" }, .Expect: 1}, |
| 184 | {.LHSElements: {"a" , "b" }, .RHSElements: {"a" , "b" , "c" }, .Expect: -1}, |
| 185 | {.LHSElements: {"a" , "b" , "c" }, .RHSElements: {"a" , "b" }, .Expect: 1}, |
| 186 | |
| 187 | }; |
| 188 | |
| 189 | auto BuildPath = [](std::vector<std::string> const& Elems) { |
| 190 | fs::path p; |
| 191 | for (auto& E : Elems) |
| 192 | p /= E; |
| 193 | return p; |
| 194 | }; |
| 195 | |
| 196 | for (auto& TC : TestCases) { |
| 197 | fs::path LHS = BuildPath(TC.LHSElements); |
| 198 | fs::path RHS = BuildPath(TC.RHSElements); |
| 199 | const int ExpectCmp = CompareElements(LHS: TC.LHSElements, RHS: TC.RHSElements); |
| 200 | assert(ExpectCmp == TC.Expect); |
| 201 | const int GotCmp = normalize_ret(ret: LHS.compare(p: RHS)); |
| 202 | assert(GotCmp == TC.Expect); |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | int main(int, char**) { |
| 207 | test_compare_basic(); |
| 208 | test_compare_elements(); |
| 209 | |
| 210 | return 0; |
| 211 | } |
| 212 | |