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 | |