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