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
12 // <experimental/filesystem>
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 <experimental/filesystem>
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"
42 namespace fs = std::experimental::filesystem;
44 struct ConcatOperatorTestcase {
47 MultiStringType expect;
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[] =
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")}
62 const ConcatOperatorTestcase LongLHSCases[] =
64 {S(""), S(LONGSTR), S(LONGSTR)}
65 , {S("p1/"), S(LONGSTR), S("p1/" LONGSTR)}
67 const ConcatOperatorTestcase CharTestCases[] =
69 {S(""), S("P"), S("P")}
70 , {S("/fooba"), S("r"), S("/foobar")}
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>
83 void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC)
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>;
93 const Ptr E = TC.expect;
94 std::size_t ReserveSize = StrLen(E) + 1;
97 path LHS(L); PathReserve(LHS, ReserveSize);
100 DisableAllocationGuard g;
107 path LHS(L); PathReserve(LHS, ReserveSize);
110 DisableAllocationGuard g;
117 path LHS(L); PathReserve(LHS, ReserveSize);
120 DisableAllocationGuard g;
126 path LHS(L); PathReserve(LHS, ReserveSize);
129 DisableAllocationGuard g;
130 LHS.concat(RHS, StrEnd(RHS));
134 // input iterator - For non-native char types, appends needs to copy the
135 // iterator range into a contigious block of memory before it can perform the
136 // code_cvt conversions.
137 // For "char" no allocations will be performed because no conversion is
139 bool DisableAllocations = std::is_same<CharT, char>::value;
141 path LHS(L); PathReserve(LHS, ReserveSize);
144 RequireAllocationGuard g; // requires 1 or more allocations occur by default
145 if (DisableAllocations) g.requireExactly(0);
151 path LHS(L); PathReserve(LHS, ReserveSize);
153 InputIter REnd(StrEnd(R));
155 RequireAllocationGuard g;
156 if (DisableAllocations) g.requireExactly(0);
157 LHS.concat(RHS, REnd);
163 template <class CharT>
164 void doConcatSourceTest(ConcatOperatorTestcase const& TC)
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;
178 path& Ref = (LHS += RHS);
180 assert(&Ref == &LHS);
185 path& Ref = LHS.concat(RHS);
187 assert(&Ref == &LHS);
193 path& Ref = (LHS += RHS);
195 assert(&Ref == &LHS);
200 path& Ref = LHS.concat(RHS);
202 assert(&Ref == &LHS);
208 path& Ref = (LHS += RHS);
210 assert(&Ref == &LHS);
215 path& Ref = LHS.concat(RHS);
217 assert(&Ref == &LHS);
222 path& Ref = LHS.concat(RHS, StrEnd(RHS));
224 assert(&Ref == &LHS);
230 path& Ref = (LHS += RHS);
232 assert(&Ref == &LHS);
235 path LHS(L); InputIter RHS(R);
236 path& Ref = LHS.concat(RHS);
238 assert(&Ref == &LHS);
243 InputIter REnd(StrEnd(R));
244 path& Ref = LHS.concat(RHS, REnd);
246 assert(&Ref == &LHS);
250 template <class CharT>
251 void doConcatECharTest(ConcatOperatorTestcase const& TC)
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;
262 path& Ref = (LHS += R);
264 assert(&Ref == &LHS);
269 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>
270 constexpr bool has_concat(int) { return true; }
272 constexpr bool has_concat(long) { return false; }
274 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>
275 constexpr bool has_concat_op(int) { return true; }
277 constexpr bool has_concat_op(long) { return false; }
279 constexpr bool has_concat_op() { return has_concat_op<It>(0); }
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);
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>(), "");
296 using It = const char* const;
297 static_assert(has_concat<It>(), "");
300 using It = input_iterator<const char*>;
301 static_assert(has_concat<It>(), "");
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;
311 using It = input_iterator<const char*, Traits>;
312 static_assert(has_concat<It>(), "");
315 using It = output_iterator<const char*>;
316 static_assert(!has_concat<It>(), "");
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>(), "");
326 static_assert(!has_concat<int*>(), "");
333 for (auto const & TC : Cases) {
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);
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);
348 doConcatSourceTest<char> (TC);
349 doConcatSourceTest<wchar_t> (TC);
350 doConcatSourceTest<char16_t>(TC);
351 doConcatSourceTest<char32_t>(TC);
353 for (auto const & TC : LongLHSCases) {
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);
361 DisableAllocationGuard g;
362 path& Ref = (LHS += RHS);
363 assert(&Ref == &LHS);
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);
373 DisableAllocationGuard g;
374 path& Ref = (LHS += RHS);
375 assert(&Ref == &LHS);
379 doConcatSourceAllocTest<char>(TC);
380 doConcatSourceAllocTest<wchar_t>(TC);
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);