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