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