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