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 {"", "", 0}, 64 {"a", "", 1}, 65 {"", "a", -1}, 66 {"a/b/c", "a/b/c", 0}, 67 {"b/a/c", "a/b/c", 1}, 68 {"a/b/c", "b/a/c", -1}, 69 {"a/b", "a/b/c", -1}, 70 {"a/b/c", "a/b", 1}, 71 {"a/b/", "a/b/.", -1}, 72 {"a/b/", "a/b", 1}, 73 {"a/b//////", "a/b/////.", -1}, 74 {"a/.././b", "a///..//.////b", 0}, 75 {"//foo//bar///baz////", "//foo/bar/baz/", 0}, // duplicate separators 76 {"///foo/bar", "/foo/bar", 0}, // "///" is not a root directory 77 {"/foo/bar/", "/foo/bar", 1}, // trailing separator 78 {"foo", "/foo", -1}, // if !this->has_root_directory() and p.has_root_directory(), a value less than 0. 79 {"/foo", "foo", 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 {"C:/a", "C:\\a", -1}, 84 #endif 85 {("//" LONGA "////" LONGB "/" LONGC "///" LONGD), ("//" LONGA "/" LONGB "/" LONGC "/" LONGD), 0}, 86 {(LONGA "/" LONGB "/" LONGC), (LONGA "/" LONGB "/" LONGB), 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(p1.compare(p2)); 111 int ret2 = normalize_ret(p1.compare(R)); 112 int ret3 = normalize_ret(p1.compare(Ptr)); 113 int ret4 = normalize_ret(p1.compare(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(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(p1); 143 auto h2 = hash_value(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(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(LHS.begin(), LHS.end(), RHS.begin(), RHS.end()); 162 if (IsLess) 163 return -1; 164 165 bool IsGreater = std::lexicographical_compare(RHS.begin(), RHS.end(), LHS.begin(), 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 {{"a"}, {"a"}, 0}, 179 {{"a"}, {"b"}, -1}, 180 {{"b"}, {"a"}, 1}, 181 {{"a", "b", "c"}, {"a", "b", "c"}, 0}, 182 {{"a", "b", "c"}, {"a", "b", "d"}, -1}, 183 {{"a", "b", "d"}, {"a", "b", "c"}, 1}, 184 {{"a", "b"}, {"a", "b", "c"}, -1}, 185 {{"a", "b", "c"}, {"a", "b"}, 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(TC.LHSElements, TC.RHSElements); 200 assert(ExpectCmp == TC.Expect); 201 const int GotCmp = normalize_ret(LHS.compare(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