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/=(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 <experimental/filesystem> 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 35 namespace fs = std::experimental::filesystem; 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("p1/p2")} 50 , {S("p1/"), S("/p2"), S("p1//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("p1"), S(""), S("p1")} 55 , {S(""), S("p2"), S("p2")} 56 }; 57 58 59 const AppendOperatorTestcase LongLHSCases[] = 60 { 61 {S("p1"), S("p2"), S("p1/p2")} 62 , {S("p1/"), S("p2"), S("p1/p2")} 63 , {S("p1"), S("/p2"), S("p1/p2")} 64 }; 65 #undef S 66 67 68 // The append operator may need to allocate a temporary buffer before a code_cvt 69 // conversion. Test if this allocation occurs by: 70 // 1. Create a path, `LHS`, and reserve enough space to append `RHS`. 71 // This prevents `LHS` from allocating during the actual appending. 72 // 2. Create a `Source` object `RHS`, which represents a "large" string. 73 // (The string must not trigger the SSO) 74 // 3. Append `RHS` to `LHS` and check for the expected allocation behavior. 75 template <class CharT> 76 void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) 77 { 78 using namespace fs; 79 using Ptr = CharT const*; 80 using Str = std::basic_string<CharT>; 81 using StrView = std::basic_string_view<CharT>; 82 using InputIter = input_iterator<Ptr>; 83 84 const Ptr L = TC.lhs; 85 Str RShort = (Ptr)TC.rhs; 86 Str EShort = (Ptr)TC.expect; 87 assert(RShort.size() >= 2); 88 CharT c = RShort.back(); 89 RShort.append(100, c); 90 EShort.append(100, c); 91 const Ptr R = RShort.data(); 92 const Str& E = EShort; 93 std::size_t ReserveSize = E.size() + 3; 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.append(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.append(RHS, REnd); 157 } 158 assert(LHS == E); 159 } 160 } 161 162 template <class CharT> 163 void doAppendSourceTest(AppendOperatorTestcase 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.append(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.append(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.append(RHS); 215 assert(LHS == E); 216 assert(&Ref == &LHS); 217 } 218 { 219 path LHS(L); 220 Ptr RHS(R); 221 path& Ref = LHS.append(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.append(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.append(RHS, REnd); 244 assert(LHS == E); 245 assert(&Ref == &LHS); 246 } 247 } 248 249 250 251 template <class It, class = decltype(fs::path{}.append(std::declval<It>()))> 252 constexpr bool has_append(int) { return true; } 253 template <class It> 254 constexpr bool has_append(long) { return false; } 255 256 template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))> 257 constexpr bool has_append_op(int) { return true; } 258 template <class It> 259 constexpr bool has_append_op(long) { return false; } 260 261 template <class It> 262 constexpr bool has_append() { 263 static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same"); 264 return has_append<It>(0) && has_append_op<It>(0); 265 } 266 267 void test_sfinae() 268 { 269 using namespace fs; 270 { 271 using It = const char* const; 272 static_assert(has_append<It>(), ""); 273 } 274 { 275 using It = input_iterator<const char*>; 276 static_assert(has_append<It>(), ""); 277 } 278 { 279 struct Traits { 280 using iterator_category = std::input_iterator_tag; 281 using value_type = const char; 282 using pointer = const char*; 283 using reference = const char&; 284 using difference_type = std::ptrdiff_t; 285 }; 286 using It = input_iterator<const char*, Traits>; 287 static_assert(has_append<It>(), ""); 288 } 289 { 290 using It = output_iterator<const char*>; 291 static_assert(!has_append<It>(), ""); 292 293 } 294 { 295 static_assert(!has_append<int*>(), ""); 296 } 297 { 298 static_assert(!has_append<char>(), ""); 299 static_assert(!has_append<const char>(), ""); 300 } 301 } 302 303 int main() 304 { 305 using namespace fs; 306 for (auto const & TC : Cases) { 307 { 308 path LHS((const char*)TC.lhs); 309 path RHS((const char*)TC.rhs); 310 path& Ref = (LHS /= RHS); 311 assert(LHS == (const char*)TC.expect); 312 assert(&Ref == &LHS); 313 } 314 doAppendSourceTest<char> (TC); 315 doAppendSourceTest<wchar_t> (TC); 316 doAppendSourceTest<char16_t>(TC); 317 doAppendSourceTest<char32_t>(TC); 318 } 319 for (auto const & TC : LongLHSCases) { 320 doAppendSourceAllocTest<char>(TC); 321 doAppendSourceAllocTest<wchar_t>(TC); 322 } 323 test_sfinae(); 324 } 325