• 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+=(const path& x);
17 // path& operator+=(const string_type& x);
18 // path& operator+=(string_view x);
19 // path& operator+=(const value_type* x);
20 // path& operator+=(value_type x);
21 // template <class Source>
22 //   path& operator+=(const Source& x);
23 // template <class EcharT>
24 //   path& operator+=(EcharT x);
25 // template <class Source>
26 //   path& concat(const Source& x);
27 // template <class InputIterator>
28 //   path& concat(InputIterator first, InputIterator last);
29 
30 
31 #include <experimental/filesystem>
32 #include <type_traits>
33 #include <string>
34 #include <string_view>
35 #include <cassert>
36 
37 #include "test_macros.h"
38 #include "test_iterators.h"
39 #include "count_new.hpp"
40 #include "filesystem_test_helper.hpp"
41 
42 namespace fs = std::experimental::filesystem;
43 
44 struct ConcatOperatorTestcase {
45   MultiStringType lhs;
46   MultiStringType rhs;
47   MultiStringType expect;
48 };
49 
50 #define LONGSTR "LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR"
51 #define S(Str) MKSTR(Str)
52 const ConcatOperatorTestcase Cases[] =
53     {
54         {S(""),         S(""),                  S("")}
55       , {S("p1"),       S("p2"),                S("p1p2")}
56       , {S("p1/"),      S("/p2"),               S("p1//p2")}
57       , {S(""),         S("\\foo/bar/baz"),     S("\\foo/bar/baz")}
58       , {S("c:\\foo"),  S(""),                  S("c:\\foo")}
59       , {S(LONGSTR),    S("foo"),               S(LONGSTR "foo")}
60       , {S("abcdefghijklmnopqrstuvwxyz/\\"), S("/\\123456789"), S("abcdefghijklmnopqrstuvwxyz/\\/\\123456789")}
61     };
62 const ConcatOperatorTestcase LongLHSCases[] =
63     {
64         {S(""),        S(LONGSTR),     S(LONGSTR)}
65       , {S("p1/"),     S(LONGSTR),      S("p1/" LONGSTR)}
66     };
67 const ConcatOperatorTestcase CharTestCases[] =
68     {
69         {S(""),       S("P"), S("P")}
70       , {S("/fooba"), S("r"), S("/foobar")}
71     };
72 #undef S
73 #undef LONGSTR
74 
75 // The concat operator may need to allocate a temporary buffer before a code_cvt
76 // conversion. Test if this allocation occurs by:
77 //   1. Create a path, `LHS`, and reserve enough space to append `RHS`.
78 //      This prevents `LHS` from allocating during the actual appending.
79 //   2. Create a `Source` object `RHS`, which represents a "large" string.
80 //      (The string must not trigger the SSO)
81 //   3. Concat `RHS` to `LHS` and check for the expected allocation behavior.
82 template <class CharT>
doConcatSourceAllocTest(ConcatOperatorTestcase const & TC)83 void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC)
84 {
85   using namespace fs;
86   using Ptr = CharT const*;
87   using Str = std::basic_string<CharT>;
88   using StrView = std::basic_string_view<CharT>;
89   using InputIter = input_iterator<Ptr>;
90 
91   const Ptr L = TC.lhs;
92   const Ptr R = TC.rhs;
93   const Ptr E =  TC.expect;
94   std::size_t ReserveSize = StrLen(E) + 1;
95   // basic_string
96   {
97     path LHS(L); PathReserve(LHS, ReserveSize);
98     Str  RHS(R);
99     {
100       DisableAllocationGuard g;
101       LHS += RHS;
102     }
103     assert(LHS == E);
104   }
105   // basic_string_view
106   {
107     path LHS(L); PathReserve(LHS, ReserveSize);
108     StrView  RHS(R);
109     {
110       DisableAllocationGuard g;
111       LHS += RHS;
112     }
113     assert(LHS == E);
114   }
115   // CharT*
116   {
117     path LHS(L); PathReserve(LHS, ReserveSize);
118     Ptr RHS(R);
119     {
120       DisableAllocationGuard g;
121       LHS += RHS;
122     }
123     assert(LHS == E);
124   }
125   {
126     path LHS(L); PathReserve(LHS, ReserveSize);
127     Ptr RHS(R);
128     {
129       DisableAllocationGuard g;
130       LHS.concat(RHS, StrEnd(RHS));
131     }
132     assert(LHS == E);
133   }
134   // input iterator - For non-native char types, appends needs to copy the
135   // iterator range into a contiguous block of memory before it can perform the
136   // code_cvt conversions.
137   // For "char" no allocations will be performed because no conversion is
138   // required.
139   bool DisableAllocations = std::is_same<CharT, char>::value;
140   {
141     path LHS(L); PathReserve(LHS, ReserveSize);
142     InputIter RHS(R);
143     {
144       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
145       if (DisableAllocations) g.requireExactly(0);
146       LHS += RHS;
147     }
148     assert(LHS == E);
149   }
150   {
151     path LHS(L); PathReserve(LHS, ReserveSize);
152     InputIter RHS(R);
153     InputIter REnd(StrEnd(R));
154     {
155       RequireAllocationGuard g;
156       if (DisableAllocations) g.requireExactly(0);
157       LHS.concat(RHS, REnd);
158     }
159     assert(LHS == E);
160   }
161 }
162 
163 template <class CharT>
doConcatSourceTest(ConcatOperatorTestcase const & TC)164 void doConcatSourceTest(ConcatOperatorTestcase const& TC)
165 {
166   using namespace fs;
167   using Ptr = CharT const*;
168   using Str = std::basic_string<CharT>;
169   using StrView = std::basic_string_view<CharT>;
170   using InputIter = input_iterator<Ptr>;
171   const Ptr L = TC.lhs;
172   const Ptr R = TC.rhs;
173   const Ptr E = TC.expect;
174   // basic_string
175   {
176     path LHS(L);
177     Str RHS(R);
178     path& Ref = (LHS += RHS);
179     assert(LHS == E);
180     assert(&Ref == &LHS);
181   }
182   {
183     path LHS(L);
184     Str RHS(R);
185     path& Ref = LHS.concat(RHS);
186     assert(LHS == E);
187     assert(&Ref == &LHS);
188   }
189   // basic_string_view
190   {
191     path LHS(L);
192     StrView RHS(R);
193     path& Ref = (LHS += RHS);
194     assert(LHS == E);
195     assert(&Ref == &LHS);
196   }
197   {
198     path LHS(L);
199     StrView RHS(R);
200     path& Ref = LHS.concat(RHS);
201     assert(LHS == E);
202     assert(&Ref == &LHS);
203   }
204   // Char*
205   {
206     path LHS(L);
207     Str RHS(R);
208     path& Ref = (LHS += RHS);
209     assert(LHS == E);
210     assert(&Ref == &LHS);
211   }
212   {
213     path LHS(L);
214     Ptr RHS(R);
215     path& Ref = LHS.concat(RHS);
216     assert(LHS == E);
217     assert(&Ref == &LHS);
218   }
219   {
220     path LHS(L);
221     Ptr RHS(R);
222     path& Ref = LHS.concat(RHS, StrEnd(RHS));
223     assert(LHS == E);
224     assert(&Ref == &LHS);
225   }
226   // iterators
227   {
228     path LHS(L);
229     InputIter RHS(R);
230     path& Ref = (LHS += RHS);
231     assert(LHS == E);
232     assert(&Ref == &LHS);
233   }
234   {
235     path LHS(L); InputIter RHS(R);
236     path& Ref = LHS.concat(RHS);
237     assert(LHS == E);
238     assert(&Ref == &LHS);
239   }
240   {
241     path LHS(L);
242     InputIter RHS(R);
243     InputIter REnd(StrEnd(R));
244     path& Ref = LHS.concat(RHS, REnd);
245     assert(LHS == E);
246     assert(&Ref == &LHS);
247   }
248 }
249 
250 template <class CharT>
doConcatECharTest(ConcatOperatorTestcase const & TC)251 void doConcatECharTest(ConcatOperatorTestcase const& TC)
252 {
253   using namespace fs;
254   using Ptr = CharT const*;
255   const Ptr RStr = TC.rhs;
256   assert(StrLen(RStr) == 1);
257   const Ptr L   = TC.lhs;
258   const CharT R = RStr[0];
259   const Ptr E   = TC.expect;
260   {
261     path LHS(L);
262     path& Ref = (LHS += R);
263     assert(LHS == E);
264     assert(&Ref == &LHS);
265   }
266 }
267 
268 
269 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>
has_concat(int)270 constexpr bool has_concat(int) { return true; }
271 template <class It>
has_concat(long)272 constexpr bool has_concat(long) { return false; }
273 
274 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>
has_concat_op(int)275 constexpr bool has_concat_op(int) { return true; }
276 template <class It>
has_concat_op(long)277 constexpr bool has_concat_op(long) { return false; }
278 template <class It>
has_concat_op()279 constexpr bool has_concat_op() { return has_concat_op<It>(0); }
280 
281 template <class It>
has_concat()282 constexpr bool has_concat() {
283   static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same");
284   return has_concat<It>(0) && has_concat_op<It>(0);
285 }
286 
test_sfinae()287 void test_sfinae() {
288   using namespace fs;
289   {
290     static_assert(has_concat_op<char>(), "");
291     static_assert(has_concat_op<const char>(), "");
292     static_assert(has_concat_op<char16_t>(), "");
293     static_assert(has_concat_op<const char16_t>(), "");
294   }
295   {
296     using It = const char* const;
297     static_assert(has_concat<It>(), "");
298   }
299   {
300     using It = input_iterator<const char*>;
301     static_assert(has_concat<It>(), "");
302   }
303   {
304     struct Traits {
305       using iterator_category = std::input_iterator_tag;
306       using value_type = const char;
307       using pointer = const char*;
308       using reference = const char&;
309       using difference_type = std::ptrdiff_t;
310     };
311     using It = input_iterator<const char*, Traits>;
312     static_assert(has_concat<It>(), "");
313   }
314   {
315     using It = output_iterator<const char*>;
316     static_assert(!has_concat<It>(), "");
317   }
318   {
319     static_assert(!has_concat<int>(0), "");
320     // operator+=(int) is well formed since it converts to operator+=(value_type)
321     // but concat(int) isn't valid because there is no concat(value_type).
322     // This should probably be addressed by a LWG issue.
323     static_assert(has_concat_op<int>(), "");
324   }
325   {
326     static_assert(!has_concat<int*>(), "");
327   }
328 }
329 
main()330 int main()
331 {
332   using namespace fs;
333   for (auto const & TC : Cases) {
334     {
335       path LHS((const char*)TC.lhs);
336       path RHS((const char*)TC.rhs);
337       path& Ref = (LHS += RHS);
338       assert(LHS == (const char*)TC.expect);
339       assert(&Ref == &LHS);
340     }
341     {
342       path LHS((const char*)TC.lhs);
343       std::string_view RHS((const char*)TC.rhs);
344       path& Ref = (LHS += RHS);
345       assert(LHS == (const char*)TC.expect);
346       assert(&Ref == &LHS);
347     }
348     doConcatSourceTest<char>    (TC);
349     doConcatSourceTest<wchar_t> (TC);
350     doConcatSourceTest<char16_t>(TC);
351     doConcatSourceTest<char32_t>(TC);
352   }
353   for (auto const & TC : LongLHSCases) {
354     // Do path test
355     {
356       path LHS((const char*)TC.lhs);
357       path RHS((const char*)TC.rhs);
358       const char* E = TC.expect;
359       PathReserve(LHS, StrLen(E) + 5);
360       {
361         DisableAllocationGuard g;
362         path& Ref = (LHS += RHS);
363         assert(&Ref == &LHS);
364       }
365       assert(LHS == E);
366     }
367     {
368       path LHS((const char*)TC.lhs);
369       std::string_view RHS((const char*)TC.rhs);
370       const char* E = TC.expect;
371       PathReserve(LHS, StrLen(E) + 5);
372       {
373         DisableAllocationGuard g;
374         path& Ref = (LHS += RHS);
375         assert(&Ref == &LHS);
376       }
377       assert(LHS == E);
378     }
379     doConcatSourceAllocTest<char>(TC);
380     doConcatSourceAllocTest<wchar_t>(TC);
381   }
382   for (auto const& TC : CharTestCases) {
383     doConcatECharTest<char>(TC);
384     doConcatECharTest<wchar_t>(TC);
385     doConcatECharTest<char16_t>(TC);
386     doConcatECharTest<char32_t>(TC);
387   }
388   test_sfinae();
389 }
390