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