Home | History | Annotate | Download | only in path.member
      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