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