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 // <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 "filesystem_include.hpp"
     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 
     43 struct ConcatOperatorTestcase {
     44   MultiStringType lhs;
     45   MultiStringType rhs;
     46   MultiStringType expect;
     47 };
     48 
     49 #define LONGSTR "LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR"
     50 #define S(Str) MKSTR(Str)
     51 const ConcatOperatorTestcase Cases[] =
     52     {
     53         {S(""),         S(""),                  S("")}
     54       , {S("p1"),       S("p2"),                S("p1p2")}
     55       , {S("p1/"),      S("/p2"),               S("p1//p2")}
     56       , {S(""),         S("\\foo/bar/baz"),     S("\\foo/bar/baz")}
     57       , {S("c:\\foo"),  S(""),                  S("c:\\foo")}
     58       , {S(LONGSTR),    S("foo"),               S(LONGSTR "foo")}
     59       , {S("abcdefghijklmnopqrstuvwxyz/\\"), S("/\\123456789"), S("abcdefghijklmnopqrstuvwxyz/\\/\\123456789")}
     60     };
     61 const ConcatOperatorTestcase LongLHSCases[] =
     62     {
     63         {S(""),        S(LONGSTR),     S(LONGSTR)}
     64       , {S("p1/"),     S(LONGSTR),      S("p1/" LONGSTR)}
     65     };
     66 const ConcatOperatorTestcase CharTestCases[] =
     67     {
     68         {S(""),       S("P"), S("P")}
     69       , {S("/fooba"), S("r"), S("/foobar")}
     70     };
     71 #undef S
     72 #undef LONGSTR
     73 
     74 // The concat operator may need to allocate a temporary buffer before a code_cvt
     75 // conversion. Test if this allocation occurs by:
     76 //   1. Create a path, `LHS`, and reserve enough space to append `RHS`.
     77 //      This prevents `LHS` from allocating during the actual appending.
     78 //   2. Create a `Source` object `RHS`, which represents a "large" string.
     79 //      (The string must not trigger the SSO)
     80 //   3. Concat `RHS` to `LHS` and check for the expected allocation behavior.
     81 template <class CharT>
     82 void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC)
     83 {
     84   using namespace fs;
     85   using Ptr = CharT const*;
     86   using Str = std::basic_string<CharT>;
     87   using StrView = std::basic_string_view<CharT>;
     88   using InputIter = input_iterator<Ptr>;
     89 
     90   const Ptr L = TC.lhs;
     91   const Ptr R = TC.rhs;
     92   const Ptr E =  TC.expect;
     93   std::size_t ReserveSize = StrLen(E) + 1;
     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.concat(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.concat(RHS, REnd);
    157     }
    158     assert(LHS == E);
    159   }
    160 }
    161 
    162 template <class CharT>
    163 void doConcatSourceTest(ConcatOperatorTestcase 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.concat(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.concat(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.concat(RHS);
    215     assert(LHS == E);
    216     assert(&Ref == &LHS);
    217   }
    218   {
    219     path LHS(L);
    220     Ptr RHS(R);
    221     path& Ref = LHS.concat(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.concat(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.concat(RHS, REnd);
    244     assert(LHS == E);
    245     assert(&Ref == &LHS);
    246   }
    247 }
    248 
    249 template <class CharT>
    250 void doConcatECharTest(ConcatOperatorTestcase const& TC)
    251 {
    252   using namespace fs;
    253   using Ptr = CharT const*;
    254   const Ptr RStr = TC.rhs;
    255   assert(StrLen(RStr) == 1);
    256   const Ptr L   = TC.lhs;
    257   const CharT R = RStr[0];
    258   const Ptr E   = TC.expect;
    259   {
    260     path LHS(L);
    261     path& Ref = (LHS += R);
    262     assert(LHS == E);
    263     assert(&Ref == &LHS);
    264   }
    265 }
    266 
    267 
    268 template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>
    269 constexpr bool has_concat(int) { return true; }
    270 template <class It>
    271 constexpr bool has_concat(long) { return false; }
    272 
    273 template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>
    274 constexpr bool has_concat_op(int) { return true; }
    275 template <class It>
    276 constexpr bool has_concat_op(long) { return false; }
    277 template <class It>
    278 constexpr bool has_concat_op() { return has_concat_op<It>(0); }
    279 
    280 template <class It>
    281 constexpr bool has_concat() {
    282   static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same");
    283   return has_concat<It>(0) && has_concat_op<It>(0);
    284 }
    285 
    286 void test_sfinae() {
    287   using namespace fs;
    288   {
    289     static_assert(has_concat_op<char>(), "");
    290     static_assert(has_concat_op<const char>(), "");
    291     static_assert(has_concat_op<char16_t>(), "");
    292     static_assert(has_concat_op<const char16_t>(), "");
    293   }
    294   {
    295     using It = const char* const;
    296     static_assert(has_concat<It>(), "");
    297   }
    298   {
    299     using It = input_iterator<const char*>;
    300     static_assert(has_concat<It>(), "");
    301   }
    302   {
    303     struct Traits {
    304       using iterator_category = std::input_iterator_tag;
    305       using value_type = const char;
    306       using pointer = const char*;
    307       using reference = const char&;
    308       using difference_type = std::ptrdiff_t;
    309     };
    310     using It = input_iterator<const char*, Traits>;
    311     static_assert(has_concat<It>(), "");
    312   }
    313   {
    314     using It = output_iterator<const char*>;
    315     static_assert(!has_concat<It>(), "");
    316   }
    317   {
    318     static_assert(!has_concat<int>(0), "");
    319     // operator+=(int) is well formed since it converts to operator+=(value_type)
    320     // but concat(int) isn't valid because there is no concat(value_type).
    321     // This should probably be addressed by a LWG issue.
    322     static_assert(has_concat_op<int>(), "");
    323   }
    324   {
    325     static_assert(!has_concat<int*>(), "");
    326   }
    327 }
    328 
    329 int main()
    330 {
    331   using namespace fs;
    332   for (auto const & TC : Cases) {
    333     {
    334       path LHS((const char*)TC.lhs);
    335       path RHS((const char*)TC.rhs);
    336       path& Ref = (LHS += RHS);
    337       assert(LHS == (const char*)TC.expect);
    338       assert(&Ref == &LHS);
    339     }
    340     {
    341       path LHS((const char*)TC.lhs);
    342       std::string_view RHS((const char*)TC.rhs);
    343       path& Ref = (LHS += RHS);
    344       assert(LHS == (const char*)TC.expect);
    345       assert(&Ref == &LHS);
    346     }
    347     doConcatSourceTest<char>    (TC);
    348     doConcatSourceTest<wchar_t> (TC);
    349     doConcatSourceTest<char16_t>(TC);
    350     doConcatSourceTest<char32_t>(TC);
    351   }
    352   for (auto const & TC : LongLHSCases) {
    353     // Do path test
    354     {
    355       path LHS((const char*)TC.lhs);
    356       path RHS((const char*)TC.rhs);
    357       const char* E = TC.expect;
    358       PathReserve(LHS, StrLen(E) + 5);
    359       {
    360         DisableAllocationGuard g;
    361         path& Ref = (LHS += RHS);
    362         assert(&Ref == &LHS);
    363       }
    364       assert(LHS == E);
    365     }
    366     {
    367       path LHS((const char*)TC.lhs);
    368       std::string_view RHS((const char*)TC.rhs);
    369       const char* E = TC.expect;
    370       PathReserve(LHS, StrLen(E) + 5);
    371       {
    372         DisableAllocationGuard g;
    373         path& Ref = (LHS += RHS);
    374         assert(&Ref == &LHS);
    375       }
    376       assert(LHS == E);
    377     }
    378     doConcatSourceAllocTest<char>(TC);
    379     doConcatSourceAllocTest<wchar_t>(TC);
    380   }
    381   for (auto const& TC : CharTestCases) {
    382     doConcatECharTest<char>(TC);
    383     doConcatECharTest<wchar_t>(TC);
    384     doConcatECharTest<char16_t>(TC);
    385     doConcatECharTest<char32_t>(TC);
    386   }
    387   test_sfinae();
    388 }
    389