xref: /llvm-project/libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp (revision aa427b1aae445ed46d9f60c5e2eaac61bdf76be3)
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