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/=(path const&) 21 // template <class Source> 22 // path& operator/=(Source const&); 23 // template <class Source> 24 // path& append(Source const&); 25 // template <class InputIterator> 26 // path& append(InputIterator first, InputIterator last); 27 28 29 #include "filesystem_include.h" 30 #include <type_traits> 31 #include <string_view> 32 #include <cassert> 33 34 // On Windows, the append function converts all inputs (pointers, iterators) 35 // to an intermediate path object, causing allocations in cases where no 36 // allocations are done on other platforms. 37 38 #include "test_macros.h" 39 #include "test_iterators.h" 40 #include "count_new.h" 41 #include "filesystem_test_helper.h" 42 43 44 struct AppendOperatorTestcase { 45 MultiStringType lhs; 46 MultiStringType rhs; 47 MultiStringType expect_posix; 48 MultiStringType expect_windows; 49 50 MultiStringType const& expected_result() const { 51 #ifdef _WIN32 52 return expect_windows; 53 #else 54 return expect_posix; 55 #endif 56 } 57 }; 58 59 #define S(Str) MKSTR(Str) 60 const AppendOperatorTestcase Cases[] = 61 { 62 {S(""), S(""), S(""), S("")} 63 , {S("p1"), S("p2"), S("p1/p2"), S("p1\\p2")} 64 , {S("p1/"), S("p2"), S("p1/p2"), S("p1/p2")} 65 , {S("p1"), S("/p2"), S("/p2"), S("/p2")} 66 , {S("p1/"), S("/p2"), S("/p2"), S("/p2")} 67 , {S("p1"), S("\\p2"), S("p1/\\p2"), S("\\p2")} 68 , {S("p1\\"), S("p2"), S("p1\\/p2"), S("p1\\p2")} 69 , {S("p1\\"), S("\\p2"), S("p1\\/\\p2"), S("\\p2")} 70 , {S(""), S("p2"), S("p2"), S("p2")} 71 , {S("/p1"), S("p2"), S("/p1/p2"), S("/p1\\p2")} 72 , {S("/p1"), S("/p2"), S("/p2"), S("/p2")} 73 , {S("/p1/p3"), S("p2"), S("/p1/p3/p2"), S("/p1/p3\\p2")} 74 , {S("/p1/p3/"), S("p2"), S("/p1/p3/p2"), S("/p1/p3/p2")} 75 , {S("/p1/"), S("p2"), S("/p1/p2"), S("/p1/p2")} 76 , {S("/p1/p3/"), S("/p2/p4"), S("/p2/p4"), S("/p2/p4")} 77 , {S("/"), S(""), S("/"), S("/")} 78 , {S("/p1"), S("/p2/"), S("/p2/"), S("/p2/")} 79 , {S("p1"), S(""), S("p1/"), S("p1\\")} 80 , {S("p1/"), S(""), S("p1/"), S("p1/")} 81 82 , {S("//host"), S("foo"), S("//host/foo"), S("//host\\foo")} 83 , {S("//host/"), S("foo"), S("//host/foo"), S("//host/foo")} 84 , {S("//host"), S(""), S("//host/"), S("//host\\")} 85 86 , {S("foo"), S("C:/bar"), S("foo/C:/bar"), S("C:/bar")} 87 , {S("foo"), S("C:"), S("foo/C:"), S("C:")} 88 89 , {S("C:"), S(""), S("C:/"), S("C:")} 90 , {S("C:foo"), S("/bar"), S("/bar"), S("C:/bar")} 91 , {S("C:foo"), S("bar"), S("C:foo/bar"), S("C:foo\\bar")} 92 , {S("C:/foo"), S("bar"), S("C:/foo/bar"), S("C:/foo\\bar")} 93 , {S("C:/foo"), S("/bar"), S("/bar"), S("C:/bar")} 94 95 , {S("C:foo"), S("C:/bar"), S("C:foo/C:/bar"), S("C:/bar")} 96 , {S("C:foo"), S("C:bar"), S("C:foo/C:bar"), S("C:foo\\bar")} 97 , {S("C:/foo"), S("C:/bar"), S("C:/foo/C:/bar"), S("C:/bar")} 98 , {S("C:/foo"), S("C:bar"), S("C:/foo/C:bar"), S("C:/foo\\bar")} 99 100 , {S("C:foo"), S("c:/bar"), S("C:foo/c:/bar"), S("c:/bar")} 101 , {S("C:foo"), S("c:bar"), S("C:foo/c:bar"), S("c:bar")} 102 , {S("C:/foo"), S("c:/bar"), S("C:/foo/c:/bar"), S("c:/bar")} 103 , {S("C:/foo"), S("c:bar"), S("C:/foo/c:bar"), S("c:bar")} 104 105 , {S("C:/foo"), S("D:bar"), S("C:/foo/D:bar"), S("D:bar")} 106 }; 107 108 109 const AppendOperatorTestcase LongLHSCases[] = 110 { 111 {S("p1"), S("p2"), S("p1/p2"), S("p1\\p2")} 112 , {S("p1/"), S("p2"), S("p1/p2"), S("p1/p2")} 113 , {S("p1"), S("/p2"), S("/p2"), S("/p2")} 114 , {S("/p1"), S("p2"), S("/p1/p2"), S("/p1\\p2")} 115 }; 116 #undef S 117 118 119 // The append operator may need to allocate a temporary buffer before a code_cvt 120 // conversion. Test if this allocation occurs by: 121 // 1. Create a path, `LHS`, and reserve enough space to append `RHS`. 122 // This prevents `LHS` from allocating during the actual appending. 123 // 2. Create a `Source` object `RHS`, which represents a "large" string. 124 // (The string must not trigger the SSO) 125 // 3. Append `RHS` to `LHS` and check for the expected allocation behavior. 126 template <class CharT> 127 void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) 128 { 129 using namespace fs; 130 using Ptr = CharT const*; 131 using Str = std::basic_string<CharT>; 132 using StrView = std::basic_string_view<CharT>; 133 using InputIter = input_iterator<Ptr>; 134 135 const Ptr L = TC.lhs; 136 Str RShort = (Ptr)TC.rhs; 137 Str EShort = (Ptr)TC.expected_result(); 138 assert(RShort.size() >= 2); 139 CharT c = RShort.back(); 140 RShort.append(100, c); 141 EShort.append(100, c); 142 const Ptr R = RShort.data(); 143 const Str& E = EShort; 144 std::size_t ReserveSize = E.size() + 3; 145 // basic_string 146 { 147 path LHS(L); PathReserve(LHS, ReserveSize); 148 Str RHS(R); 149 { 150 TEST_NOT_WIN32(DisableAllocationGuard g); 151 LHS /= RHS; 152 } 153 assert(PathEq(LHS, E)); 154 } 155 // basic_string_view 156 { 157 path LHS(L); PathReserve(LHS, ReserveSize); 158 StrView RHS(R); 159 { 160 TEST_NOT_WIN32(DisableAllocationGuard g); 161 LHS /= RHS; 162 } 163 assert(PathEq(LHS, E)); 164 } 165 // CharT* 166 { 167 path LHS(L); PathReserve(LHS, ReserveSize); 168 Ptr RHS(R); 169 { 170 TEST_NOT_WIN32(DisableAllocationGuard g); 171 LHS /= RHS; 172 } 173 assert(PathEq(LHS, E)); 174 } 175 { 176 path LHS(L); PathReserve(LHS, ReserveSize); 177 Ptr RHS(R); 178 { 179 TEST_NOT_WIN32(DisableAllocationGuard g); 180 LHS.append(RHS, StrEnd(RHS)); 181 } 182 assert(PathEq(LHS, E)); 183 } 184 { 185 path LHS(L); PathReserve(LHS, ReserveSize); 186 path RHS(R); 187 { 188 DisableAllocationGuard g; 189 LHS /= RHS; 190 } 191 assert(PathEq(LHS, E)); 192 } 193 // input iterator - For non-native char types, appends needs to copy the 194 // iterator range into a contiguous block of memory before it can perform the 195 // code_cvt conversions. 196 // For "char" no allocations will be performed because no conversion is 197 // required. 198 // On Windows, the append method is more complex and uses intermediate 199 // path objects, which causes extra allocations. 200 #ifdef _WIN32 201 bool DisableAllocations = false; 202 #else 203 bool DisableAllocations = std::is_same<CharT, char>::value; 204 #endif 205 { 206 path LHS(L); PathReserve(LHS, ReserveSize); 207 InputIter RHS(R); 208 { 209 RequireAllocationGuard g; // requires 1 or more allocations occur by default 210 if (DisableAllocations) g.requireExactly(0); 211 LHS /= RHS; 212 } 213 assert(PathEq(LHS, E)); 214 } 215 { 216 path LHS(L); PathReserve(LHS, ReserveSize); 217 InputIter RHS(R); 218 InputIter REnd(StrEnd(R)); 219 { 220 RequireAllocationGuard g; 221 if (DisableAllocations) g.requireExactly(0); 222 LHS.append(RHS, REnd); 223 } 224 assert(PathEq(LHS, E)); 225 } 226 } 227 228 template <class CharT> 229 void doAppendSourceTest(AppendOperatorTestcase const& TC) 230 { 231 using namespace fs; 232 using Ptr = CharT const*; 233 using Str = std::basic_string<CharT>; 234 using StrView = std::basic_string_view<CharT>; 235 using InputIter = input_iterator<Ptr>; 236 const Ptr L = TC.lhs; 237 const Ptr R = TC.rhs; 238 const Ptr E = TC.expected_result(); 239 // basic_string 240 { 241 path Result(L); 242 Str RHS(R); 243 path& Ref = (Result /= RHS); 244 assert(Result == E); 245 assert(&Ref == &Result); 246 } 247 { 248 path LHS(L); 249 Str RHS(R); 250 path& Ref = LHS.append(RHS); 251 assert(PathEq(LHS, E)); 252 assert(&Ref == &LHS); 253 } 254 // basic_string_view 255 { 256 path LHS(L); 257 StrView RHS(R); 258 path& Ref = (LHS /= RHS); 259 assert(PathEq(LHS, E)); 260 assert(&Ref == &LHS); 261 } 262 { 263 path LHS(L); 264 StrView RHS(R); 265 path& Ref = LHS.append(RHS); 266 assert(PathEq(LHS, E)); 267 assert(&Ref == &LHS); 268 } 269 // Char* 270 { 271 path LHS(L); 272 Str RHS(R); 273 path& Ref = (LHS /= RHS); 274 assert(PathEq(LHS, E)); 275 assert(&Ref == &LHS); 276 } 277 { 278 path LHS(L); 279 Ptr RHS(R); 280 path& Ref = LHS.append(RHS); 281 assert(PathEq(LHS, E)); 282 assert(&Ref == &LHS); 283 } 284 { 285 path LHS(L); 286 Ptr RHS(R); 287 path& Ref = LHS.append(RHS, StrEnd(RHS)); 288 assert(PathEq(LHS, E)); 289 assert(&Ref == &LHS); 290 } 291 // iterators 292 { 293 path LHS(L); 294 InputIter RHS(R); 295 path& Ref = (LHS /= RHS); 296 assert(PathEq(LHS, E)); 297 assert(&Ref == &LHS); 298 } 299 { 300 path LHS(L); InputIter RHS(R); 301 path& Ref = LHS.append(RHS); 302 assert(PathEq(LHS, E)); 303 assert(&Ref == &LHS); 304 } 305 { 306 path LHS(L); 307 InputIter RHS(R); 308 InputIter REnd(StrEnd(R)); 309 path& Ref = LHS.append(RHS, REnd); 310 assert(PathEq(LHS, E)); 311 assert(&Ref == &LHS); 312 } 313 } 314 315 316 317 template <class It, class = decltype(fs::path{}.append(std::declval<It>()))> 318 constexpr bool has_append(int) { return true; } 319 template <class It> 320 constexpr bool has_append(long) { return false; } 321 322 template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))> 323 constexpr bool has_append_op(int) { return true; } 324 template <class It> 325 constexpr bool has_append_op(long) { return false; } 326 327 template <class It> 328 constexpr bool has_append() { 329 static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same"); 330 return has_append<It>(0) && has_append_op<It>(0); 331 } 332 333 void test_sfinae() 334 { 335 using namespace fs; 336 { 337 using It = const char* const; 338 static_assert(has_append<It>(), ""); 339 } 340 { 341 using It = input_iterator<const char*>; 342 static_assert(has_append<It>(), ""); 343 } 344 { 345 struct Traits { 346 using iterator_category = std::input_iterator_tag; 347 using value_type = const char; 348 using pointer = const char*; 349 using reference = const char&; 350 using difference_type = std::ptrdiff_t; 351 }; 352 using It = input_iterator<const char*, Traits>; 353 static_assert(has_append<It>(), ""); 354 } 355 { 356 using It = output_iterator<const char*>; 357 static_assert(!has_append<It>(), ""); 358 359 } 360 { 361 static_assert(!has_append<int*>(), ""); 362 } 363 { 364 static_assert(!has_append<char>(), ""); 365 static_assert(!has_append<const char>(), ""); 366 } 367 } 368 369 int main(int, char**) 370 { 371 using namespace fs; 372 for (auto const & TC : Cases) { 373 { 374 const char* LHS_In = TC.lhs; 375 const char* RHS_In = TC.rhs; 376 path LHS(LHS_In); 377 path RHS(RHS_In); 378 path& Res = (LHS /= RHS); 379 assert(PathEq(Res, (const char*)TC.expected_result())); 380 assert(&Res == &LHS); 381 } 382 doAppendSourceTest<char> (TC); 383 doAppendSourceTest<wchar_t> (TC); 384 doAppendSourceTest<char16_t>(TC); 385 doAppendSourceTest<char32_t>(TC); 386 } 387 for (auto const & TC : LongLHSCases) { 388 (void)TC; 389 LIBCPP_ONLY(doAppendSourceAllocTest<char>(TC)); 390 LIBCPP_ONLY(doAppendSourceAllocTest<wchar_t>(TC)); 391 } 392 test_sfinae(); 393 394 return 0; 395 } 396