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