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