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"
42namespace fs = std::filesystem;
43
44struct 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"
62const 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
94static inline int normalize_ret(int ret) { return ret < 0 ? -1 : (ret > 0 ? 1 : 0); }
95
96void 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
160int 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
172void 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
206int main(int, char**) {
207 test_compare_basic();
208 test_compare_elements();
209
210 return 0;
211}
212

source code of libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp