• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===----------------------------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // UNSUPPORTED: c++98, c++03
11 
12 // <filesystem>
13 
14 // class path
15 
16 // path& operator/=(path const&)
17 // template <class Source>
18 //      path& operator/=(Source const&);
19 // template <class Source>
20 //      path& append(Source const&);
21 // template <class InputIterator>
22 //      path& append(InputIterator first, InputIterator last);
23 
24 
25 #include "filesystem_include.hpp"
26 #include <type_traits>
27 #include <string_view>
28 #include <cassert>
29 
30 #include "test_macros.h"
31 #include "test_iterators.h"
32 #include "count_new.hpp"
33 #include "filesystem_test_helper.hpp"
34 #include "verbose_assert.h"
35 
36 
37 struct AppendOperatorTestcase {
38   MultiStringType lhs;
39   MultiStringType rhs;
40   MultiStringType expect;
41 };
42 
43 #define S(Str) MKSTR(Str)
44 const AppendOperatorTestcase Cases[] =
45     {
46         {S(""),     S(""),      S("")}
47       , {S("p1"),   S("p2"),    S("p1/p2")}
48       , {S("p1/"),  S("p2"),    S("p1/p2")}
49       , {S("p1"),   S("/p2"),   S("/p2")}
50       , {S("p1/"),  S("/p2"),   S("/p2")}
51       , {S("p1"),   S("\\p2"),  S("p1/\\p2")}
52       , {S("p1\\"), S("p2"),  S("p1\\/p2")}
53       , {S("p1\\"), S("\\p2"),  S("p1\\/\\p2")}
54       , {S(""),     S("p2"),    S("p2")}
55       , {S("/p1"),  S("p2"),    S("/p1/p2")}
56       , {S("/p1"),  S("/p2"),    S("/p2")}
57       , {S("/p1/p3"),  S("p2"),    S("/p1/p3/p2")}
58       , {S("/p1/p3/"),  S("p2"),    S("/p1/p3/p2")}
59       , {S("/p1/"),  S("p2"),    S("/p1/p2")}
60       , {S("/p1/p3/"),  S("/p2/p4"),    S("/p2/p4")}
61       , {S("/"),    S(""),      S("/")}
62       , {S("/p1"), S("/p2/"), S("/p2/")}
63       , {S("p1"),   S(""),      S("p1/")}
64       , {S("p1/"),  S(""),      S("p1/")}
65     };
66 
67 
68 const AppendOperatorTestcase LongLHSCases[] =
69     {
70         {S("p1"),   S("p2"),    S("p1/p2")}
71       , {S("p1/"),  S("p2"),    S("p1/p2")}
72       , {S("p1"),   S("/p2"),   S("/p2")}
73       , {S("/p1"),  S("p2"),    S("/p1/p2")}
74     };
75 #undef S
76 
77 
78 // The append operator may need to allocate a temporary buffer before a code_cvt
79 // conversion. Test if this allocation occurs by:
80 //   1. Create a path, `LHS`, and reserve enough space to append `RHS`.
81 //      This prevents `LHS` from allocating during the actual appending.
82 //   2. Create a `Source` object `RHS`, which represents a "large" string.
83 //      (The string must not trigger the SSO)
84 //   3. Append `RHS` to `LHS` and check for the expected allocation behavior.
85 template <class CharT>
doAppendSourceAllocTest(AppendOperatorTestcase const & TC)86 void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
87 {
88   using namespace fs;
89   using Ptr = CharT const*;
90   using Str = std::basic_string<CharT>;
91   using StrView = std::basic_string_view<CharT>;
92   using InputIter = input_iterator<Ptr>;
93 
94   const Ptr L = TC.lhs;
95   Str RShort = (Ptr)TC.rhs;
96   Str EShort = (Ptr)TC.expect;
97   assert(RShort.size() >= 2);
98   CharT c = RShort.back();
99   RShort.append(100, c);
100   EShort.append(100, c);
101   const Ptr R = RShort.data();
102   const Str& E = EShort;
103   std::size_t ReserveSize = E.size() + 3;
104   // basic_string
105   {
106     path LHS(L); PathReserve(LHS, ReserveSize);
107     Str  RHS(R);
108     {
109       DisableAllocationGuard g;
110       LHS /= RHS;
111     }
112     ASSERT_PRED(PathEq, LHS , E);
113   }
114   // basic_string_view
115   {
116     path LHS(L); PathReserve(LHS, ReserveSize);
117     StrView  RHS(R);
118     {
119       DisableAllocationGuard g;
120       LHS /= RHS;
121     }
122     assert(PathEq(LHS, E));
123   }
124   // CharT*
125   {
126     path LHS(L); PathReserve(LHS, ReserveSize);
127     Ptr RHS(R);
128     {
129       DisableAllocationGuard g;
130       LHS /= RHS;
131     }
132     assert(PathEq(LHS, E));
133   }
134   {
135     path LHS(L); PathReserve(LHS, ReserveSize);
136     Ptr RHS(R);
137     {
138       DisableAllocationGuard g;
139       LHS.append(RHS, StrEnd(RHS));
140     }
141     assert(PathEq(LHS, E));
142   }
143   // input iterator - For non-native char types, appends needs to copy the
144   // iterator range into a contiguous block of memory before it can perform the
145   // code_cvt conversions.
146   // For "char" no allocations will be performed because no conversion is
147   // required.
148   bool DisableAllocations = std::is_same<CharT, char>::value;
149   {
150     path LHS(L); PathReserve(LHS, ReserveSize);
151     InputIter RHS(R);
152     {
153       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
154       if (DisableAllocations) g.requireExactly(0);
155       LHS /= RHS;
156     }
157     assert(PathEq(LHS, E));
158   }
159   {
160     path LHS(L); PathReserve(LHS, ReserveSize);
161     InputIter RHS(R);
162     InputIter REnd(StrEnd(R));
163     {
164       RequireAllocationGuard g;
165       if (DisableAllocations) g.requireExactly(0);
166       LHS.append(RHS, REnd);
167     }
168     assert(PathEq(LHS, E));
169   }
170 }
171 
172 template <class CharT>
doAppendSourceTest(AppendOperatorTestcase const & TC)173 void doAppendSourceTest(AppendOperatorTestcase const& TC)
174 {
175   using namespace fs;
176   using Ptr = CharT const*;
177   using Str = std::basic_string<CharT>;
178   using StrView = std::basic_string_view<CharT>;
179   using InputIter = input_iterator<Ptr>;
180   const Ptr L = TC.lhs;
181   const Ptr R = TC.rhs;
182   const Ptr E = TC.expect;
183   // basic_string
184   {
185     path Result(L);
186     Str RHS(R);
187     path& Ref = (Result /= RHS);
188     ASSERT_EQ(Result, E)
189         << DISPLAY(L) << DISPLAY(R);
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_PRED(PathEq, LHS, E)
234         << DISPLAY(L) << DISPLAY(R);
235     assert(&Ref == &LHS);
236   }
237   // iterators
238   {
239     path LHS(L);
240     InputIter RHS(R);
241     path& Ref = (LHS /= RHS);
242     assert(PathEq(LHS, E));
243     assert(&Ref == &LHS);
244   }
245   {
246     path LHS(L); InputIter RHS(R);
247     path& Ref = LHS.append(RHS);
248     assert(PathEq(LHS, E));
249     assert(&Ref == &LHS);
250   }
251   {
252     path LHS(L);
253     InputIter RHS(R);
254     InputIter REnd(StrEnd(R));
255     path& Ref = LHS.append(RHS, REnd);
256     assert(PathEq(LHS, E));
257     assert(&Ref == &LHS);
258   }
259 }
260 
261 
262 
263 template <class It, class = decltype(fs::path{}.append(std::declval<It>()))>
has_append(int)264 constexpr bool has_append(int) { return true; }
265 template <class It>
has_append(long)266 constexpr bool has_append(long) { return false; }
267 
268 template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))>
has_append_op(int)269 constexpr bool has_append_op(int) { return true; }
270 template <class It>
has_append_op(long)271 constexpr bool has_append_op(long) { return false; }
272 
273 template <class It>
has_append()274 constexpr bool has_append() {
275   static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same");
276   return has_append<It>(0) && has_append_op<It>(0);
277 }
278 
test_sfinae()279 void test_sfinae()
280 {
281   using namespace fs;
282   {
283     using It = const char* const;
284     static_assert(has_append<It>(), "");
285   }
286   {
287     using It = input_iterator<const char*>;
288     static_assert(has_append<It>(), "");
289   }
290   {
291     struct Traits {
292       using iterator_category = std::input_iterator_tag;
293       using value_type = const char;
294       using pointer = const char*;
295       using reference = const char&;
296       using difference_type = std::ptrdiff_t;
297     };
298     using It = input_iterator<const char*, Traits>;
299     static_assert(has_append<It>(), "");
300   }
301   {
302     using It = output_iterator<const char*>;
303     static_assert(!has_append<It>(), "");
304 
305   }
306   {
307     static_assert(!has_append<int*>(), "");
308   }
309   {
310     static_assert(!has_append<char>(), "");
311     static_assert(!has_append<const char>(), "");
312   }
313 }
314 
main()315 int main()
316 {
317   using namespace fs;
318   for (auto const & TC : Cases) {
319     {
320       const char* LHS_In = TC.lhs;
321       const char* RHS_In = TC.rhs;
322       path LHS(LHS_In);
323       path RHS(RHS_In);
324       path& Res = (LHS /= RHS);
325       ASSERT_PRED(PathEq, Res, (const char*)TC.expect)
326           << DISPLAY(LHS_In) << DISPLAY(RHS_In);
327       assert(&Res == &LHS);
328     }
329     doAppendSourceTest<char>    (TC);
330     doAppendSourceTest<wchar_t> (TC);
331     doAppendSourceTest<char16_t>(TC);
332     doAppendSourceTest<char32_t>(TC);
333   }
334   for (auto const & TC : LongLHSCases) {
335     doAppendSourceAllocTest<char>(TC);
336     doAppendSourceAllocTest<wchar_t>(TC);
337   }
338   test_sfinae();
339 }
340