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>
doAppendSourceAllocTest(AppendOperatorTestcase const & TC)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>
doAppendSourceTest(AppendOperatorTestcase const & TC)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>()))>
has_append(int)252 constexpr bool has_append(int) { return true; }
253 template <class It>
has_append(long)254 constexpr bool has_append(long) { return false; }
255
256 template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))>
has_append_op(int)257 constexpr bool has_append_op(int) { return true; }
258 template <class It>
has_append_op(long)259 constexpr bool has_append_op(long) { return false; }
260
261 template <class It>
has_append()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
test_sfinae()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
main()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