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