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   {
179     path LHS(L); PathReserve(LHS, ReserveSize);
180     path RHS(R);
181     {
182       DisableAllocationGuard g;
183       LHS /= RHS;
184     }
185     assert(PathEq(LHS, E));
186   }
187   // input iterator - For non-native char types, appends needs to copy the
188   // iterator range into a contiguous block of memory before it can perform the
189   // code_cvt conversions.
190   // For "char" no allocations will be performed because no conversion is
191   // required.
192   bool DisableAllocations = std::is_same<CharT, char>::value;
193   {
194     path LHS(L); PathReserve(LHS, ReserveSize);
195     InputIter RHS(R);
196     {
197       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
198       if (DisableAllocations) g.requireExactly(0);
199       LHS /= RHS;
200     }
201     assert(PathEq(LHS, E));
202   }
203   {
204     path LHS(L); PathReserve(LHS, ReserveSize);
205     InputIter RHS(R);
206     InputIter REnd(StrEnd(R));
207     {
208       RequireAllocationGuard g;
209       if (DisableAllocations) g.requireExactly(0);
210       LHS.append(RHS, REnd);
211     }
212     assert(PathEq(LHS, E));
213   }
214 }
215 
216 template <class CharT>
217 void doAppendSourceTest(AppendOperatorTestcase const& TC)
218 {
219   using namespace fs;
220   using Ptr = CharT const*;
221   using Str = std::basic_string<CharT>;
222   using StrView = std::basic_string_view<CharT>;
223   using InputIter = input_iterator<Ptr>;
224   const Ptr L = TC.lhs;
225   const Ptr R = TC.rhs;
226   const Ptr E = TC.expected_result();
227   // basic_string
228   {
229     path Result(L);
230     Str RHS(R);
231     path& Ref = (Result /= RHS);
232     assert(Result == E);
233     assert(&Ref == &Result);
234   }
235   {
236     path LHS(L);
237     Str RHS(R);
238     path& Ref = LHS.append(RHS);
239     assert(PathEq(LHS, E));
240     assert(&Ref == &LHS);
241   }
242   // basic_string_view
243   {
244     path LHS(L);
245     StrView RHS(R);
246     path& Ref = (LHS /= RHS);
247     assert(PathEq(LHS, E));
248     assert(&Ref == &LHS);
249   }
250   {
251     path LHS(L);
252     StrView RHS(R);
253     path& Ref = LHS.append(RHS);
254     assert(PathEq(LHS, E));
255     assert(&Ref == &LHS);
256   }
257   // Char*
258   {
259     path LHS(L);
260     Str RHS(R);
261     path& Ref = (LHS /= RHS);
262     assert(PathEq(LHS, E));
263     assert(&Ref == &LHS);
264   }
265   {
266     path LHS(L);
267     Ptr RHS(R);
268     path& Ref = LHS.append(RHS);
269     assert(PathEq(LHS, E));
270     assert(&Ref == &LHS);
271   }
272   {
273     path LHS(L);
274     Ptr RHS(R);
275     path& Ref = LHS.append(RHS, StrEnd(RHS));
276     assert(PathEq(LHS, E));
277     assert(&Ref == &LHS);
278   }
279   // iterators
280   {
281     path LHS(L);
282     InputIter RHS(R);
283     path& Ref = (LHS /= RHS);
284     assert(PathEq(LHS, E));
285     assert(&Ref == &LHS);
286   }
287   {
288     path LHS(L); InputIter RHS(R);
289     path& Ref = LHS.append(RHS);
290     assert(PathEq(LHS, E));
291     assert(&Ref == &LHS);
292   }
293   {
294     path LHS(L);
295     InputIter RHS(R);
296     InputIter REnd(StrEnd(R));
297     path& Ref = LHS.append(RHS, REnd);
298     assert(PathEq(LHS, E));
299     assert(&Ref == &LHS);
300   }
301 }
302 
303 
304 
305 template <class It, class = decltype(fs::path{}.append(std::declval<It>()))>
306 constexpr bool has_append(int) { return true; }
307 template <class It>
308 constexpr bool has_append(long) { return false; }
309 
310 template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))>
311 constexpr bool has_append_op(int) { return true; }
312 template <class It>
313 constexpr bool has_append_op(long) { return false; }
314 
315 template <class It>
316 constexpr bool has_append() {
317   static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same");
318   return has_append<It>(0) && has_append_op<It>(0);
319 }
320 
321 void test_sfinae()
322 {
323   using namespace fs;
324   {
325     using It = const char* const;
326     static_assert(has_append<It>(), "");
327   }
328   {
329     using It = input_iterator<const char*>;
330     static_assert(has_append<It>(), "");
331   }
332   {
333     struct Traits {
334       using iterator_category = std::input_iterator_tag;
335       using value_type = const char;
336       using pointer = const char*;
337       using reference = const char&;
338       using difference_type = std::ptrdiff_t;
339     };
340     using It = input_iterator<const char*, Traits>;
341     static_assert(has_append<It>(), "");
342   }
343   {
344     using It = output_iterator<const char*>;
345     static_assert(!has_append<It>(), "");
346 
347   }
348   {
349     static_assert(!has_append<int*>(), "");
350   }
351   {
352     static_assert(!has_append<char>(), "");
353     static_assert(!has_append<const char>(), "");
354   }
355 }
356 
357 int main(int, char**)
358 {
359   using namespace fs;
360   for (auto const & TC : Cases) {
361     {
362       const char* LHS_In = TC.lhs;
363       const char* RHS_In = TC.rhs;
364       path LHS(LHS_In);
365       path RHS(RHS_In);
366       path& Res = (LHS /= RHS);
367       assert(PathEq(Res, (const char*)TC.expected_result()));
368       assert(&Res == &LHS);
369     }
370     doAppendSourceTest<char>    (TC);
371     doAppendSourceTest<wchar_t> (TC);
372     doAppendSourceTest<char16_t>(TC);
373     doAppendSourceTest<char32_t>(TC);
374   }
375   for (auto const & TC : LongLHSCases) {
376     (void)TC;
377     LIBCPP_ONLY(doAppendSourceAllocTest<char>(TC));
378     LIBCPP_ONLY(doAppendSourceAllocTest<wchar_t>(TC));
379   }
380   test_sfinae();
381 
382   return 0;
383 }
384