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