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+=(const path& x);
     17 // path& operator+=(const string_type& x);
     18 // path& operator+=(string_view x);
     19 // path& operator+=(const value_type* x);
     20 // path& operator+=(value_type x);
     21 // template <class Source>
     22 //   path& operator+=(const Source& x);
     23 // template <class EcharT>
     24 //   path& operator+=(EcharT x);
     25 // template <class Source>
     26 //   path& concat(const Source& x);
     27 // template <class InputIterator>
     28 //   path& concat(InputIterator first, InputIterator last);
     29 
     30 
     31 #include <experimental/filesystem>
     32 #include <type_traits>
     33 #include <string>
     34 #include <string_view>
     35 #include <cassert>
     36 
     37 #include "test_macros.h"
     38 #include "test_iterators.h"
     39 #include "count_new.hpp"
     40 #include "filesystem_test_helper.hpp"
     41 
     42 namespace fs = std::experimental::filesystem;
     43 
     44 struct ConcatOperatorTestcase {
     45   MultiStringType lhs;
     46   MultiStringType rhs;
     47   MultiStringType expect;
     48 };
     49 
     50 #define LONGSTR "LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR"
     51 #define S(Str) MKSTR(Str)
     52 const ConcatOperatorTestcase Cases[] =
     53     {
     54         {S(""),         S(""),                  S("")}
     55       , {S("p1"),       S("p2"),                S("p1p2")}
     56       , {S("p1/"),      S("/p2"),               S("p1//p2")}
     57       , {S(""),         S("\\foo/bar/baz"),     S("\\foo/bar/baz")}
     58       , {S("c:\\foo"),  S(""),                  S("c:\\foo")}
     59       , {S(LONGSTR),    S("foo"),               S(LONGSTR "foo")}
     60       , {S("abcdefghijklmnopqrstuvwxyz/\\"), S("/\\123456789"), S("abcdefghijklmnopqrstuvwxyz/\\/\\123456789")}
     61     };
     62 const ConcatOperatorTestcase LongLHSCases[] =
     63     {
     64         {S(""),        S(LONGSTR),     S(LONGSTR)}
     65       , {S("p1/"),     S(LONGSTR),      S("p1/" LONGSTR)}
     66     };
     67 const ConcatOperatorTestcase CharTestCases[] =
     68     {
     69         {S(""),       S("P"), S("P")}
     70       , {S("/fooba"), S("r"), S("/foobar")}
     71     };
     72 #undef S
     73 #undef LONGSTR
     74 
     75 // The concat operator may need to allocate a temporary buffer before a code_cvt
     76 // conversion. Test if this allocation occurs by:
     77 //   1. Create a path, `LHS`, and reserve enough space to append `RHS`.
     78 //      This prevents `LHS` from allocating during the actual appending.
     79 //   2. Create a `Source` object `RHS`, which represents a "large" string.
     80 //      (The string must not trigger the SSO)
     81 //   3. Concat `RHS` to `LHS` and check for the expected allocation behavior.
     82 template <class CharT>
     83 void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC)
     84 {
     85   using namespace fs;
     86   using Ptr = CharT const*;
     87   using Str = std::basic_string<CharT>;
     88   using StrView = std::basic_string_view<CharT>;
     89   using InputIter = input_iterator<Ptr>;
     90 
     91   const Ptr L = TC.lhs;
     92   const Ptr R = TC.rhs;
     93   const Ptr E =  TC.expect;
     94   std::size_t ReserveSize = StrLen(E) + 1;
     95   // basic_string
     96   {
     97     path LHS(L); PathReserve(LHS, ReserveSize);
     98     Str  RHS(R);
     99     {
    100       DisableAllocationGuard g;
    101       LHS += RHS;
    102     }
    103     assert(LHS == E);
    104   }
    105   // basic_string_view
    106   {
    107     path LHS(L); PathReserve(LHS, ReserveSize);
    108     StrView  RHS(R);
    109     {
    110       DisableAllocationGuard g;
    111       LHS += RHS;
    112     }
    113     assert(LHS == E);
    114   }
    115   // CharT*
    116   {
    117     path LHS(L); PathReserve(LHS, ReserveSize);
    118     Ptr RHS(R);
    119     {
    120       DisableAllocationGuard g;
    121       LHS += RHS;
    122     }
    123     assert(LHS == E);
    124   }
    125   {
    126     path LHS(L); PathReserve(LHS, ReserveSize);
    127     Ptr RHS(R);
    128     {
    129       DisableAllocationGuard g;
    130       LHS.concat(RHS, StrEnd(RHS));
    131     }
    132     assert(LHS == E);
    133   }
    134   // input iterator - For non-native char types, appends needs to copy the
    135   // iterator range into a contiguous block of memory before it can perform the
    136   // code_cvt conversions.
    137   // For "char" no allocations will be performed because no conversion is
    138   // required.
    139   bool DisableAllocations = std::is_same<CharT, char>::value;
    140   {
    141     path LHS(L); PathReserve(LHS, ReserveSize);
    142     InputIter RHS(R);
    143     {
    144       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
    145       if (DisableAllocations) g.requireExactly(0);
    146       LHS += RHS;
    147     }
    148     assert(LHS == E);
    149   }
    150   {
    151     path LHS(L); PathReserve(LHS, ReserveSize);
    152     InputIter RHS(R);
    153     InputIter REnd(StrEnd(R));
    154     {
    155       RequireAllocationGuard g;
    156       if (DisableAllocations) g.requireExactly(0);
    157       LHS.concat(RHS, REnd);
    158     }
    159     assert(LHS == E);
    160   }
    161 }
    162 
    163 template <class CharT>
    164 void doConcatSourceTest(ConcatOperatorTestcase const& TC)
    165 {
    166   using namespace fs;
    167   using Ptr = CharT const*;
    168   using Str = std::basic_string<CharT>;
    169   using StrView = std::basic_string_view<CharT>;
    170   using InputIter = input_iterator<Ptr>;
    171   const Ptr L = TC.lhs;
    172   const Ptr R = TC.rhs;
    173   const Ptr E = TC.expect;
    174   // basic_string
    175   {
    176     path LHS(L);
    177     Str RHS(R);
    178     path& Ref = (LHS += RHS);
    179     assert(LHS == E);
    180     assert(&Ref == &LHS);
    181   }
    182   {
    183     path LHS(L);
    184     Str RHS(R);
    185     path& Ref = LHS.concat(RHS);
    186     assert(LHS == E);
    187     assert(&Ref == &LHS);
    188   }
    189   // basic_string_view
    190   {
    191     path LHS(L);
    192     StrView RHS(R);
    193     path& Ref = (LHS += RHS);
    194     assert(LHS == E);
    195     assert(&Ref == &LHS);
    196   }
    197   {
    198     path LHS(L);
    199     StrView RHS(R);
    200     path& Ref = LHS.concat(RHS);
    201     assert(LHS == E);
    202     assert(&Ref == &LHS);
    203   }
    204   // Char*
    205   {
    206     path LHS(L);
    207     Str RHS(R);
    208     path& Ref = (LHS += RHS);
    209     assert(LHS == E);
    210     assert(&Ref == &LHS);
    211   }
    212   {
    213     path LHS(L);
    214     Ptr RHS(R);
    215     path& Ref = LHS.concat(RHS);
    216     assert(LHS == E);
    217     assert(&Ref == &LHS);
    218   }
    219   {
    220     path LHS(L);
    221     Ptr RHS(R);
    222     path& Ref = LHS.concat(RHS, StrEnd(RHS));
    223     assert(LHS == E);
    224     assert(&Ref == &LHS);
    225   }
    226   // iterators
    227   {
    228     path LHS(L);
    229     InputIter RHS(R);
    230     path& Ref = (LHS += RHS);
    231     assert(LHS == E);
    232     assert(&Ref == &LHS);
    233   }
    234   {
    235     path LHS(L); InputIter RHS(R);
    236     path& Ref = LHS.concat(RHS);
    237     assert(LHS == E);
    238     assert(&Ref == &LHS);
    239   }
    240   {
    241     path LHS(L);
    242     InputIter RHS(R);
    243     InputIter REnd(StrEnd(R));
    244     path& Ref = LHS.concat(RHS, REnd);
    245     assert(LHS == E);
    246     assert(&Ref == &LHS);
    247   }
    248 }
    249 
    250 template <class CharT>
    251 void doConcatECharTest(ConcatOperatorTestcase const& TC)
    252 {
    253   using namespace fs;
    254   using Ptr = CharT const*;
    255   const Ptr RStr = TC.rhs;
    256   assert(StrLen(RStr) == 1);
    257   const Ptr L   = TC.lhs;
    258   const CharT R = RStr[0];
    259   const Ptr E   = TC.expect;
    260   {
    261     path LHS(L);
    262     path& Ref = (LHS += R);
    263     assert(LHS == E);
    264     assert(&Ref == &LHS);
    265   }
    266 }
    267 
    268 
    269 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>
    270 constexpr bool has_concat(int) { return true; }
    271 template <class It>
    272 constexpr bool has_concat(long) { return false; }
    273 
    274 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>
    275 constexpr bool has_concat_op(int) { return true; }
    276 template <class It>
    277 constexpr bool has_concat_op(long) { return false; }
    278 template <class It>
    279 constexpr bool has_concat_op() { return has_concat_op<It>(0); }
    280 
    281 template <class It>
    282 constexpr bool has_concat() {
    283   static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same");
    284   return has_concat<It>(0) && has_concat_op<It>(0);
    285 }
    286 
    287 void test_sfinae() {
    288   using namespace fs;
    289   {
    290     static_assert(has_concat_op<char>(), "");
    291     static_assert(has_concat_op<const char>(), "");
    292     static_assert(has_concat_op<char16_t>(), "");
    293     static_assert(has_concat_op<const char16_t>(), "");
    294   }
    295   {
    296     using It = const char* const;
    297     static_assert(has_concat<It>(), "");
    298   }
    299   {
    300     using It = input_iterator<const char*>;
    301     static_assert(has_concat<It>(), "");
    302   }
    303   {
    304     struct Traits {
    305       using iterator_category = std::input_iterator_tag;
    306       using value_type = const char;
    307       using pointer = const char*;
    308       using reference = const char&;
    309       using difference_type = std::ptrdiff_t;
    310     };
    311     using It = input_iterator<const char*, Traits>;
    312     static_assert(has_concat<It>(), "");
    313   }
    314   {
    315     using It = output_iterator<const char*>;
    316     static_assert(!has_concat<It>(), "");
    317   }
    318   {
    319     static_assert(!has_concat<int>(0), "");
    320     // operator+=(int) is well formed since it converts to operator+=(value_type)
    321     // but concat(int) isn't valid because there is no concat(value_type).
    322     // This should probably be addressed by a LWG issue.
    323     static_assert(has_concat_op<int>(), "");
    324   }
    325   {
    326     static_assert(!has_concat<int*>(), "");
    327   }
    328 }
    329 
    330 int main()
    331 {
    332   using namespace fs;
    333   for (auto const & TC : Cases) {
    334     {
    335       path LHS((const char*)TC.lhs);
    336       path RHS((const char*)TC.rhs);
    337       path& Ref = (LHS += RHS);
    338       assert(LHS == (const char*)TC.expect);
    339       assert(&Ref == &LHS);
    340     }
    341     {
    342       path LHS((const char*)TC.lhs);
    343       std::string_view RHS((const char*)TC.rhs);
    344       path& Ref = (LHS += RHS);
    345       assert(LHS == (const char*)TC.expect);
    346       assert(&Ref == &LHS);
    347     }
    348     doConcatSourceTest<char>    (TC);
    349     doConcatSourceTest<wchar_t> (TC);
    350     doConcatSourceTest<char16_t>(TC);
    351     doConcatSourceTest<char32_t>(TC);
    352   }
    353   for (auto const & TC : LongLHSCases) {
    354     // Do path test
    355     {
    356       path LHS((const char*)TC.lhs);
    357       path RHS((const char*)TC.rhs);
    358       const char* E = TC.expect;
    359       PathReserve(LHS, StrLen(E) + 5);
    360       {
    361         DisableAllocationGuard g;
    362         path& Ref = (LHS += RHS);
    363         assert(&Ref == &LHS);
    364       }
    365       assert(LHS == E);
    366     }
    367     {
    368       path LHS((const char*)TC.lhs);
    369       std::string_view RHS((const char*)TC.rhs);
    370       const char* E = TC.expect;
    371       PathReserve(LHS, StrLen(E) + 5);
    372       {
    373         DisableAllocationGuard g;
    374         path& Ref = (LHS += RHS);
    375         assert(&Ref == &LHS);
    376       }
    377       assert(LHS == E);
    378     }
    379     doConcatSourceAllocTest<char>(TC);
    380     doConcatSourceAllocTest<wchar_t>(TC);
    381   }
    382   for (auto const& TC : CharTestCases) {
    383     doConcatECharTest<char>(TC);
    384     doConcatECharTest<wchar_t>(TC);
    385     doConcatECharTest<char16_t>(TC);
    386     doConcatECharTest<char32_t>(TC);
    387   }
    388   test_sfinae();
    389 }
    390