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