xref: /llvm-project/libcxx/test/std/input.output/filesystems/class.path/path.member/path.concat.pass.cpp (revision afe40b305d2292bce5b2694220697c6e6bdeed65)
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
10 
11 // <filesystem>
12 
13 // class path
14 
15 // path& operator+=(const path& x);
16 // path& operator+=(const string_type& x);
17 // path& operator+=(string_view x);
18 // path& operator+=(const value_type* x);
19 // path& operator+=(value_type x);
20 // template <class Source>
21 //   path& operator+=(const Source& x);
22 // template <class EcharT>
23 //   path& operator+=(EcharT x);
24 // template <class Source>
25 //   path& concat(const Source& x);
26 // template <class InputIterator>
27 //   path& concat(InputIterator first, InputIterator last);
28 
29 
30 #include "filesystem_include.h"
31 #include <type_traits>
32 #include <string>
33 #include <string_view>
34 #include <cassert>
35 
36 #include "test_macros.h"
37 #include "test_iterators.h"
38 #include "count_new.h"
39 #include "filesystem_test_helper.h"
40 
41 
42 struct ConcatOperatorTestcase {
43   MultiStringType lhs;
44   MultiStringType rhs;
45   MultiStringType expect;
46 };
47 
48 #define LONGSTR "LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR"
49 #define S(Str) MKSTR(Str)
50 const ConcatOperatorTestcase Cases[] =
51     {
52         {S(""),         S(""),                  S("")}
53       , {S("p1"),       S("p2"),                S("p1p2")}
54       , {S("p1/"),      S("/p2"),               S("p1//p2")}
55       , {S(""),         S("\\foo/bar/baz"),     S("\\foo/bar/baz")}
56       , {S("c:\\foo"),  S(""),                  S("c:\\foo")}
57       , {S(LONGSTR),    S("foo"),               S(LONGSTR "foo")}
58       , {S("abcdefghijklmnopqrstuvwxyz/\\"), S("/\\123456789"), S("abcdefghijklmnopqrstuvwxyz/\\/\\123456789")}
59     };
60 const ConcatOperatorTestcase LongLHSCases[] =
61     {
62         {S(""),        S(LONGSTR),     S(LONGSTR)}
63       , {S("p1/"),     S(LONGSTR),      S("p1/" LONGSTR)}
64     };
65 const ConcatOperatorTestcase CharTestCases[] =
66     {
67         {S(""),       S("P"), S("P")}
68       , {S("/fooba"), S("r"), S("/foobar")}
69     };
70 #undef S
71 #undef LONGSTR
72 
73 // The concat operator may need to allocate a temporary buffer before a code_cvt
74 // conversion. Test if this allocation occurs by:
75 //   1. Create a path, `LHS`, and reserve enough space to append `RHS`.
76 //      This prevents `LHS` from allocating during the actual appending.
77 //   2. Create a `Source` object `RHS`, which represents a "large" string.
78 //      (The string must not trigger the SSO)
79 //   3. Concat `RHS` to `LHS` and check for the expected allocation behavior.
80 template <class CharT>
81 void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC)
82 {
83   using namespace fs;
84   using Ptr = CharT const*;
85   using Str = std::basic_string<CharT>;
86   using StrView = std::basic_string_view<CharT>;
87   using InputIter = input_iterator<Ptr>;
88 
89   const Ptr L = TC.lhs;
90   const Ptr R = TC.rhs;
91   const Ptr E =  TC.expect;
92   std::size_t ReserveSize = StrLen(E) + 1;
93   // basic_string
94   {
95     path LHS(L); PathReserve(LHS, ReserveSize);
96     Str  RHS(R);
97     {
98       DisableAllocationGuard g;
99       LHS += RHS;
100     }
101     assert(LHS == E);
102   }
103   // basic_string_view
104   {
105     path LHS(L); PathReserve(LHS, ReserveSize);
106     StrView  RHS(R);
107     {
108       DisableAllocationGuard g;
109       LHS += RHS;
110     }
111     assert(LHS == E);
112   }
113   // CharT*
114   {
115     path LHS(L); PathReserve(LHS, ReserveSize);
116     Ptr RHS(R);
117     {
118       DisableAllocationGuard g;
119       LHS += RHS;
120     }
121     assert(LHS == E);
122   }
123   {
124     path LHS(L); PathReserve(LHS, ReserveSize);
125     Ptr RHS(R);
126     {
127       DisableAllocationGuard g;
128       LHS.concat(RHS, StrEnd(RHS));
129     }
130     assert(LHS == E);
131   }
132   // input iterator - For non-native char types, appends needs to copy the
133   // iterator range into a contiguous block of memory before it can perform the
134   // code_cvt conversions.
135   // For "char" no allocations will be performed because no conversion is
136   // required.
137   bool DisableAllocations = std::is_same<CharT, char>::value;
138   {
139     path LHS(L); PathReserve(LHS, ReserveSize);
140     InputIter RHS(R);
141     {
142       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
143       if (DisableAllocations) g.requireExactly(0);
144       LHS += RHS;
145     }
146     assert(LHS == E);
147   }
148   {
149     path LHS(L); PathReserve(LHS, ReserveSize);
150     InputIter RHS(R);
151     InputIter REnd(StrEnd(R));
152     {
153       RequireAllocationGuard g;
154       if (DisableAllocations) g.requireExactly(0);
155       LHS.concat(RHS, REnd);
156     }
157     assert(LHS == E);
158   }
159 }
160 
161 template <class CharT>
162 void doConcatSourceTest(ConcatOperatorTestcase const& TC)
163 {
164   using namespace fs;
165   using Ptr = CharT const*;
166   using Str = std::basic_string<CharT>;
167   using StrView = std::basic_string_view<CharT>;
168   using InputIter = input_iterator<Ptr>;
169   const Ptr L = TC.lhs;
170   const Ptr R = TC.rhs;
171   const Ptr E = TC.expect;
172   // basic_string
173   {
174     path LHS(L);
175     Str RHS(R);
176     path& Ref = (LHS += RHS);
177     assert(LHS == E);
178     assert(&Ref == &LHS);
179   }
180   {
181     path LHS(L);
182     Str RHS(R);
183     path& Ref = LHS.concat(RHS);
184     assert(LHS == E);
185     assert(&Ref == &LHS);
186   }
187   // basic_string_view
188   {
189     path LHS(L);
190     StrView RHS(R);
191     path& Ref = (LHS += RHS);
192     assert(LHS == E);
193     assert(&Ref == &LHS);
194   }
195   {
196     path LHS(L);
197     StrView RHS(R);
198     path& Ref = LHS.concat(RHS);
199     assert(LHS == E);
200     assert(&Ref == &LHS);
201   }
202   // Char*
203   {
204     path LHS(L);
205     Str RHS(R);
206     path& Ref = (LHS += RHS);
207     assert(LHS == E);
208     assert(&Ref == &LHS);
209   }
210   {
211     path LHS(L);
212     Ptr RHS(R);
213     path& Ref = LHS.concat(RHS);
214     assert(LHS == E);
215     assert(&Ref == &LHS);
216   }
217   {
218     path LHS(L);
219     Ptr RHS(R);
220     path& Ref = LHS.concat(RHS, StrEnd(RHS));
221     assert(LHS == E);
222     assert(&Ref == &LHS);
223   }
224   // iterators
225   {
226     path LHS(L);
227     InputIter RHS(R);
228     path& Ref = (LHS += RHS);
229     assert(LHS == E);
230     assert(&Ref == &LHS);
231   }
232   {
233     path LHS(L); InputIter RHS(R);
234     path& Ref = LHS.concat(RHS);
235     assert(LHS == E);
236     assert(&Ref == &LHS);
237   }
238   {
239     path LHS(L);
240     InputIter RHS(R);
241     InputIter REnd(StrEnd(R));
242     path& Ref = LHS.concat(RHS, REnd);
243     assert(LHS == E);
244     assert(&Ref == &LHS);
245   }
246 }
247 
248 template <class CharT>
249 void doConcatECharTest(ConcatOperatorTestcase const& TC)
250 {
251   using namespace fs;
252   using Ptr = CharT const*;
253   const Ptr RStr = TC.rhs;
254   assert(StrLen(RStr) == 1);
255   const Ptr L   = TC.lhs;
256   const CharT R = RStr[0];
257   const Ptr E   = TC.expect;
258   {
259     path LHS(L);
260     path& Ref = (LHS += R);
261     assert(LHS == E);
262     assert(&Ref == &LHS);
263   }
264 }
265 
266 
267 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>
268 constexpr bool has_concat(int) { return true; }
269 template <class It>
270 constexpr bool has_concat(long) { return false; }
271 
272 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>
273 constexpr bool has_concat_op(int) { return true; }
274 template <class It>
275 constexpr bool has_concat_op(long) { return false; }
276 template <class It>
277 constexpr bool has_concat_op() { return has_concat_op<It>(0); }
278 
279 template <class It>
280 constexpr bool has_concat() {
281   static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same");
282   return has_concat<It>(0) && has_concat_op<It>(0);
283 }
284 
285 void test_sfinae() {
286   using namespace fs;
287   {
288     static_assert(has_concat_op<char>(), "");
289     static_assert(has_concat_op<const char>(), "");
290     static_assert(has_concat_op<char16_t>(), "");
291     static_assert(has_concat_op<const char16_t>(), "");
292   }
293   {
294     using It = const char* const;
295     static_assert(has_concat<It>(), "");
296   }
297   {
298     using It = input_iterator<const char*>;
299     static_assert(has_concat<It>(), "");
300   }
301   {
302     struct Traits {
303       using iterator_category = std::input_iterator_tag;
304       using value_type = const char;
305       using pointer = const char*;
306       using reference = const char&;
307       using difference_type = std::ptrdiff_t;
308     };
309     using It = input_iterator<const char*, Traits>;
310     static_assert(has_concat<It>(), "");
311   }
312   {
313     using It = output_iterator<const char*>;
314     static_assert(!has_concat<It>(), "");
315   }
316   {
317     static_assert(!has_concat<int>(0), "");
318     // operator+=(int) is well formed since it converts to operator+=(value_type)
319     // but concat(int) isn't valid because there is no concat(value_type).
320     // This should probably be addressed by a LWG issue.
321     static_assert(has_concat_op<int>(), "");
322   }
323   {
324     static_assert(!has_concat<int*>(), "");
325   }
326 }
327 
328 int main(int, char**)
329 {
330   using namespace fs;
331   for (auto const & TC : Cases) {
332     {
333       path LHS((const char*)TC.lhs);
334       path RHS((const char*)TC.rhs);
335       path& Ref = (LHS += RHS);
336       assert(LHS == (const char*)TC.expect);
337       assert(&Ref == &LHS);
338     }
339     {
340       path LHS((const char*)TC.lhs);
341       std::string_view RHS((const char*)TC.rhs);
342       path& Ref = (LHS += RHS);
343       assert(LHS == (const char*)TC.expect);
344       assert(&Ref == &LHS);
345     }
346     doConcatSourceTest<char>    (TC);
347     doConcatSourceTest<wchar_t> (TC);
348     doConcatSourceTest<char16_t>(TC);
349     doConcatSourceTest<char32_t>(TC);
350   }
351   for (auto const & TC : LongLHSCases) {
352     // Do path test
353     {
354       path LHS((const char*)TC.lhs);
355       path RHS((const char*)TC.rhs);
356       const char* E = TC.expect;
357       PathReserve(LHS, StrLen(E) + 5);
358       {
359         LIBCPP_ONLY(DisableAllocationGuard g);
360         path& Ref = (LHS += RHS);
361         assert(&Ref == &LHS);
362       }
363       assert(LHS == E);
364     }
365     {
366       path LHS((const char*)TC.lhs);
367       std::string_view RHS((const char*)TC.rhs);
368       const char* E = TC.expect;
369       PathReserve(LHS, StrLen(E) + 5);
370       {
371         LIBCPP_ONLY(DisableAllocationGuard g);
372         path& Ref = (LHS += RHS);
373         assert(&Ref == &LHS);
374       }
375       assert(LHS == E);
376     }
377     LIBCPP_ONLY(doConcatSourceAllocTest<char>(TC));
378     LIBCPP_ONLY(doConcatSourceAllocTest<wchar_t>(TC));
379   }
380   for (auto const& TC : CharTestCases) {
381     doConcatECharTest<char>(TC);
382     doConcatECharTest<wchar_t>(TC);
383     doConcatECharTest<char16_t>(TC);
384     doConcatECharTest<char32_t>(TC);
385   }
386   test_sfinae();
387 
388   return 0;
389 }
390