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