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 = 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 // In DLL builds on Windows, the overridden operator new won't pick up 145 // allocations done within the DLL, so the RequireAllocationGuard below 146 // won't necessarily see allocations in the cases where they're expected. 147 bool DisableAllocations = std::is_same<CharT, path::value_type>::value; 148 { 149 path LHS(L); PathReserve(LHS, ReserveSize); 150 InputIter RHS(R); 151 { 152 RequireAllocationGuard g; // requires 1 or more allocations occur by default 153 if (DisableAllocations) g.requireExactly(0); 154 else TEST_ONLY_WIN32_DLL(g.requireAtLeast(0)); 155 LHS += RHS; 156 } 157 assert(LHS == E); 158 } 159 { 160 path LHS(L); PathReserve(LHS, ReserveSize); 161 InputIter RHS(R); 162 InputIter REnd(StrEnd(R)); 163 { 164 RequireAllocationGuard g; 165 if (DisableAllocations) g.requireExactly(0); 166 else TEST_ONLY_WIN32_DLL(g.requireAtLeast(0)); 167 LHS.concat(RHS, REnd); 168 } 169 assert(LHS == E); 170 } 171 } 172 173 template <class CharT> 174 void doConcatSourceTest(ConcatOperatorTestcase const& TC) 175 { 176 using namespace fs; 177 using Ptr = CharT const*; 178 using Str = std::basic_string<CharT>; 179 using StrView = std::basic_string_view<CharT>; 180 using InputIter = input_iterator<Ptr>; 181 const Ptr L = TC.lhs; 182 const Ptr R = TC.rhs; 183 const Ptr E = TC.expect; 184 // basic_string 185 { 186 path LHS(L); 187 Str RHS(R); 188 path& Ref = (LHS += RHS); 189 assert(LHS == E); 190 assert(&Ref == &LHS); 191 } 192 { 193 path LHS(L); 194 Str RHS(R); 195 path& Ref = LHS.concat(RHS); 196 assert(LHS == E); 197 assert(&Ref == &LHS); 198 } 199 // basic_string_view 200 { 201 path LHS(L); 202 StrView RHS(R); 203 path& Ref = (LHS += RHS); 204 assert(LHS == E); 205 assert(&Ref == &LHS); 206 } 207 { 208 path LHS(L); 209 StrView RHS(R); 210 path& Ref = LHS.concat(RHS); 211 assert(LHS == E); 212 assert(&Ref == &LHS); 213 } 214 // Char* 215 { 216 path LHS(L); 217 Str RHS(R); 218 path& Ref = (LHS += RHS); 219 assert(LHS == E); 220 assert(&Ref == &LHS); 221 } 222 { 223 path LHS(L); 224 Ptr RHS(R); 225 path& Ref = LHS.concat(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, StrEnd(RHS)); 233 assert(LHS == E); 234 assert(&Ref == &LHS); 235 } 236 // iterators 237 { 238 path LHS(L); 239 InputIter RHS(R); 240 path& Ref = (LHS += RHS); 241 assert(LHS == E); 242 assert(&Ref == &LHS); 243 } 244 { 245 path LHS(L); InputIter RHS(R); 246 path& Ref = LHS.concat(RHS); 247 assert(LHS == E); 248 assert(&Ref == &LHS); 249 } 250 { 251 path LHS(L); 252 InputIter RHS(R); 253 InputIter REnd(StrEnd(R)); 254 path& Ref = LHS.concat(RHS, REnd); 255 assert(LHS == E); 256 assert(&Ref == &LHS); 257 } 258 } 259 260 template <class CharT> 261 void doConcatECharTest(ConcatOperatorTestcase const& TC) 262 { 263 using namespace fs; 264 using Ptr = CharT const*; 265 const Ptr RStr = TC.rhs; 266 assert(StrLen(RStr) == 1); 267 const Ptr L = TC.lhs; 268 const CharT R = RStr[0]; 269 const Ptr E = TC.expect; 270 { 271 path LHS(L); 272 path& Ref = (LHS += R); 273 assert(LHS == E); 274 assert(&Ref == &LHS); 275 } 276 } 277 278 279 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))> 280 constexpr bool has_concat(int) { return true; } 281 template <class It> 282 constexpr bool has_concat(long) { return false; } 283 284 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))> 285 constexpr bool has_concat_op(int) { return true; } 286 template <class It> 287 constexpr bool has_concat_op(long) { return false; } 288 template <class It> 289 constexpr bool has_concat_op() { return has_concat_op<It>(0); } 290 291 template <class It> 292 constexpr bool has_concat() { 293 static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same"); 294 return has_concat<It>(0) && has_concat_op<It>(0); 295 } 296 297 void test_sfinae() { 298 using namespace fs; 299 { 300 static_assert(has_concat_op<char>(), ""); 301 static_assert(has_concat_op<const char>(), ""); 302 static_assert(has_concat_op<char16_t>(), ""); 303 static_assert(has_concat_op<const char16_t>(), ""); 304 } 305 { 306 using It = const char* const; 307 static_assert(has_concat<It>(), ""); 308 } 309 { 310 using It = input_iterator<const char*>; 311 static_assert(has_concat<It>(), ""); 312 } 313 { 314 struct Traits { 315 using iterator_category = std::input_iterator_tag; 316 using value_type = const char; 317 using pointer = const char*; 318 using reference = const char&; 319 using difference_type = std::ptrdiff_t; 320 }; 321 using It = input_iterator<const char*, Traits>; 322 static_assert(has_concat<It>(), ""); 323 } 324 { 325 using It = output_iterator<const char*>; 326 static_assert(!has_concat<It>(), ""); 327 } 328 { 329 static_assert(!has_concat<int>(0), ""); 330 // operator+=(int) is well formed since it converts to operator+=(value_type) 331 // but concat(int) isn't valid because there is no concat(value_type). 332 // This should probably be addressed by a LWG issue. 333 static_assert(has_concat_op<int>(), ""); 334 } 335 { 336 static_assert(!has_concat<int*>(), ""); 337 } 338 } 339 340 int main(int, char**) 341 { 342 using namespace fs; 343 for (auto const & TC : Cases) { 344 { 345 path LHS((const char*)TC.lhs); 346 path RHS((const char*)TC.rhs); 347 path& Ref = (LHS += RHS); 348 assert(LHS == (const char*)TC.expect); 349 assert(&Ref == &LHS); 350 } 351 { 352 path LHS((const char*)TC.lhs); 353 std::basic_string_view<path::value_type> RHS((const path::value_type*)TC.rhs); 354 path& Ref = (LHS += RHS); 355 assert(LHS == (const char*)TC.expect); 356 assert(&Ref == &LHS); 357 } 358 doConcatSourceTest<char> (TC); 359 doConcatSourceTest<wchar_t> (TC); 360 doConcatSourceTest<char16_t>(TC); 361 doConcatSourceTest<char32_t>(TC); 362 } 363 for (auto const & TC : LongLHSCases) { 364 // Do path test 365 { 366 path LHS((const char*)TC.lhs); 367 path RHS((const char*)TC.rhs); 368 const char* E = TC.expect; 369 PathReserve(LHS, StrLen(E) + 5); 370 { 371 LIBCPP_ONLY(DisableAllocationGuard g); 372 path& Ref = (LHS += RHS); 373 assert(&Ref == &LHS); 374 } 375 assert(LHS == E); 376 } 377 { 378 path LHS((const char*)TC.lhs); 379 std::basic_string_view<path::value_type> RHS((const path::value_type*)TC.rhs); 380 const char* E = TC.expect; 381 PathReserve(LHS, StrLen(E) + 5); 382 { 383 LIBCPP_ONLY(DisableAllocationGuard g); 384 path& Ref = (LHS += RHS); 385 assert(&Ref == &LHS); 386 } 387 assert(LHS == E); 388 } 389 LIBCPP_ONLY(doConcatSourceAllocTest<char>(TC)); 390 LIBCPP_ONLY(doConcatSourceAllocTest<wchar_t>(TC)); 391 } 392 for (auto const& TC : CharTestCases) { 393 doConcatECharTest<char>(TC); 394 doConcatECharTest<wchar_t>(TC); 395 doConcatECharTest<char16_t>(TC); 396 doConcatECharTest<char32_t>(TC); 397 } 398 test_sfinae(); 399 400 return 0; 401 } 402