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