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