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

source code of libcxx/test/std/input.output/filesystems/class.path/path.member/path.concat.pass.cpp