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