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+=(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 "filesystem_include.hpp"
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
43 struct ConcatOperatorTestcase {
44 MultiStringType lhs;
45 MultiStringType rhs;
46 MultiStringType expect;
47 };
48
49 #define LONGSTR "LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR"
50 #define S(Str) MKSTR(Str)
51 const ConcatOperatorTestcase Cases[] =
52 {
53 {S(""), S(""), S("")}
54 , {S("p1"), S("p2"), S("p1p2")}
55 , {S("p1/"), S("/p2"), S("p1//p2")}
56 , {S(""), S("\\foo/bar/baz"), S("\\foo/bar/baz")}
57 , {S("c:\\foo"), S(""), S("c:\\foo")}
58 , {S(LONGSTR), S("foo"), S(LONGSTR "foo")}
59 , {S("abcdefghijklmnopqrstuvwxyz/\\"), S("/\\123456789"), S("abcdefghijklmnopqrstuvwxyz/\\/\\123456789")}
60 };
61 const ConcatOperatorTestcase LongLHSCases[] =
62 {
63 {S(""), S(LONGSTR), S(LONGSTR)}
64 , {S("p1/"), S(LONGSTR), S("p1/" LONGSTR)}
65 };
66 const ConcatOperatorTestcase CharTestCases[] =
67 {
68 {S(""), S("P"), S("P")}
69 , {S("/fooba"), S("r"), S("/foobar")}
70 };
71 #undef S
72 #undef LONGSTR
73
74 // The concat operator may need to allocate a temporary buffer before a code_cvt
75 // conversion. Test if this allocation occurs by:
76 // 1. Create a path, `LHS`, and reserve enough space to append `RHS`.
77 // This prevents `LHS` from allocating during the actual appending.
78 // 2. Create a `Source` object `RHS`, which represents a "large" string.
79 // (The string must not trigger the SSO)
80 // 3. Concat `RHS` to `LHS` and check for the expected allocation behavior.
81 template <class CharT>
doConcatSourceAllocTest(ConcatOperatorTestcase const & TC)82 void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC)
83 {
84 using namespace fs;
85 using Ptr = CharT const*;
86 using Str = std::basic_string<CharT>;
87 using StrView = std::basic_string_view<CharT>;
88 using InputIter = input_iterator<Ptr>;
89
90 const Ptr L = TC.lhs;
91 const Ptr R = TC.rhs;
92 const Ptr E = TC.expect;
93 std::size_t ReserveSize = StrLen(E) + 1;
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.concat(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.concat(RHS, REnd);
157 }
158 assert(LHS == E);
159 }
160 }
161
162 template <class CharT>
doConcatSourceTest(ConcatOperatorTestcase const & TC)163 void doConcatSourceTest(ConcatOperatorTestcase 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.concat(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.concat(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.concat(RHS);
215 assert(LHS == E);
216 assert(&Ref == &LHS);
217 }
218 {
219 path LHS(L);
220 Ptr RHS(R);
221 path& Ref = LHS.concat(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.concat(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.concat(RHS, REnd);
244 assert(LHS == E);
245 assert(&Ref == &LHS);
246 }
247 }
248
249 template <class CharT>
doConcatECharTest(ConcatOperatorTestcase const & TC)250 void doConcatECharTest(ConcatOperatorTestcase const& TC)
251 {
252 using namespace fs;
253 using Ptr = CharT const*;
254 const Ptr RStr = TC.rhs;
255 assert(StrLen(RStr) == 1);
256 const Ptr L = TC.lhs;
257 const CharT R = RStr[0];
258 const Ptr E = TC.expect;
259 {
260 path LHS(L);
261 path& Ref = (LHS += R);
262 assert(LHS == E);
263 assert(&Ref == &LHS);
264 }
265 }
266
267
268 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>
has_concat(int)269 constexpr bool has_concat(int) { return true; }
270 template <class It>
has_concat(long)271 constexpr bool has_concat(long) { return false; }
272
273 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>
has_concat_op(int)274 constexpr bool has_concat_op(int) { return true; }
275 template <class It>
has_concat_op(long)276 constexpr bool has_concat_op(long) { return false; }
277 template <class It>
has_concat_op()278 constexpr bool has_concat_op() { return has_concat_op<It>(0); }
279
280 template <class It>
has_concat()281 constexpr bool has_concat() {
282 static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same");
283 return has_concat<It>(0) && has_concat_op<It>(0);
284 }
285
test_sfinae()286 void test_sfinae() {
287 using namespace fs;
288 {
289 static_assert(has_concat_op<char>(), "");
290 static_assert(has_concat_op<const char>(), "");
291 static_assert(has_concat_op<char16_t>(), "");
292 static_assert(has_concat_op<const char16_t>(), "");
293 }
294 {
295 using It = const char* const;
296 static_assert(has_concat<It>(), "");
297 }
298 {
299 using It = input_iterator<const char*>;
300 static_assert(has_concat<It>(), "");
301 }
302 {
303 struct Traits {
304 using iterator_category = std::input_iterator_tag;
305 using value_type = const char;
306 using pointer = const char*;
307 using reference = const char&;
308 using difference_type = std::ptrdiff_t;
309 };
310 using It = input_iterator<const char*, Traits>;
311 static_assert(has_concat<It>(), "");
312 }
313 {
314 using It = output_iterator<const char*>;
315 static_assert(!has_concat<It>(), "");
316 }
317 {
318 static_assert(!has_concat<int>(0), "");
319 // operator+=(int) is well formed since it converts to operator+=(value_type)
320 // but concat(int) isn't valid because there is no concat(value_type).
321 // This should probably be addressed by a LWG issue.
322 static_assert(has_concat_op<int>(), "");
323 }
324 {
325 static_assert(!has_concat<int*>(), "");
326 }
327 }
328
main()329 int main()
330 {
331 using namespace fs;
332 for (auto const & TC : Cases) {
333 {
334 path LHS((const char*)TC.lhs);
335 path RHS((const char*)TC.rhs);
336 path& Ref = (LHS += RHS);
337 assert(LHS == (const char*)TC.expect);
338 assert(&Ref == &LHS);
339 }
340 {
341 path LHS((const char*)TC.lhs);
342 std::string_view RHS((const char*)TC.rhs);
343 path& Ref = (LHS += RHS);
344 assert(LHS == (const char*)TC.expect);
345 assert(&Ref == &LHS);
346 }
347 doConcatSourceTest<char> (TC);
348 doConcatSourceTest<wchar_t> (TC);
349 doConcatSourceTest<char16_t>(TC);
350 doConcatSourceTest<char32_t>(TC);
351 }
352 for (auto const & TC : LongLHSCases) {
353 // Do path test
354 {
355 path LHS((const char*)TC.lhs);
356 path RHS((const char*)TC.rhs);
357 const char* E = TC.expect;
358 PathReserve(LHS, StrLen(E) + 5);
359 {
360 DisableAllocationGuard g;
361 path& Ref = (LHS += RHS);
362 assert(&Ref == &LHS);
363 }
364 assert(LHS == E);
365 }
366 {
367 path LHS((const char*)TC.lhs);
368 std::string_view RHS((const char*)TC.rhs);
369 const char* E = TC.expect;
370 PathReserve(LHS, StrLen(E) + 5);
371 {
372 DisableAllocationGuard g;
373 path& Ref = (LHS += RHS);
374 assert(&Ref == &LHS);
375 }
376 assert(LHS == E);
377 }
378 doConcatSourceAllocTest<char>(TC);
379 doConcatSourceAllocTest<wchar_t>(TC);
380 }
381 for (auto const& TC : CharTestCases) {
382 doConcatECharTest<char>(TC);
383 doConcatECharTest<wchar_t>(TC);
384 doConcatECharTest<char16_t>(TC);
385 doConcatECharTest<char32_t>(TC);
386 }
387 test_sfinae();
388 }
389