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 9*31cbe0f2SLouis Dionne // UNSUPPORTED: c++03 10f7b43230SLouis Dionne 11f7b43230SLouis Dionne // <filesystem> 12f7b43230SLouis Dionne 13f7b43230SLouis Dionne // class path 14f7b43230SLouis Dionne 15f7b43230SLouis Dionne // path& operator/=(path const&) 16f7b43230SLouis Dionne // template <class Source> 17f7b43230SLouis Dionne // path& operator/=(Source const&); 18f7b43230SLouis Dionne // template <class Source> 19f7b43230SLouis Dionne // path& append(Source const&); 20f7b43230SLouis Dionne // template <class InputIterator> 21f7b43230SLouis Dionne // path& append(InputIterator first, InputIterator last); 22f7b43230SLouis Dionne 23f7b43230SLouis Dionne 24cc89063bSNico Weber #include "filesystem_include.h" 25f7b43230SLouis Dionne #include <type_traits> 26f7b43230SLouis Dionne #include <string_view> 27f7b43230SLouis Dionne #include <cassert> 28f7b43230SLouis Dionne 29f7b43230SLouis Dionne #include "test_macros.h" 30f7b43230SLouis Dionne #include "test_iterators.h" 31cc89063bSNico Weber #include "count_new.h" 32cc89063bSNico Weber #include "filesystem_test_helper.h" 33f7b43230SLouis Dionne #include "verbose_assert.h" 34f7b43230SLouis Dionne 35f7b43230SLouis Dionne 36f7b43230SLouis Dionne struct AppendOperatorTestcase { 37f7b43230SLouis Dionne MultiStringType lhs; 38f7b43230SLouis Dionne MultiStringType rhs; 39f7b43230SLouis Dionne MultiStringType expect; 40f7b43230SLouis Dionne }; 41f7b43230SLouis Dionne 42f7b43230SLouis Dionne #define S(Str) MKSTR(Str) 43f7b43230SLouis Dionne const AppendOperatorTestcase Cases[] = 44f7b43230SLouis Dionne { 45f7b43230SLouis Dionne {S(""), S(""), S("")} 46f7b43230SLouis Dionne , {S("p1"), S("p2"), S("p1/p2")} 47f7b43230SLouis Dionne , {S("p1/"), S("p2"), S("p1/p2")} 48f7b43230SLouis Dionne , {S("p1"), S("/p2"), S("/p2")} 49f7b43230SLouis Dionne , {S("p1/"), S("/p2"), S("/p2")} 50f7b43230SLouis Dionne , {S("p1"), S("\\p2"), S("p1/\\p2")} 51f7b43230SLouis Dionne , {S("p1\\"), S("p2"), S("p1\\/p2")} 52f7b43230SLouis Dionne , {S("p1\\"), S("\\p2"), S("p1\\/\\p2")} 53f7b43230SLouis Dionne , {S(""), S("p2"), S("p2")} 54f7b43230SLouis Dionne , {S("/p1"), S("p2"), S("/p1/p2")} 55f7b43230SLouis Dionne , {S("/p1"), S("/p2"), S("/p2")} 56f7b43230SLouis Dionne , {S("/p1/p3"), S("p2"), S("/p1/p3/p2")} 57f7b43230SLouis Dionne , {S("/p1/p3/"), S("p2"), S("/p1/p3/p2")} 58f7b43230SLouis Dionne , {S("/p1/"), S("p2"), S("/p1/p2")} 59f7b43230SLouis Dionne , {S("/p1/p3/"), S("/p2/p4"), S("/p2/p4")} 60f7b43230SLouis Dionne , {S("/"), S(""), S("/")} 61f7b43230SLouis Dionne , {S("/p1"), S("/p2/"), S("/p2/")} 62f7b43230SLouis Dionne , {S("p1"), S(""), S("p1/")} 63f7b43230SLouis Dionne , {S("p1/"), S(""), S("p1/")} 64f7b43230SLouis Dionne }; 65f7b43230SLouis Dionne 66f7b43230SLouis Dionne 67f7b43230SLouis Dionne const AppendOperatorTestcase LongLHSCases[] = 68f7b43230SLouis Dionne { 69f7b43230SLouis Dionne {S("p1"), S("p2"), S("p1/p2")} 70f7b43230SLouis Dionne , {S("p1/"), S("p2"), S("p1/p2")} 71f7b43230SLouis Dionne , {S("p1"), S("/p2"), S("/p2")} 72f7b43230SLouis Dionne , {S("/p1"), S("p2"), S("/p1/p2")} 73f7b43230SLouis Dionne }; 74f7b43230SLouis Dionne #undef S 75f7b43230SLouis Dionne 76f7b43230SLouis Dionne 77f7b43230SLouis Dionne // The append operator may need to allocate a temporary buffer before a code_cvt 78f7b43230SLouis Dionne // conversion. Test if this allocation occurs by: 79f7b43230SLouis Dionne // 1. Create a path, `LHS`, and reserve enough space to append `RHS`. 80f7b43230SLouis Dionne // This prevents `LHS` from allocating during the actual appending. 81f7b43230SLouis Dionne // 2. Create a `Source` object `RHS`, which represents a "large" string. 82f7b43230SLouis Dionne // (The string must not trigger the SSO) 83f7b43230SLouis Dionne // 3. Append `RHS` to `LHS` and check for the expected allocation behavior. 84f7b43230SLouis Dionne template <class CharT> 85f7b43230SLouis Dionne void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) 86f7b43230SLouis Dionne { 87f7b43230SLouis Dionne using namespace fs; 88f7b43230SLouis Dionne using Ptr = CharT const*; 89f7b43230SLouis Dionne using Str = std::basic_string<CharT>; 90f7b43230SLouis Dionne using StrView = std::basic_string_view<CharT>; 91f7b43230SLouis Dionne using InputIter = input_iterator<Ptr>; 92f7b43230SLouis Dionne 93f7b43230SLouis Dionne const Ptr L = TC.lhs; 94f7b43230SLouis Dionne Str RShort = (Ptr)TC.rhs; 95f7b43230SLouis Dionne Str EShort = (Ptr)TC.expect; 96f7b43230SLouis Dionne assert(RShort.size() >= 2); 97f7b43230SLouis Dionne CharT c = RShort.back(); 98f7b43230SLouis Dionne RShort.append(100, c); 99f7b43230SLouis Dionne EShort.append(100, c); 100f7b43230SLouis Dionne const Ptr R = RShort.data(); 101f7b43230SLouis Dionne const Str& E = EShort; 102f7b43230SLouis Dionne std::size_t ReserveSize = E.size() + 3; 103f7b43230SLouis Dionne // basic_string 104f7b43230SLouis Dionne { 105f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 106f7b43230SLouis Dionne Str RHS(R); 107f7b43230SLouis Dionne { 108f7b43230SLouis Dionne DisableAllocationGuard g; 109f7b43230SLouis Dionne LHS /= RHS; 110f7b43230SLouis Dionne } 111f7b43230SLouis Dionne ASSERT_PRED(PathEq, LHS , E); 112f7b43230SLouis Dionne } 113f7b43230SLouis Dionne // basic_string_view 114f7b43230SLouis Dionne { 115f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 116f7b43230SLouis Dionne StrView RHS(R); 117f7b43230SLouis Dionne { 118f7b43230SLouis Dionne DisableAllocationGuard g; 119f7b43230SLouis Dionne LHS /= RHS; 120f7b43230SLouis Dionne } 121f7b43230SLouis Dionne assert(PathEq(LHS, E)); 122f7b43230SLouis Dionne } 123f7b43230SLouis Dionne // CharT* 124f7b43230SLouis Dionne { 125f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 126f7b43230SLouis Dionne Ptr RHS(R); 127f7b43230SLouis Dionne { 128f7b43230SLouis Dionne DisableAllocationGuard g; 129f7b43230SLouis Dionne LHS /= RHS; 130f7b43230SLouis Dionne } 131f7b43230SLouis Dionne assert(PathEq(LHS, E)); 132f7b43230SLouis Dionne } 133f7b43230SLouis Dionne { 134f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 135f7b43230SLouis Dionne Ptr RHS(R); 136f7b43230SLouis Dionne { 137f7b43230SLouis Dionne DisableAllocationGuard g; 138f7b43230SLouis Dionne LHS.append(RHS, StrEnd(RHS)); 139f7b43230SLouis Dionne } 140f7b43230SLouis Dionne assert(PathEq(LHS, E)); 141f7b43230SLouis Dionne } 142f7b43230SLouis Dionne // input iterator - For non-native char types, appends needs to copy the 143f7b43230SLouis Dionne // iterator range into a contiguous block of memory before it can perform the 144f7b43230SLouis Dionne // code_cvt conversions. 145f7b43230SLouis Dionne // For "char" no allocations will be performed because no conversion is 146f7b43230SLouis Dionne // required. 147f7b43230SLouis Dionne bool DisableAllocations = std::is_same<CharT, char>::value; 148f7b43230SLouis Dionne { 149f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 150f7b43230SLouis Dionne InputIter RHS(R); 151f7b43230SLouis Dionne { 152f7b43230SLouis Dionne RequireAllocationGuard g; // requires 1 or more allocations occur by default 153f7b43230SLouis Dionne if (DisableAllocations) g.requireExactly(0); 154f7b43230SLouis Dionne LHS /= RHS; 155f7b43230SLouis Dionne } 156f7b43230SLouis Dionne assert(PathEq(LHS, E)); 157f7b43230SLouis Dionne } 158f7b43230SLouis Dionne { 159f7b43230SLouis Dionne path LHS(L); PathReserve(LHS, ReserveSize); 160f7b43230SLouis Dionne InputIter RHS(R); 161f7b43230SLouis Dionne InputIter REnd(StrEnd(R)); 162f7b43230SLouis Dionne { 163f7b43230SLouis Dionne RequireAllocationGuard g; 164f7b43230SLouis Dionne if (DisableAllocations) g.requireExactly(0); 165f7b43230SLouis Dionne LHS.append(RHS, REnd); 166f7b43230SLouis Dionne } 167f7b43230SLouis Dionne assert(PathEq(LHS, E)); 168f7b43230SLouis Dionne } 169f7b43230SLouis Dionne } 170f7b43230SLouis Dionne 171f7b43230SLouis Dionne template <class CharT> 172f7b43230SLouis Dionne void doAppendSourceTest(AppendOperatorTestcase const& TC) 173f7b43230SLouis Dionne { 174f7b43230SLouis Dionne using namespace fs; 175f7b43230SLouis Dionne using Ptr = CharT const*; 176f7b43230SLouis Dionne using Str = std::basic_string<CharT>; 177f7b43230SLouis Dionne using StrView = std::basic_string_view<CharT>; 178f7b43230SLouis Dionne using InputIter = input_iterator<Ptr>; 179f7b43230SLouis Dionne const Ptr L = TC.lhs; 180f7b43230SLouis Dionne const Ptr R = TC.rhs; 181f7b43230SLouis Dionne const Ptr E = TC.expect; 182f7b43230SLouis Dionne // basic_string 183f7b43230SLouis Dionne { 184f7b43230SLouis Dionne path Result(L); 185f7b43230SLouis Dionne Str RHS(R); 186f7b43230SLouis Dionne path& Ref = (Result /= RHS); 187f7b43230SLouis Dionne ASSERT_EQ(Result, E) 188f7b43230SLouis Dionne << DISPLAY(L) << DISPLAY(R); 189f7b43230SLouis Dionne assert(&Ref == &Result); 190f7b43230SLouis Dionne } 191f7b43230SLouis Dionne { 192f7b43230SLouis Dionne path LHS(L); 193f7b43230SLouis Dionne Str RHS(R); 194f7b43230SLouis Dionne path& Ref = LHS.append(RHS); 195f7b43230SLouis Dionne assert(PathEq(LHS, E)); 196f7b43230SLouis Dionne assert(&Ref == &LHS); 197f7b43230SLouis Dionne } 198f7b43230SLouis Dionne // basic_string_view 199f7b43230SLouis Dionne { 200f7b43230SLouis Dionne path LHS(L); 201f7b43230SLouis Dionne StrView RHS(R); 202f7b43230SLouis Dionne path& Ref = (LHS /= RHS); 203f7b43230SLouis Dionne assert(PathEq(LHS, E)); 204f7b43230SLouis Dionne assert(&Ref == &LHS); 205f7b43230SLouis Dionne } 206f7b43230SLouis Dionne { 207f7b43230SLouis Dionne path LHS(L); 208f7b43230SLouis Dionne StrView RHS(R); 209f7b43230SLouis Dionne path& Ref = LHS.append(RHS); 210f7b43230SLouis Dionne assert(PathEq(LHS, E)); 211f7b43230SLouis Dionne assert(&Ref == &LHS); 212f7b43230SLouis Dionne } 213f7b43230SLouis Dionne // Char* 214f7b43230SLouis Dionne { 215f7b43230SLouis Dionne path LHS(L); 216f7b43230SLouis Dionne Str RHS(R); 217f7b43230SLouis Dionne path& Ref = (LHS /= RHS); 218f7b43230SLouis Dionne assert(PathEq(LHS, E)); 219f7b43230SLouis Dionne assert(&Ref == &LHS); 220f7b43230SLouis Dionne } 221f7b43230SLouis Dionne { 222f7b43230SLouis Dionne path LHS(L); 223f7b43230SLouis Dionne Ptr RHS(R); 224f7b43230SLouis Dionne path& Ref = LHS.append(RHS); 225f7b43230SLouis Dionne assert(PathEq(LHS, E)); 226f7b43230SLouis Dionne assert(&Ref == &LHS); 227f7b43230SLouis Dionne } 228f7b43230SLouis Dionne { 229f7b43230SLouis Dionne path LHS(L); 230f7b43230SLouis Dionne Ptr RHS(R); 231f7b43230SLouis Dionne path& Ref = LHS.append(RHS, StrEnd(RHS)); 232f7b43230SLouis Dionne ASSERT_PRED(PathEq, LHS, E) 233f7b43230SLouis Dionne << DISPLAY(L) << DISPLAY(R); 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); 324f7b43230SLouis Dionne ASSERT_PRED(PathEq, Res, (const char*)TC.expect) 325f7b43230SLouis Dionne << DISPLAY(LHS_In) << DISPLAY(RHS_In); 326f7b43230SLouis Dionne assert(&Res == &LHS); 327f7b43230SLouis Dionne } 328f7b43230SLouis Dionne doAppendSourceTest<char> (TC); 329f7b43230SLouis Dionne doAppendSourceTest<wchar_t> (TC); 330f7b43230SLouis Dionne doAppendSourceTest<char16_t>(TC); 331f7b43230SLouis Dionne doAppendSourceTest<char32_t>(TC); 332f7b43230SLouis Dionne } 333f7b43230SLouis Dionne for (auto const & TC : LongLHSCases) { 334f7b43230SLouis Dionne doAppendSourceAllocTest<char>(TC); 335f7b43230SLouis Dionne doAppendSourceAllocTest<wchar_t>(TC); 336f7b43230SLouis Dionne } 337f7b43230SLouis Dionne test_sfinae(); 338f7b43230SLouis Dionne 339f7b43230SLouis Dionne return 0; 340f7b43230SLouis Dionne } 341