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