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
35namespace fs = std::experimental::filesystem;
36
37struct AppendOperatorTestcase {
38  MultiStringType lhs;
39  MultiStringType rhs;
40  MultiStringType expect;
41};
42
43#define S(Str) MKSTR(Str)
44const 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
59const 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.
75template <class CharT>
76void 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
162template <class CharT>
163void 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
251template <class It, class = decltype(fs::path{}.append(std::declval<It>()))>
252constexpr bool has_append(int) { return true; }
253template <class It>
254constexpr bool has_append(long) { return false; }
255
256template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))>
257constexpr bool has_append_op(int) { return true; }
258template <class It>
259constexpr bool has_append_op(long) { return false; }
260
261template <class It>
262constexpr 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
267void 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
303int 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