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