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