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