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 931cbe0f2SLouis Dionne // UNSUPPORTED: c++03 10f7b43230SLouis Dionne 11*88ffc727SLouis Dionne // These tests require locale for non-char paths 12*88ffc727SLouis Dionne // UNSUPPORTED: libcpp-has-no-localization 13*88ffc727SLouis Dionne 14f7b43230SLouis Dionne // <filesystem> 15f7b43230SLouis Dionne 16f7b43230SLouis Dionne // class path 17f7b43230SLouis Dionne 18f7b43230SLouis Dionne // path& operator/=(path const&) 19f7b43230SLouis Dionne // template <class Source> 20f7b43230SLouis Dionne // path& operator/=(Source const&); 21f7b43230SLouis Dionne // template <class Source> 22f7b43230SLouis Dionne // path& append(Source const&); 23f7b43230SLouis Dionne // template <class InputIterator> 24f7b43230SLouis Dionne // path& append(InputIterator first, InputIterator last); 25f7b43230SLouis Dionne 26f7b43230SLouis Dionne 27cc89063bSNico Weber #include "filesystem_include.h" 28f7b43230SLouis Dionne #include <type_traits> 29f7b43230SLouis Dionne #include <string_view> 30f7b43230SLouis Dionne #include <cassert> 31f7b43230SLouis Dionne 32f7b43230SLouis Dionne #include "test_macros.h" 33f7b43230SLouis Dionne #include "test_iterators.h" 34cc89063bSNico Weber #include "count_new.h" 35cc89063bSNico Weber #include "filesystem_test_helper.h" 36f7b43230SLouis Dionne 37f7b43230SLouis Dionne 38f7b43230SLouis Dionne struct AppendOperatorTestcase { 39f7b43230SLouis Dionne MultiStringType lhs; 40f7b43230SLouis Dionne MultiStringType rhs; 41f7b43230SLouis Dionne MultiStringType expect; 42f7b43230SLouis Dionne }; 43f7b43230SLouis Dionne 44f7b43230SLouis Dionne #define S(Str) MKSTR(Str) 45f7b43230SLouis Dionne const AppendOperatorTestcase Cases[] = 46f7b43230SLouis Dionne { 47f7b43230SLouis Dionne {S(""), S(""), S("")} 48f7b43230SLouis Dionne , {S("p1"), S("p2"), S("p1/p2")} 49f7b43230SLouis Dionne , {S("p1/"), S("p2"), S("p1/p2")} 50f7b43230SLouis Dionne , {S("p1"), S("/p2"), S("/p2")} 51f7b43230SLouis Dionne , {S("p1/"), S("/p2"), S("/p2")} 52f7b43230SLouis Dionne , {S("p1"), S("\\p2"), S("p1/\\p2")} 53f7b43230SLouis Dionne , {S("p1\\"), S("p2"), S("p1\\/p2")} 54f7b43230SLouis Dionne , {S("p1\\"), S("\\p2"), S("p1\\/\\p2")} 55f7b43230SLouis Dionne , {S(""), S("p2"), S("p2")} 56f7b43230SLouis Dionne , {S("/p1"), S("p2"), S("/p1/p2")} 57f7b43230SLouis Dionne , {S("/p1"), S("/p2"), S("/p2")} 58f7b43230SLouis Dionne , {S("/p1/p3"), S("p2"), S("/p1/p3/p2")} 59f7b43230SLouis Dionne , {S("/p1/p3/"), S("p2"), S("/p1/p3/p2")} 60f7b43230SLouis Dionne , {S("/p1/"), S("p2"), S("/p1/p2")} 61f7b43230SLouis Dionne , {S("/p1/p3/"), S("/p2/p4"), S("/p2/p4")} 62f7b43230SLouis Dionne , {S("/"), S(""), S("/")} 63f7b43230SLouis Dionne , {S("/p1"), S("/p2/"), S("/p2/")} 64f7b43230SLouis Dionne , {S("p1"), S(""), S("p1/")} 65f7b43230SLouis Dionne , {S("p1/"), S(""), S("p1/")} 66f7b43230SLouis Dionne }; 67f7b43230SLouis Dionne 68f7b43230SLouis Dionne 69f7b43230SLouis Dionne const AppendOperatorTestcase LongLHSCases[] = 70f7b43230SLouis Dionne { 71f7b43230SLouis Dionne {S("p1"), S("p2"), S("p1/p2")} 72f7b43230SLouis Dionne , {S("p1/"), S("p2"), S("p1/p2")} 73f7b43230SLouis Dionne , {S("p1"), S("/p2"), S("/p2")} 74f7b43230SLouis Dionne , {S("/p1"), S("p2"), S("/p1/p2")} 75f7b43230SLouis Dionne }; 76f7b43230SLouis Dionne #undef S 77f7b43230SLouis Dionne 78f7b43230SLouis Dionne 79f7b43230SLouis Dionne // The append operator may need to allocate a temporary buffer before a code_cvt 80f7b43230SLouis Dionne // conversion. Test if this allocation occurs by: 81f7b43230SLouis Dionne // 1. Create a path, `LHS`, and reserve enough space to append `RHS`. 82f7b43230SLouis Dionne // This prevents `LHS` from allocating during the actual appending. 83f7b43230SLouis Dionne // 2. Create a `Source` object `RHS`, which represents a "large" string. 84f7b43230SLouis Dionne // (The string must not trigger the SSO) 85f7b43230SLouis Dionne // 3. Append `RHS` to `LHS` and check for the expected allocation behavior. 86f7b43230SLouis Dionne template <class CharT> 87f7b43230SLouis Dionne void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) 88f7b43230SLouis Dionne { 89f7b43230SLouis Dionne using namespace fs; 90f7b43230SLouis Dionne using Ptr = CharT const*; 91f7b43230SLouis Dionne using Str = std::basic_string<CharT>; 92f7b43230SLouis Dionne using StrView = std::basic_string_view<CharT>; 93f7b43230SLouis Dionne using InputIter = input_iterator<Ptr>; 94f7b43230SLouis Dionne 95f7b43230SLouis Dionne const Ptr L = TC.lhs; 96f7b43230SLouis Dionne Str RShort = (Ptr)TC.rhs; 97f7b43230SLouis Dionne Str EShort = (Ptr)TC.expect; 98f7b43230SLouis Dionne assert(RShort.size() >= 2); 99f7b43230SLouis Dionne CharT c = RShort.back(); 100f7b43230SLouis Dionne RShort.append(100, c); 101f7b43230SLouis Dionne EShort.append(100, c); 102f7b43230SLouis Dionne const Ptr R = RShort.data(); 103f7b43230SLouis Dionne const Str& E = EShort; 104f7b43230SLouis Dionne std::size_t ReserveSize = E.size() + 3; 105f7b43230SLouis Dionne // basic_string 106f7b43230SLouis Dionne { 107f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 108f7b43230SLouis Dionne Str RHS(R); 109f7b43230SLouis Dionne { 110f7b43230SLouis Dionne DisableAllocationGuard g; 111f7b43230SLouis Dionne LHS /= RHS; 112f7b43230SLouis Dionne } 113e557b6a6SLouis Dionne assert(PathEq(LHS, E)); 114f7b43230SLouis Dionne } 115f7b43230SLouis Dionne // basic_string_view 116f7b43230SLouis Dionne { 117f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 118f7b43230SLouis Dionne StrView RHS(R); 119f7b43230SLouis Dionne { 120f7b43230SLouis Dionne DisableAllocationGuard g; 121f7b43230SLouis Dionne LHS /= RHS; 122f7b43230SLouis Dionne } 123f7b43230SLouis Dionne assert(PathEq(LHS, E)); 124f7b43230SLouis Dionne } 125f7b43230SLouis Dionne // CharT* 126f7b43230SLouis Dionne { 127f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 128f7b43230SLouis Dionne Ptr RHS(R); 129f7b43230SLouis Dionne { 130f7b43230SLouis Dionne DisableAllocationGuard g; 131f7b43230SLouis Dionne LHS /= RHS; 132f7b43230SLouis Dionne } 133f7b43230SLouis Dionne assert(PathEq(LHS, E)); 134f7b43230SLouis Dionne } 135f7b43230SLouis Dionne { 136f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 137f7b43230SLouis Dionne Ptr RHS(R); 138f7b43230SLouis Dionne { 139f7b43230SLouis Dionne DisableAllocationGuard g; 140f7b43230SLouis Dionne LHS.append(RHS, StrEnd(RHS)); 141f7b43230SLouis Dionne } 142f7b43230SLouis Dionne assert(PathEq(LHS, E)); 143f7b43230SLouis Dionne } 144f7b43230SLouis Dionne // input iterator - For non-native char types, appends needs to copy the 145f7b43230SLouis Dionne // iterator range into a contiguous block of memory before it can perform the 146f7b43230SLouis Dionne // code_cvt conversions. 147f7b43230SLouis Dionne // For "char" no allocations will be performed because no conversion is 148f7b43230SLouis Dionne // required. 149f7b43230SLouis Dionne bool DisableAllocations = std::is_same<CharT, char>::value; 150f7b43230SLouis Dionne { 151f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 152f7b43230SLouis Dionne InputIter RHS(R); 153f7b43230SLouis Dionne { 154f7b43230SLouis Dionne RequireAllocationGuard g; // requires 1 or more allocations occur by default 155f7b43230SLouis Dionne if (DisableAllocations) g.requireExactly(0); 156f7b43230SLouis Dionne LHS /= RHS; 157f7b43230SLouis Dionne } 158f7b43230SLouis Dionne assert(PathEq(LHS, E)); 159f7b43230SLouis Dionne } 160f7b43230SLouis Dionne { 161f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 162f7b43230SLouis Dionne InputIter RHS(R); 163f7b43230SLouis Dionne InputIter REnd(StrEnd(R)); 164f7b43230SLouis Dionne { 165f7b43230SLouis Dionne RequireAllocationGuard g; 166f7b43230SLouis Dionne if (DisableAllocations) g.requireExactly(0); 167f7b43230SLouis Dionne LHS.append(RHS, REnd); 168f7b43230SLouis Dionne } 169f7b43230SLouis Dionne assert(PathEq(LHS, E)); 170f7b43230SLouis Dionne } 171f7b43230SLouis Dionne } 172f7b43230SLouis Dionne 173f7b43230SLouis Dionne template <class CharT> 174f7b43230SLouis Dionne void doAppendSourceTest(AppendOperatorTestcase const& TC) 175f7b43230SLouis Dionne { 176f7b43230SLouis Dionne using namespace fs; 177f7b43230SLouis Dionne using Ptr = CharT const*; 178f7b43230SLouis Dionne using Str = std::basic_string<CharT>; 179f7b43230SLouis Dionne using StrView = std::basic_string_view<CharT>; 180f7b43230SLouis Dionne using InputIter = input_iterator<Ptr>; 181f7b43230SLouis Dionne const Ptr L = TC.lhs; 182f7b43230SLouis Dionne const Ptr R = TC.rhs; 183f7b43230SLouis Dionne const Ptr E = TC.expect; 184f7b43230SLouis Dionne // basic_string 185f7b43230SLouis Dionne { 186f7b43230SLouis Dionne path Result(L); 187f7b43230SLouis Dionne Str RHS(R); 188f7b43230SLouis Dionne path& Ref = (Result /= RHS); 189e557b6a6SLouis Dionne assert(Result == E); 190f7b43230SLouis Dionne assert(&Ref == &Result); 191f7b43230SLouis Dionne } 192f7b43230SLouis Dionne { 193f7b43230SLouis Dionne path LHS(L); 194f7b43230SLouis Dionne Str RHS(R); 195f7b43230SLouis Dionne path& Ref = LHS.append(RHS); 196f7b43230SLouis Dionne assert(PathEq(LHS, E)); 197f7b43230SLouis Dionne assert(&Ref == &LHS); 198f7b43230SLouis Dionne } 199f7b43230SLouis Dionne // basic_string_view 200f7b43230SLouis Dionne { 201f7b43230SLouis Dionne path LHS(L); 202f7b43230SLouis Dionne StrView RHS(R); 203f7b43230SLouis Dionne path& Ref = (LHS /= RHS); 204f7b43230SLouis Dionne assert(PathEq(LHS, E)); 205f7b43230SLouis Dionne assert(&Ref == &LHS); 206f7b43230SLouis Dionne } 207f7b43230SLouis Dionne { 208f7b43230SLouis Dionne path LHS(L); 209f7b43230SLouis Dionne StrView RHS(R); 210f7b43230SLouis Dionne path& Ref = LHS.append(RHS); 211f7b43230SLouis Dionne assert(PathEq(LHS, E)); 212f7b43230SLouis Dionne assert(&Ref == &LHS); 213f7b43230SLouis Dionne } 214f7b43230SLouis Dionne // Char* 215f7b43230SLouis Dionne { 216f7b43230SLouis Dionne path LHS(L); 217f7b43230SLouis Dionne Str RHS(R); 218f7b43230SLouis Dionne path& Ref = (LHS /= RHS); 219f7b43230SLouis Dionne assert(PathEq(LHS, E)); 220f7b43230SLouis Dionne assert(&Ref == &LHS); 221f7b43230SLouis Dionne } 222f7b43230SLouis Dionne { 223f7b43230SLouis Dionne path LHS(L); 224f7b43230SLouis Dionne Ptr RHS(R); 225f7b43230SLouis Dionne path& Ref = LHS.append(RHS); 226f7b43230SLouis Dionne assert(PathEq(LHS, E)); 227f7b43230SLouis Dionne assert(&Ref == &LHS); 228f7b43230SLouis Dionne } 229f7b43230SLouis Dionne { 230f7b43230SLouis Dionne path LHS(L); 231f7b43230SLouis Dionne Ptr RHS(R); 232f7b43230SLouis Dionne path& Ref = LHS.append(RHS, StrEnd(RHS)); 233e557b6a6SLouis Dionne assert(PathEq(LHS, E)); 234f7b43230SLouis Dionne assert(&Ref == &LHS); 235f7b43230SLouis Dionne } 236f7b43230SLouis Dionne // iterators 237f7b43230SLouis Dionne { 238f7b43230SLouis Dionne path LHS(L); 239f7b43230SLouis Dionne InputIter RHS(R); 240f7b43230SLouis Dionne path& Ref = (LHS /= RHS); 241f7b43230SLouis Dionne assert(PathEq(LHS, E)); 242f7b43230SLouis Dionne assert(&Ref == &LHS); 243f7b43230SLouis Dionne } 244f7b43230SLouis Dionne { 245f7b43230SLouis Dionne path LHS(L); InputIter RHS(R); 246f7b43230SLouis Dionne path& Ref = LHS.append(RHS); 247f7b43230SLouis Dionne assert(PathEq(LHS, E)); 248f7b43230SLouis Dionne assert(&Ref == &LHS); 249f7b43230SLouis Dionne } 250f7b43230SLouis Dionne { 251f7b43230SLouis Dionne path LHS(L); 252f7b43230SLouis Dionne InputIter RHS(R); 253f7b43230SLouis Dionne InputIter REnd(StrEnd(R)); 254f7b43230SLouis Dionne path& Ref = LHS.append(RHS, REnd); 255f7b43230SLouis Dionne assert(PathEq(LHS, E)); 256f7b43230SLouis Dionne assert(&Ref == &LHS); 257f7b43230SLouis Dionne } 258f7b43230SLouis Dionne } 259f7b43230SLouis Dionne 260f7b43230SLouis Dionne 261f7b43230SLouis Dionne 262f7b43230SLouis Dionne template <class It, class = decltype(fs::path{}.append(std::declval<It>()))> 263f7b43230SLouis Dionne constexpr bool has_append(int) { return true; } 264f7b43230SLouis Dionne template <class It> 265f7b43230SLouis Dionne constexpr bool has_append(long) { return false; } 266f7b43230SLouis Dionne 267f7b43230SLouis Dionne template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))> 268f7b43230SLouis Dionne constexpr bool has_append_op(int) { return true; } 269f7b43230SLouis Dionne template <class It> 270f7b43230SLouis Dionne constexpr bool has_append_op(long) { return false; } 271f7b43230SLouis Dionne 272f7b43230SLouis Dionne template <class It> 273f7b43230SLouis Dionne constexpr bool has_append() { 274f7b43230SLouis Dionne static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same"); 275f7b43230SLouis Dionne return has_append<It>(0) && has_append_op<It>(0); 276f7b43230SLouis Dionne } 277f7b43230SLouis Dionne 278f7b43230SLouis Dionne void test_sfinae() 279f7b43230SLouis Dionne { 280f7b43230SLouis Dionne using namespace fs; 281f7b43230SLouis Dionne { 282f7b43230SLouis Dionne using It = const char* const; 283f7b43230SLouis Dionne static_assert(has_append<It>(), ""); 284f7b43230SLouis Dionne } 285f7b43230SLouis Dionne { 286f7b43230SLouis Dionne using It = input_iterator<const char*>; 287f7b43230SLouis Dionne static_assert(has_append<It>(), ""); 288f7b43230SLouis Dionne } 289f7b43230SLouis Dionne { 290f7b43230SLouis Dionne struct Traits { 291f7b43230SLouis Dionne using iterator_category = std::input_iterator_tag; 292f7b43230SLouis Dionne using value_type = const char; 293f7b43230SLouis Dionne using pointer = const char*; 294f7b43230SLouis Dionne using reference = const char&; 295f7b43230SLouis Dionne using difference_type = std::ptrdiff_t; 296f7b43230SLouis Dionne }; 297f7b43230SLouis Dionne using It = input_iterator<const char*, Traits>; 298f7b43230SLouis Dionne static_assert(has_append<It>(), ""); 299f7b43230SLouis Dionne } 300f7b43230SLouis Dionne { 301f7b43230SLouis Dionne using It = output_iterator<const char*>; 302f7b43230SLouis Dionne static_assert(!has_append<It>(), ""); 303f7b43230SLouis Dionne 304f7b43230SLouis Dionne } 305f7b43230SLouis Dionne { 306f7b43230SLouis Dionne static_assert(!has_append<int*>(), ""); 307f7b43230SLouis Dionne } 308f7b43230SLouis Dionne { 309f7b43230SLouis Dionne static_assert(!has_append<char>(), ""); 310f7b43230SLouis Dionne static_assert(!has_append<const char>(), ""); 311f7b43230SLouis Dionne } 312f7b43230SLouis Dionne } 313f7b43230SLouis Dionne 314f7b43230SLouis Dionne int main(int, char**) 315f7b43230SLouis Dionne { 316f7b43230SLouis Dionne using namespace fs; 317f7b43230SLouis Dionne for (auto const & TC : Cases) { 318f7b43230SLouis Dionne { 319f7b43230SLouis Dionne const char* LHS_In = TC.lhs; 320f7b43230SLouis Dionne const char* RHS_In = TC.rhs; 321f7b43230SLouis Dionne path LHS(LHS_In); 322f7b43230SLouis Dionne path RHS(RHS_In); 323f7b43230SLouis Dionne path& Res = (LHS /= RHS); 324e557b6a6SLouis Dionne assert(PathEq(Res, (const char*)TC.expect)); 325f7b43230SLouis Dionne assert(&Res == &LHS); 326f7b43230SLouis Dionne } 327f7b43230SLouis Dionne doAppendSourceTest<char> (TC); 328f7b43230SLouis Dionne doAppendSourceTest<wchar_t> (TC); 329f7b43230SLouis Dionne doAppendSourceTest<char16_t>(TC); 330f7b43230SLouis Dionne doAppendSourceTest<char32_t>(TC); 331f7b43230SLouis Dionne } 332f7b43230SLouis Dionne for (auto const & TC : LongLHSCases) { 333afe40b30SMartin Storsjö LIBCPP_ONLY(doAppendSourceAllocTest<char>(TC)); 334afe40b30SMartin Storsjö LIBCPP_ONLY(doAppendSourceAllocTest<wchar_t>(TC)); 335f7b43230SLouis Dionne } 336f7b43230SLouis Dionne test_sfinae(); 337f7b43230SLouis Dionne 338f7b43230SLouis Dionne return 0; 339f7b43230SLouis Dionne } 340