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