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>
doAppendSourceAllocTest(AppendOperatorTestcase const & TC)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>
doAppendSourceTest(AppendOperatorTestcase const & TC)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>()))>
has_append(int)264 constexpr bool has_append(int) { return true; }
265 template <class It>
has_append(long)266 constexpr bool has_append(long) { return false; }
267
268 template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))>
has_append_op(int)269 constexpr bool has_append_op(int) { return true; }
270 template <class It>
has_append_op(long)271 constexpr bool has_append_op(long) { return false; }
272
273 template <class It>
has_append()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
test_sfinae()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
main()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 }
340