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 // <filesystem>
12 
13 // class path
14 
15 // path& operator/=(path const&)
16 // template <class Source>
17 //      path& operator/=(Source const&);
18 // template <class Source>
19 //      path& append(Source const&);
20 // template <class InputIterator>
21 //      path& append(InputIterator first, InputIterator last);
22 
23 
24 #include "filesystem_include.h"
25 #include <type_traits>
26 #include <string_view>
27 #include <cassert>
28 
29 #include "test_macros.h"
30 #include "test_iterators.h"
31 #include "count_new.h"
32 #include "filesystem_test_helper.h"
33 #include "verbose_assert.h"
34 
35 
36 struct AppendOperatorTestcase {
37   MultiStringType lhs;
38   MultiStringType rhs;
39   MultiStringType expect;
40 };
41 
42 #define S(Str) MKSTR(Str)
43 const AppendOperatorTestcase Cases[] =
44     {
45         {S(""),     S(""),      S("")}
46       , {S("p1"),   S("p2"),    S("p1/p2")}
47       , {S("p1/"),  S("p2"),    S("p1/p2")}
48       , {S("p1"),   S("/p2"),   S("/p2")}
49       , {S("p1/"),  S("/p2"),   S("/p2")}
50       , {S("p1"),   S("\\p2"),  S("p1/\\p2")}
51       , {S("p1\\"), S("p2"),  S("p1\\/p2")}
52       , {S("p1\\"), S("\\p2"),  S("p1\\/\\p2")}
53       , {S(""),     S("p2"),    S("p2")}
54       , {S("/p1"),  S("p2"),    S("/p1/p2")}
55       , {S("/p1"),  S("/p2"),    S("/p2")}
56       , {S("/p1/p3"),  S("p2"),    S("/p1/p3/p2")}
57       , {S("/p1/p3/"),  S("p2"),    S("/p1/p3/p2")}
58       , {S("/p1/"),  S("p2"),    S("/p1/p2")}
59       , {S("/p1/p3/"),  S("/p2/p4"),    S("/p2/p4")}
60       , {S("/"),    S(""),      S("/")}
61       , {S("/p1"), S("/p2/"), S("/p2/")}
62       , {S("p1"),   S(""),      S("p1/")}
63       , {S("p1/"),  S(""),      S("p1/")}
64     };
65 
66 
67 const AppendOperatorTestcase LongLHSCases[] =
68     {
69         {S("p1"),   S("p2"),    S("p1/p2")}
70       , {S("p1/"),  S("p2"),    S("p1/p2")}
71       , {S("p1"),   S("/p2"),   S("/p2")}
72       , {S("/p1"),  S("p2"),    S("/p1/p2")}
73     };
74 #undef S
75 
76 
77 // The append operator may need to allocate a temporary buffer before a code_cvt
78 // conversion. Test if this allocation occurs by:
79 //   1. Create a path, `LHS`, and reserve enough space to append `RHS`.
80 //      This prevents `LHS` from allocating during the actual appending.
81 //   2. Create a `Source` object `RHS`, which represents a "large" string.
82 //      (The string must not trigger the SSO)
83 //   3. Append `RHS` to `LHS` and check for the expected allocation behavior.
84 template <class CharT>
85 void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
86 {
87   using namespace fs;
88   using Ptr = CharT const*;
89   using Str = std::basic_string<CharT>;
90   using StrView = std::basic_string_view<CharT>;
91   using InputIter = input_iterator<Ptr>;
92 
93   const Ptr L = TC.lhs;
94   Str RShort = (Ptr)TC.rhs;
95   Str EShort = (Ptr)TC.expect;
96   assert(RShort.size() >= 2);
97   CharT c = RShort.back();
98   RShort.append(100, c);
99   EShort.append(100, c);
100   const Ptr R = RShort.data();
101   const Str& E = EShort;
102   std::size_t ReserveSize = E.size() + 3;
103   // basic_string
104   {
105     path LHS(L); PathReserve(LHS, ReserveSize);
106     Str  RHS(R);
107     {
108       DisableAllocationGuard g;
109       LHS /= RHS;
110     }
111     ASSERT_PRED(PathEq, LHS , E);
112   }
113   // basic_string_view
114   {
115     path LHS(L); PathReserve(LHS, ReserveSize);
116     StrView  RHS(R);
117     {
118       DisableAllocationGuard g;
119       LHS /= RHS;
120     }
121     assert(PathEq(LHS, E));
122   }
123   // CharT*
124   {
125     path LHS(L); PathReserve(LHS, ReserveSize);
126     Ptr RHS(R);
127     {
128       DisableAllocationGuard g;
129       LHS /= RHS;
130     }
131     assert(PathEq(LHS, E));
132   }
133   {
134     path LHS(L); PathReserve(LHS, ReserveSize);
135     Ptr RHS(R);
136     {
137       DisableAllocationGuard g;
138       LHS.append(RHS, StrEnd(RHS));
139     }
140     assert(PathEq(LHS, E));
141   }
142   // input iterator - For non-native char types, appends needs to copy the
143   // iterator range into a contiguous block of memory before it can perform the
144   // code_cvt conversions.
145   // For "char" no allocations will be performed because no conversion is
146   // required.
147   bool DisableAllocations = std::is_same<CharT, char>::value;
148   {
149     path LHS(L); PathReserve(LHS, ReserveSize);
150     InputIter RHS(R);
151     {
152       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
153       if (DisableAllocations) g.requireExactly(0);
154       LHS /= RHS;
155     }
156     assert(PathEq(LHS, E));
157   }
158   {
159     path LHS(L); PathReserve(LHS, ReserveSize);
160     InputIter RHS(R);
161     InputIter REnd(StrEnd(R));
162     {
163       RequireAllocationGuard g;
164       if (DisableAllocations) g.requireExactly(0);
165       LHS.append(RHS, REnd);
166     }
167     assert(PathEq(LHS, E));
168   }
169 }
170 
171 template <class CharT>
172 void doAppendSourceTest(AppendOperatorTestcase const& TC)
173 {
174   using namespace fs;
175   using Ptr = CharT const*;
176   using Str = std::basic_string<CharT>;
177   using StrView = std::basic_string_view<CharT>;
178   using InputIter = input_iterator<Ptr>;
179   const Ptr L = TC.lhs;
180   const Ptr R = TC.rhs;
181   const Ptr E = TC.expect;
182   // basic_string
183   {
184     path Result(L);
185     Str RHS(R);
186     path& Ref = (Result /= RHS);
187     ASSERT_EQ(Result, E)
188         << DISPLAY(L) << DISPLAY(R);
189     assert(&Ref == &Result);
190   }
191   {
192     path LHS(L);
193     Str RHS(R);
194     path& Ref = LHS.append(RHS);
195     assert(PathEq(LHS, E));
196     assert(&Ref == &LHS);
197   }
198   // basic_string_view
199   {
200     path LHS(L);
201     StrView RHS(R);
202     path& Ref = (LHS /= RHS);
203     assert(PathEq(LHS, E));
204     assert(&Ref == &LHS);
205   }
206   {
207     path LHS(L);
208     StrView RHS(R);
209     path& Ref = LHS.append(RHS);
210     assert(PathEq(LHS, E));
211     assert(&Ref == &LHS);
212   }
213   // Char*
214   {
215     path LHS(L);
216     Str RHS(R);
217     path& Ref = (LHS /= RHS);
218     assert(PathEq(LHS, E));
219     assert(&Ref == &LHS);
220   }
221   {
222     path LHS(L);
223     Ptr RHS(R);
224     path& Ref = LHS.append(RHS);
225     assert(PathEq(LHS, E));
226     assert(&Ref == &LHS);
227   }
228   {
229     path LHS(L);
230     Ptr RHS(R);
231     path& Ref = LHS.append(RHS, StrEnd(RHS));
232     ASSERT_PRED(PathEq, LHS, E)
233         << DISPLAY(L) << DISPLAY(R);
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_PRED(PathEq, Res, (const char*)TC.expect)
325           << DISPLAY(LHS_In) << DISPLAY(RHS_In);
326       assert(&Res == &LHS);
327     }
328     doAppendSourceTest<char>    (TC);
329     doAppendSourceTest<wchar_t> (TC);
330     doAppendSourceTest<char16_t>(TC);
331     doAppendSourceTest<char32_t>(TC);
332   }
333   for (auto const & TC : LongLHSCases) {
334     doAppendSourceAllocTest<char>(TC);
335     doAppendSourceAllocTest<wchar_t>(TC);
336   }
337   test_sfinae();
338 
339   return 0;
340 }
341