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