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