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>
doConcatSourceAllocTest(ConcatOperatorTestcase const & TC)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   // In DLL builds on Windows, the overridden operator new won't pick up
145   // allocations done within the DLL, so the RequireAllocationGuard below
146   // won't necessarily see allocations in the cases where they're expected.
147   bool DisableAllocations = std::is_same<CharT, path::value_type>::value;
148   {
149     path LHS(L); PathReserve(LHS, ReserveSize);
150     InputIter RHS(R);
151     {
152       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
153       if (DisableAllocations) g.requireExactly(0);
154       else TEST_ONLY_WIN32_DLL(g.requireAtLeast(0));
155       LHS += RHS;
156     }
157     assert(LHS == E);
158   }
159   {
160     path LHS(L); PathReserve(LHS, ReserveSize);
161     InputIter RHS(R);
162     InputIter REnd(StrEnd(R));
163     {
164       RequireAllocationGuard g;
165       if (DisableAllocations) g.requireExactly(0);
166       else TEST_ONLY_WIN32_DLL(g.requireAtLeast(0));
167       LHS.concat(RHS, REnd);
168     }
169     assert(LHS == E);
170   }
171 }
172 
173 template <class CharT>
doConcatSourceTest(ConcatOperatorTestcase const & TC)174 void doConcatSourceTest(ConcatOperatorTestcase const& TC)
175 {
176   using namespace fs;
177   using Ptr = CharT const*;
178   using Str = std::basic_string<CharT>;
179   using StrView = std::basic_string_view<CharT>;
180   using InputIter = cpp17_input_iterator<Ptr>;
181   const Ptr L = TC.lhs;
182   const Ptr R = TC.rhs;
183   const Ptr E = TC.expect;
184   // basic_string
185   {
186     path LHS(L);
187     Str RHS(R);
188     path& Ref = (LHS += RHS);
189     assert(LHS == E);
190     assert(&Ref == &LHS);
191   }
192   {
193     path LHS(L);
194     Str RHS(R);
195     path& Ref = LHS.concat(RHS);
196     assert(LHS == E);
197     assert(&Ref == &LHS);
198   }
199   // basic_string_view
200   {
201     path LHS(L);
202     StrView RHS(R);
203     path& Ref = (LHS += RHS);
204     assert(LHS == E);
205     assert(&Ref == &LHS);
206   }
207   {
208     path LHS(L);
209     StrView RHS(R);
210     path& Ref = LHS.concat(RHS);
211     assert(LHS == E);
212     assert(&Ref == &LHS);
213   }
214   // Char*
215   {
216     path LHS(L);
217     Str RHS(R);
218     path& Ref = (LHS += RHS);
219     assert(LHS == E);
220     assert(&Ref == &LHS);
221   }
222   {
223     path LHS(L);
224     Ptr RHS(R);
225     path& Ref = LHS.concat(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, StrEnd(RHS));
233     assert(LHS == E);
234     assert(&Ref == &LHS);
235   }
236   // iterators
237   {
238     path LHS(L);
239     InputIter RHS(R);
240     path& Ref = (LHS += RHS);
241     assert(LHS == E);
242     assert(&Ref == &LHS);
243   }
244   {
245     path LHS(L); InputIter RHS(R);
246     path& Ref = LHS.concat(RHS);
247     assert(LHS == E);
248     assert(&Ref == &LHS);
249   }
250   {
251     path LHS(L);
252     InputIter RHS(R);
253     InputIter REnd(StrEnd(R));
254     path& Ref = LHS.concat(RHS, REnd);
255     assert(LHS == E);
256     assert(&Ref == &LHS);
257   }
258 }
259 
260 template <class CharT>
doConcatECharTest(ConcatOperatorTestcase const & TC)261 void doConcatECharTest(ConcatOperatorTestcase const& TC)
262 {
263   using namespace fs;
264   using Ptr = CharT const*;
265   const Ptr RStr = TC.rhs;
266   assert(StrLen(RStr) == 1);
267   const Ptr L   = TC.lhs;
268   const CharT R = RStr[0];
269   const Ptr E   = TC.expect;
270   {
271     path LHS(L);
272     path& Ref = (LHS += R);
273     assert(LHS == E);
274     assert(&Ref == &LHS);
275   }
276 }
277 
278 
279 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>
has_concat(int)280 constexpr bool has_concat(int) { return true; }
281 template <class It>
has_concat(long)282 constexpr bool has_concat(long) { return false; }
283 
284 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>
has_concat_op(int)285 constexpr bool has_concat_op(int) { return true; }
286 template <class It>
has_concat_op(long)287 constexpr bool has_concat_op(long) { return false; }
288 template <class It>
has_concat_op()289 constexpr bool has_concat_op() { return has_concat_op<It>(0); }
290 
291 template <class It>
has_concat()292 constexpr bool has_concat() {
293   static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same");
294   return has_concat<It>(0) && has_concat_op<It>(0);
295 }
296 
test_sfinae()297 void test_sfinae() {
298   using namespace fs;
299   {
300     static_assert(has_concat_op<char>(), "");
301     static_assert(has_concat_op<const char>(), "");
302     static_assert(has_concat_op<char16_t>(), "");
303     static_assert(has_concat_op<const char16_t>(), "");
304   }
305   {
306     using It = const char* const;
307     static_assert(has_concat<It>(), "");
308   }
309   {
310     using It = cpp17_input_iterator<const char*>;
311     static_assert(has_concat<It>(), "");
312   }
313   {
314     struct Traits {
315       using iterator_category = std::input_iterator_tag;
316       using value_type = const char;
317       using pointer = const char*;
318       using reference = const char&;
319       using difference_type = std::ptrdiff_t;
320     };
321     using It = cpp17_input_iterator<const char*, Traits>;
322     static_assert(has_concat<It>(), "");
323   }
324   {
325     using It = output_iterator<const char*>;
326     static_assert(!has_concat<It>(), "");
327   }
328   {
329     static_assert(!has_concat<int>(0), "");
330     // operator+=(int) is well formed since it converts to operator+=(value_type)
331     // but concat(int) isn't valid because there is no concat(value_type).
332     // This should probably be addressed by a LWG issue.
333     static_assert(has_concat_op<int>(), "");
334   }
335   {
336     static_assert(!has_concat<int*>(), "");
337   }
338 }
339 
main(int,char **)340 int main(int, char**)
341 {
342   using namespace fs;
343   for (auto const & TC : Cases) {
344     {
345       path LHS((const char*)TC.lhs);
346       path RHS((const char*)TC.rhs);
347       path& Ref = (LHS += RHS);
348       assert(LHS == (const char*)TC.expect);
349       assert(&Ref == &LHS);
350     }
351     {
352       path LHS((const char*)TC.lhs);
353       std::basic_string_view<path::value_type> RHS((const path::value_type*)TC.rhs);
354       path& Ref = (LHS += RHS);
355       assert(LHS == (const char*)TC.expect);
356       assert(&Ref == &LHS);
357     }
358     doConcatSourceTest<char>    (TC);
359     doConcatSourceTest<wchar_t> (TC);
360     doConcatSourceTest<char16_t>(TC);
361     doConcatSourceTest<char32_t>(TC);
362   }
363   for (auto const & TC : LongLHSCases) {
364     // Do path test
365     {
366       path LHS((const char*)TC.lhs);
367       path 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     {
378       path LHS((const char*)TC.lhs);
379       std::basic_string_view<path::value_type> RHS((const path::value_type*)TC.rhs);
380       const char* E = TC.expect;
381       PathReserve(LHS, StrLen(E) + 5);
382       {
383         LIBCPP_ONLY(DisableAllocationGuard g);
384         path& Ref = (LHS += RHS);
385         assert(&Ref == &LHS);
386       }
387       assert(LHS == E);
388     }
389     LIBCPP_ONLY(doConcatSourceAllocTest<char>(TC));
390     LIBCPP_ONLY(doConcatSourceAllocTest<wchar_t>(TC));
391   }
392   for (auto const& TC : CharTestCases) {
393     doConcatECharTest<char>(TC);
394     doConcatECharTest<wchar_t>(TC);
395     doConcatECharTest<char16_t>(TC);
396     doConcatECharTest<char32_t>(TC);
397   }
398   test_sfinae();
399 
400   return 0;
401 }
402