1 //===----------------------------------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // UNSUPPORTED: c++98, c++03
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);
31 #include "filesystem_include.hpp"
32 #include <type_traits>
34 #include <string_view>
37 #include "test_macros.h"
38 #include "test_iterators.h"
39 #include "count_new.hpp"
40 #include "filesystem_test_helper.hpp"
43 struct ConcatOperatorTestcase {
46 MultiStringType expect;
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[] =
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")}
61 const ConcatOperatorTestcase LongLHSCases[] =
63 {S(""), S(LONGSTR), S(LONGSTR)}
64 , {S("p1/"), S(LONGSTR), S("p1/" LONGSTR)}
66 const ConcatOperatorTestcase CharTestCases[] =
68 {S(""), S("P"), S("P")}
69 , {S("/fooba"), S("r"), S("/foobar")}
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>
82 void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC)
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>;
92 const Ptr E = TC.expect;
93 std::size_t ReserveSize = StrLen(E) + 1;
96 path LHS(L); PathReserve(LHS, ReserveSize);
99 DisableAllocationGuard g;
106 path LHS(L); PathReserve(LHS, ReserveSize);
109 DisableAllocationGuard g;
116 path LHS(L); PathReserve(LHS, ReserveSize);
119 DisableAllocationGuard g;
125 path LHS(L); PathReserve(LHS, ReserveSize);
128 DisableAllocationGuard g;
129 LHS.concat(RHS, StrEnd(RHS));
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
138 bool DisableAllocations = std::is_same<CharT, char>::value;
140 path LHS(L); PathReserve(LHS, ReserveSize);
143 RequireAllocationGuard g; // requires 1 or more allocations occur by default
144 if (DisableAllocations) g.requireExactly(0);
150 path LHS(L); PathReserve(LHS, ReserveSize);
152 InputIter REnd(StrEnd(R));
154 RequireAllocationGuard g;
155 if (DisableAllocations) g.requireExactly(0);
156 LHS.concat(RHS, REnd);
162 template <class CharT>
163 void doConcatSourceTest(ConcatOperatorTestcase const& TC)
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;
177 path& Ref = (LHS += RHS);
179 assert(&Ref == &LHS);
184 path& Ref = LHS.concat(RHS);
186 assert(&Ref == &LHS);
192 path& Ref = (LHS += RHS);
194 assert(&Ref == &LHS);
199 path& Ref = LHS.concat(RHS);
201 assert(&Ref == &LHS);
207 path& Ref = (LHS += RHS);
209 assert(&Ref == &LHS);
214 path& Ref = LHS.concat(RHS);
216 assert(&Ref == &LHS);
221 path& Ref = LHS.concat(RHS, StrEnd(RHS));
223 assert(&Ref == &LHS);
229 path& Ref = (LHS += RHS);
231 assert(&Ref == &LHS);
234 path LHS(L); InputIter RHS(R);
235 path& Ref = LHS.concat(RHS);
237 assert(&Ref == &LHS);
242 InputIter REnd(StrEnd(R));
243 path& Ref = LHS.concat(RHS, REnd);
245 assert(&Ref == &LHS);
249 template <class CharT>
250 void doConcatECharTest(ConcatOperatorTestcase const& TC)
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;
261 path& Ref = (LHS += R);
263 assert(&Ref == &LHS);
268 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>
269 constexpr bool has_concat(int) { return true; }
271 constexpr bool has_concat(long) { return false; }
273 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>
274 constexpr bool has_concat_op(int) { return true; }
276 constexpr bool has_concat_op(long) { return false; }
278 constexpr bool has_concat_op() { return has_concat_op<It>(0); }
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);
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>(), "");
295 using It = const char* const;
296 static_assert(has_concat<It>(), "");
299 using It = input_iterator<const char*>;
300 static_assert(has_concat<It>(), "");
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;
310 using It = input_iterator<const char*, Traits>;
311 static_assert(has_concat<It>(), "");
314 using It = output_iterator<const char*>;
315 static_assert(!has_concat<It>(), "");
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>(), "");
325 static_assert(!has_concat<int*>(), "");
332 for (auto const & TC : Cases) {
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);
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);
347 doConcatSourceTest<char> (TC);
348 doConcatSourceTest<wchar_t> (TC);
349 doConcatSourceTest<char16_t>(TC);
350 doConcatSourceTest<char32_t>(TC);
352 for (auto const & TC : LongLHSCases) {
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);
360 DisableAllocationGuard g;
361 path& Ref = (LHS += RHS);
362 assert(&Ref == &LHS);
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);
372 DisableAllocationGuard g;
373 path& Ref = (LHS += RHS);
374 assert(&Ref == &LHS);
378 doConcatSourceAllocTest<char>(TC);
379 doConcatSourceAllocTest<wchar_t>(TC);
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);