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 
68 
69 const AppendOperatorTestcase LongLHSCases[] =
70     {
71         {S("p1"),   S("p2"),    S("p1/p2")}
72       , {S("p1/"),  S("p2"),    S("p1/p2")}
73       , {S("p1"),   S("/p2"),   S("/p2")}
74       , {S("/p1"),  S("p2"),    S("/p1/p2")}
75     };
76 #undef S
77 
78 
79 // The append 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. Append `RHS` to `LHS` and check for the expected allocation behavior.
86 template <class CharT>
87 void doAppendSourceAllocTest(AppendOperatorTestcase 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 = input_iterator<Ptr>;
94 
95   const Ptr L = TC.lhs;
96   Str RShort = (Ptr)TC.rhs;
97   Str EShort = (Ptr)TC.expect;
98   assert(RShort.size() >= 2);
99   CharT c = RShort.back();
100   RShort.append(100, c);
101   EShort.append(100, c);
102   const Ptr R = RShort.data();
103   const Str& E = EShort;
104   std::size_t ReserveSize = E.size() + 3;
105   // basic_string
106   {
107     path LHS(L); PathReserve(LHS, ReserveSize);
108     Str  RHS(R);
109     {
110       DisableAllocationGuard g;
111       LHS /= RHS;
112     }
113     assert(PathEq(LHS, E));
114   }
115   // basic_string_view
116   {
117     path LHS(L); PathReserve(LHS, ReserveSize);
118     StrView  RHS(R);
119     {
120       DisableAllocationGuard g;
121       LHS /= RHS;
122     }
123     assert(PathEq(LHS, E));
124   }
125   // CharT*
126   {
127     path LHS(L); PathReserve(LHS, ReserveSize);
128     Ptr RHS(R);
129     {
130       DisableAllocationGuard g;
131       LHS /= RHS;
132     }
133     assert(PathEq(LHS, E));
134   }
135   {
136     path LHS(L); PathReserve(LHS, ReserveSize);
137     Ptr RHS(R);
138     {
139       DisableAllocationGuard g;
140       LHS.append(RHS, StrEnd(RHS));
141     }
142     assert(PathEq(LHS, E));
143   }
144   // input iterator - For non-native char types, appends needs to copy the
145   // iterator range into a contiguous block of memory before it can perform the
146   // code_cvt conversions.
147   // For "char" no allocations will be performed because no conversion is
148   // required.
149   bool DisableAllocations = std::is_same<CharT, char>::value;
150   {
151     path LHS(L); PathReserve(LHS, ReserveSize);
152     InputIter RHS(R);
153     {
154       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
155       if (DisableAllocations) g.requireExactly(0);
156       LHS /= RHS;
157     }
158     assert(PathEq(LHS, E));
159   }
160   {
161     path LHS(L); PathReserve(LHS, ReserveSize);
162     InputIter RHS(R);
163     InputIter REnd(StrEnd(R));
164     {
165       RequireAllocationGuard g;
166       if (DisableAllocations) g.requireExactly(0);
167       LHS.append(RHS, REnd);
168     }
169     assert(PathEq(LHS, E));
170   }
171 }
172 
173 template <class CharT>
174 void doAppendSourceTest(AppendOperatorTestcase 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 = 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 Result(L);
187     Str RHS(R);
188     path& Ref = (Result /= RHS);
189     assert(Result == E);
190     assert(&Ref == &Result);
191   }
192   {
193     path LHS(L);
194     Str RHS(R);
195     path& Ref = LHS.append(RHS);
196     assert(PathEq(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(PathEq(LHS, E));
205     assert(&Ref == &LHS);
206   }
207   {
208     path LHS(L);
209     StrView RHS(R);
210     path& Ref = LHS.append(RHS);
211     assert(PathEq(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(PathEq(LHS, E));
220     assert(&Ref == &LHS);
221   }
222   {
223     path LHS(L);
224     Ptr RHS(R);
225     path& Ref = LHS.append(RHS);
226     assert(PathEq(LHS, E));
227     assert(&Ref == &LHS);
228   }
229   {
230     path LHS(L);
231     Ptr RHS(R);
232     path& Ref = LHS.append(RHS, StrEnd(RHS));
233     assert(PathEq(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(PathEq(LHS, E));
242     assert(&Ref == &LHS);
243   }
244   {
245     path LHS(L); InputIter RHS(R);
246     path& Ref = LHS.append(RHS);
247     assert(PathEq(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.append(RHS, REnd);
255     assert(PathEq(LHS, E));
256     assert(&Ref == &LHS);
257   }
258 }
259 
260 
261 
262 template <class It, class = decltype(fs::path{}.append(std::declval<It>()))>
263 constexpr bool has_append(int) { return true; }
264 template <class It>
265 constexpr bool has_append(long) { return false; }
266 
267 template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))>
268 constexpr bool has_append_op(int) { return true; }
269 template <class It>
270 constexpr bool has_append_op(long) { return false; }
271 
272 template <class It>
273 constexpr bool has_append() {
274   static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same");
275   return has_append<It>(0) && has_append_op<It>(0);
276 }
277 
278 void test_sfinae()
279 {
280   using namespace fs;
281   {
282     using It = const char* const;
283     static_assert(has_append<It>(), "");
284   }
285   {
286     using It = input_iterator<const char*>;
287     static_assert(has_append<It>(), "");
288   }
289   {
290     struct Traits {
291       using iterator_category = std::input_iterator_tag;
292       using value_type = const char;
293       using pointer = const char*;
294       using reference = const char&;
295       using difference_type = std::ptrdiff_t;
296     };
297     using It = input_iterator<const char*, Traits>;
298     static_assert(has_append<It>(), "");
299   }
300   {
301     using It = output_iterator<const char*>;
302     static_assert(!has_append<It>(), "");
303 
304   }
305   {
306     static_assert(!has_append<int*>(), "");
307   }
308   {
309     static_assert(!has_append<char>(), "");
310     static_assert(!has_append<const char>(), "");
311   }
312 }
313 
314 int main(int, char**)
315 {
316   using namespace fs;
317   for (auto const & TC : Cases) {
318     {
319       const char* LHS_In = TC.lhs;
320       const char* RHS_In = TC.rhs;
321       path LHS(LHS_In);
322       path RHS(RHS_In);
323       path& Res = (LHS /= RHS);
324       assert(PathEq(Res, (const char*)TC.expect));
325       assert(&Res == &LHS);
326     }
327     doAppendSourceTest<char>    (TC);
328     doAppendSourceTest<wchar_t> (TC);
329     doAppendSourceTest<char16_t>(TC);
330     doAppendSourceTest<char32_t>(TC);
331   }
332   for (auto const & TC : LongLHSCases) {
333     (void)TC;
334     LIBCPP_ONLY(doAppendSourceAllocTest<char>(TC));
335     LIBCPP_ONLY(doAppendSourceAllocTest<wchar_t>(TC));
336   }
337   test_sfinae();
338 
339   return 0;
340 }
341