brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.1 KiB · 65c4dab Raw
412 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++1410 11// These tests require locale for non-char paths12// UNSUPPORTED: no-localization13 14// In MinGW mode, with optimizations enabled with a DLL, the number of counted15// allocations mismatches, as some ctor/dtor calls are generated in the16// calling code, and some are called from the DLL.17// ADDITIONAL_COMPILE_FLAGS: -DALLOW_MISMATCHING_LIBRRARY_INTERNAL_ALLOCATIONS18 19// <filesystem>20 21// class path22 23// path& operator/=(path const&)24// template <class Source>25//      path& operator/=(Source const&);26// template <class Source>27//      path& append(Source const&);28// template <class InputIterator>29//      path& append(InputIterator first, InputIterator last);30 31#include <filesystem>32#include <type_traits>33#include <string_view>34#include <cassert>35#include <utility>36 37// On Windows, the append function converts all inputs (pointers, iterators)38// to an intermediate path object, causing allocations in cases where no39// allocations are done on other platforms.40 41#include "../path_helper.h"42#include "count_new.h"43#include "make_string.h"44#include "test_iterators.h"45#include "test_macros.h"46namespace fs = std::filesystem;47 48struct AppendOperatorTestcase {49  MultiStringType lhs;50  MultiStringType rhs;51  MultiStringType expect_posix;52  MultiStringType expect_windows;53 54  MultiStringType const& expected_result() const {55#ifdef _WIN3256    return expect_windows;57#else58    return expect_posix;59#endif60  }61};62 63#define S(Str) MKSTR(Str)64const AppendOperatorTestcase Cases[] =65    {66        {S(""),        S(""),         S(""),              S("")}67      , {S("p1"),      S("p2"),       S("p1/p2"),         S("p1\\p2")}68      , {S("p1/"),     S("p2"),       S("p1/p2"),         S("p1/p2")}69      , {S("p1"),      S("/p2"),      S("/p2"),           S("/p2")}70      , {S("p1/"),     S("/p2"),      S("/p2"),           S("/p2")}71      , {S("p1"),      S("\\p2"),     S("p1/\\p2"),       S("\\p2")}72      , {S("p1\\"),    S("p2"),       S("p1\\/p2"),       S("p1\\p2")}73      , {S("p1\\"),    S("\\p2"),     S("p1\\/\\p2"),     S("\\p2")}74      , {S(""),        S("p2"),       S("p2"),            S("p2")}75      , {S("/p1"),     S("p2"),       S("/p1/p2"),        S("/p1\\p2")}76      , {S("/p1"),     S("/p2"),      S("/p2"),           S("/p2")}77      , {S("/p1/p3"),  S("p2"),       S("/p1/p3/p2"),     S("/p1/p3\\p2")}78      , {S("/p1/p3/"), S("p2"),       S("/p1/p3/p2"),     S("/p1/p3/p2")}79      , {S("/p1/"),    S("p2"),       S("/p1/p2"),        S("/p1/p2")}80      , {S("/p1/p3/"), S("/p2/p4"),   S("/p2/p4"),        S("/p2/p4")}81      , {S("/"),       S(""),         S("/"),             S("/")}82      , {S("/p1"),     S("/p2/"),     S("/p2/"),          S("/p2/")}83      , {S("p1"),      S(""),         S("p1/"),           S("p1\\")}84      , {S("p1/"),     S(""),         S("p1/"),           S("p1/")}85 86      , {S("//host"),  S("foo"),      S("//host/foo"),    S("//host\\foo")}87      , {S("//host/"), S("foo"),      S("//host/foo"),    S("//host/foo")}88      , {S("//host"),  S(""),         S("//host/"),       S("//host\\")}89 90      , {S("foo"),     S("C:/bar"),   S("foo/C:/bar"),    S("C:/bar")}91      , {S("foo"),     S("C:"),       S("foo/C:"),        S("C:")}92 93      , {S("C:"),      S(""),         S("C:/"),           S("C:")}94      , {S("C:foo"),   S("/bar"),     S("/bar"),          S("C:/bar")}95      , {S("C:foo"),   S("bar"),      S("C:foo/bar"),     S("C:foo\\bar")}96      , {S("C:/foo"),  S("bar"),      S("C:/foo/bar"),    S("C:/foo\\bar")}97      , {S("C:/foo"),  S("/bar"),     S("/bar"),          S("C:/bar")}98 99      , {S("C:foo"),   S("C:/bar"),   S("C:foo/C:/bar"),  S("C:/bar")}100      , {S("C:foo"),   S("C:bar"),    S("C:foo/C:bar"),   S("C:foo\\bar")}101      , {S("C:/foo"),  S("C:/bar"),   S("C:/foo/C:/bar"), S("C:/bar")}102      , {S("C:/foo"),  S("C:bar"),    S("C:/foo/C:bar"),  S("C:/foo\\bar")}103 104      , {S("C:foo"),   S("c:/bar"),   S("C:foo/c:/bar"),  S("c:/bar")}105      , {S("C:foo"),   S("c:bar"),    S("C:foo/c:bar"),   S("c:bar")}106      , {S("C:/foo"),  S("c:/bar"),   S("C:/foo/c:/bar"), S("c:/bar")}107      , {S("C:/foo"),  S("c:bar"),    S("C:/foo/c:bar"),  S("c:bar")}108 109      , {S("C:/foo"),  S("D:bar"),    S("C:/foo/D:bar"),  S("D:bar")}110    };111 112 113const AppendOperatorTestcase LongLHSCases[] =114    {115        {S("p1"),   S("p2"),    S("p1/p2"),  S("p1\\p2")}116      , {S("p1/"),  S("p2"),    S("p1/p2"),  S("p1/p2")}117      , {S("p1"),   S("/p2"),   S("/p2"),    S("/p2")}118      , {S("/p1"),  S("p2"),    S("/p1/p2"), S("/p1\\p2")}119    };120#undef S121 122 123// The append operator may need to allocate a temporary buffer before a code_cvt124// conversion. Test if this allocation occurs by:125//   1. Create a path, `LHS`, and reserve enough space to append `RHS`.126//      This prevents `LHS` from allocating during the actual appending.127//   2. Create a `Source` object `RHS`, which represents a "large" string.128//      (The string must not trigger the SSO)129//   3. Append `RHS` to `LHS` and check for the expected allocation behavior.130template <class CharT>131void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)132{133  using namespace fs;134  using Ptr = CharT const*;135  using Str = std::basic_string<CharT>;136  using StrView = std::basic_string_view<CharT>;137  using InputIter = cpp17_input_iterator<Ptr>;138 139  const Ptr L = TC.lhs;140  Str RShort = (Ptr)TC.rhs;141  Str EShort = (Ptr)TC.expected_result();142  assert(RShort.size() >= 2);143  CharT c = RShort.back();144  RShort.append(100, c);145  EShort.append(100, c);146  const Ptr R = RShort.data();147  const Str& E = EShort;148  std::size_t ReserveSize = E.size() + 3;149  // basic_string150  {151    path LHS(L); PathReserve(LHS, ReserveSize);152    Str  RHS(R);153    {154      TEST_NOT_WIN32(DisableAllocationGuard g);155      LHS /= RHS;156    }157    assert(PathEq(LHS, E));158  }159  // basic_string_view160  {161    path LHS(L); PathReserve(LHS, ReserveSize);162    StrView  RHS(R);163    {164      TEST_NOT_WIN32(DisableAllocationGuard g);165      LHS /= RHS;166    }167    assert(PathEq(LHS, E));168  }169  // CharT*170  {171    path LHS(L); PathReserve(LHS, ReserveSize);172    Ptr RHS(R);173    {174      TEST_NOT_WIN32(DisableAllocationGuard g);175      LHS /= RHS;176    }177    assert(PathEq(LHS, E));178  }179  {180    path LHS(L); PathReserve(LHS, ReserveSize);181    Ptr RHS(R);182    {183      TEST_NOT_WIN32(DisableAllocationGuard g);184      LHS.append(RHS, StrEnd(RHS));185    }186    assert(PathEq(LHS, E));187  }188  {189    path LHS(L); PathReserve(LHS, ReserveSize);190    path RHS(R);191    {192      DisableAllocationGuard g;193      LHS /= RHS;194    }195    assert(PathEq(LHS, E));196  }197  // input iterator - For non-native char types, appends needs to copy the198  // iterator range into a contiguous block of memory before it can perform the199  // code_cvt conversions.200  // For "char" no allocations will be performed because no conversion is201  // required.202  // On Windows, the append method is more complex and uses intermediate203  // path objects, which causes extra allocations. This is checked by comparing204  // path::value_type with "char" - on Windows, it's wchar_t.205#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS206  // Only check allocations if we can pick up allocations done within the207  // library implementation.208  bool ExpectNoAllocations = std::is_same<CharT, char>::value &&209                             std::is_same<path::value_type, char>::value;210#endif211  {212    path LHS(L); PathReserve(LHS, ReserveSize);213    InputIter RHS(R);214    {215      RequireAllocationGuard g(0); // require "at least zero" allocations by default216#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS217      if (ExpectNoAllocations)218        g.requireExactly(0);219#endif220      LHS /= RHS;221    }222    assert(PathEq(LHS, E));223  }224  {225    path LHS(L); PathReserve(LHS, ReserveSize);226    InputIter RHS(R);227    InputIter REnd(StrEnd(R));228    {229      RequireAllocationGuard g(0); // require "at least zero" allocations by default230#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS231      if (ExpectNoAllocations)232        g.requireExactly(0);233#endif234      LHS.append(RHS, REnd);235    }236    assert(PathEq(LHS, E));237  }238}239 240template <class CharT>241void doAppendSourceTest(AppendOperatorTestcase const& TC)242{243  using namespace fs;244  using Ptr = CharT const*;245  using Str = std::basic_string<CharT>;246  using StrView = std::basic_string_view<CharT>;247  using InputIter = cpp17_input_iterator<Ptr>;248  const Ptr L = TC.lhs;249  const Ptr R = TC.rhs;250  const Ptr E = TC.expected_result();251  // basic_string252  {253    path Result(L);254    Str RHS(R);255    path& Ref = (Result /= RHS);256    assert(Result == E);257    assert(&Ref == &Result);258  }259  {260    path LHS(L);261    Str RHS(R);262    path& Ref = LHS.append(RHS);263    assert(PathEq(LHS, E));264    assert(&Ref == &LHS);265  }266  // basic_string_view267  {268    path LHS(L);269    StrView RHS(R);270    path& Ref = (LHS /= RHS);271    assert(PathEq(LHS, E));272    assert(&Ref == &LHS);273  }274  {275    path LHS(L);276    StrView RHS(R);277    path& Ref = LHS.append(RHS);278    assert(PathEq(LHS, E));279    assert(&Ref == &LHS);280  }281  // Char*282  {283    path LHS(L);284    Str RHS(R);285    path& Ref = (LHS /= RHS);286    assert(PathEq(LHS, E));287    assert(&Ref == &LHS);288  }289  {290    path LHS(L);291    Ptr RHS(R);292    path& Ref = LHS.append(RHS);293    assert(PathEq(LHS, E));294    assert(&Ref == &LHS);295  }296  {297    path LHS(L);298    Ptr RHS(R);299    path& Ref = LHS.append(RHS, StrEnd(RHS));300    assert(PathEq(LHS, E));301    assert(&Ref == &LHS);302  }303  // iterators304  {305    path LHS(L);306    InputIter RHS(R);307    path& Ref = (LHS /= RHS);308    assert(PathEq(LHS, E));309    assert(&Ref == &LHS);310  }311  {312    path LHS(L); InputIter RHS(R);313    path& Ref = LHS.append(RHS);314    assert(PathEq(LHS, E));315    assert(&Ref == &LHS);316  }317  {318    path LHS(L);319    InputIter RHS(R);320    InputIter REnd(StrEnd(R));321    path& Ref = LHS.append(RHS, REnd);322    assert(PathEq(LHS, E));323    assert(&Ref == &LHS);324  }325}326 327 328 329template <class It, class = decltype(fs::path{}.append(std::declval<It>()))>330constexpr bool has_append(int) { return true; }331template <class It>332constexpr bool has_append(long) { return false; }333 334template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))>335constexpr bool has_append_op(int) { return true; }336template <class It>337constexpr bool has_append_op(long) { return false; }338 339template <class It>340constexpr bool has_append() {341  static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same");342  return has_append<It>(0) && has_append_op<It>(0);343}344 345void test_sfinae()346{347  using namespace fs;348  {349    using It = const char* const;350    static_assert(has_append<It>(), "");351  }352  {353    using It = cpp17_input_iterator<const char*>;354    static_assert(has_append<It>(), "");355  }356  {357    struct Traits {358      using iterator_category = std::input_iterator_tag;359      using value_type = const char;360      using pointer = const char*;361      using reference = const char&;362      using difference_type = std::ptrdiff_t;363    };364    using It = cpp17_input_iterator<const char*, Traits>;365    static_assert(has_append<It>(), "");366  }367  {368    using It = cpp17_output_iterator<const char*>;369    static_assert(!has_append<It>(), "");370 371  }372  {373    static_assert(!has_append<int*>(), "");374  }375  {376    static_assert(!has_append<char>(), "");377    static_assert(!has_append<const char>(), "");378  }379}380 381int main(int, char**)382{383  using namespace fs;384  for (auto const & TC : Cases) {385    {386      const char* LHS_In = TC.lhs;387      const char* RHS_In = TC.rhs;388      path LHS(LHS_In);389      path RHS(RHS_In);390      path& Res = (LHS /= RHS);391      assert(PathEq(Res, (const char*)TC.expected_result()));392      assert(&Res == &LHS);393    }394    doAppendSourceTest<char>    (TC);395#ifndef TEST_HAS_NO_WIDE_CHARACTERS396    doAppendSourceTest<wchar_t> (TC);397#endif398    doAppendSourceTest<char16_t>(TC);399    doAppendSourceTest<char32_t>(TC);400  }401  for (auto const & TC : LongLHSCases) {402    (void)TC;403    LIBCPP_ONLY(doAppendSourceAllocTest<char>(TC));404#ifndef TEST_HAS_NO_WIDE_CHARACTERS405    LIBCPP_ONLY(doAppendSourceAllocTest<wchar_t>(TC));406#endif407  }408  test_sfinae();409 410  return 0;411}412