//===----------------------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is dual licensed under the MIT and the University of Illinois Open // Source Licenses. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++98, c++03 // <experimental/filesystem> // class path // path& operator/=(path const&) // template <class Source> // path& operator/=(Source const&); // template <class Source> // path& append(Source const&); // template <class InputIterator> // path& append(InputIterator first, InputIterator last); #include <experimental/filesystem> #include <type_traits> #include <string_view> #include <cassert> #include "test_macros.h" #include "test_iterators.h" #include "count_new.hpp" #include "filesystem_test_helper.hpp" namespace fs = std::experimental::filesystem; struct AppendOperatorTestcase { MultiStringType lhs; MultiStringType rhs; MultiStringType expect; }; #define S(Str) MKSTR(Str) const AppendOperatorTestcase Cases[] = { {S(""), S(""), S("")} , {S("p1"), S("p2"), S("p1/p2")} , {S("p1/"), S("p2"), S("p1/p2")} , {S("p1"), S("/p2"), S("p1/p2")} , {S("p1/"), S("/p2"), S("p1//p2")} , {S("p1"), S("\\p2"), S("p1/\\p2")} , {S("p1\\"), S("p2"), S("p1\\/p2")} , {S("p1\\"), S("\\p2"), S("p1\\/\\p2")} , {S("p1"), S(""), S("p1")} , {S(""), S("p2"), S("p2")} }; const AppendOperatorTestcase LongLHSCases[] = { {S("p1"), S("p2"), S("p1/p2")} , {S("p1/"), S("p2"), S("p1/p2")} , {S("p1"), S("/p2"), S("p1/p2")} }; #undef S // The append operator may need to allocate a temporary buffer before a code_cvt // conversion. Test if this allocation occurs by: // 1. Create a path, `LHS`, and reserve enough space to append `RHS`. // This prevents `LHS` from allocating during the actual appending. // 2. Create a `Source` object `RHS`, which represents a "large" string. // (The string must not trigger the SSO) // 3. Append `RHS` to `LHS` and check for the expected allocation behavior. template <class CharT> void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) { using namespace fs; using Ptr = CharT const*; using Str = std::basic_string<CharT>; using StrView = std::basic_string_view<CharT>; using InputIter = input_iterator<Ptr>; const Ptr L = TC.lhs; Str RShort = (Ptr)TC.rhs; Str EShort = (Ptr)TC.expect; assert(RShort.size() >= 2); CharT c = RShort.back(); RShort.append(100, c); EShort.append(100, c); const Ptr R = RShort.data(); const Str& E = EShort; std::size_t ReserveSize = E.size() + 3; // basic_string { path LHS(L); PathReserve(LHS, ReserveSize); Str RHS(R); { DisableAllocationGuard g; LHS /= RHS; } assert(LHS == E); } // basic_string_view { path LHS(L); PathReserve(LHS, ReserveSize); StrView RHS(R); { DisableAllocationGuard g; LHS /= RHS; } assert(LHS == E); } // CharT* { path LHS(L); PathReserve(LHS, ReserveSize); Ptr RHS(R); { DisableAllocationGuard g; LHS /= RHS; } assert(LHS == E); } { path LHS(L); PathReserve(LHS, ReserveSize); Ptr RHS(R); { DisableAllocationGuard g; LHS.append(RHS, StrEnd(RHS)); } assert(LHS == E); } // input iterator - For non-native char types, appends needs to copy the // iterator range into a contiguous block of memory before it can perform the // code_cvt conversions. // For "char" no allocations will be performed because no conversion is // required. bool DisableAllocations = std::is_same<CharT, char>::value; { path LHS(L); PathReserve(LHS, ReserveSize); InputIter RHS(R); { RequireAllocationGuard g; // requires 1 or more allocations occur by default if (DisableAllocations) g.requireExactly(0); LHS /= RHS; } assert(LHS == E); } { path LHS(L); PathReserve(LHS, ReserveSize); InputIter RHS(R); InputIter REnd(StrEnd(R)); { RequireAllocationGuard g; if (DisableAllocations) g.requireExactly(0); LHS.append(RHS, REnd); } assert(LHS == E); } } template <class CharT> void doAppendSourceTest(AppendOperatorTestcase const& TC) { using namespace fs; using Ptr = CharT const*; using Str = std::basic_string<CharT>; using StrView = std::basic_string_view<CharT>; using InputIter = input_iterator<Ptr>; const Ptr L = TC.lhs; const Ptr R = TC.rhs; const Ptr E = TC.expect; // basic_string { path LHS(L); Str RHS(R); path& Ref = (LHS /= RHS); assert(LHS == E); assert(&Ref == &LHS); } { path LHS(L); Str RHS(R); path& Ref = LHS.append(RHS); assert(LHS == E); assert(&Ref == &LHS); } // basic_string_view { path LHS(L); StrView RHS(R); path& Ref = (LHS /= RHS); assert(LHS == E); assert(&Ref == &LHS); } { path LHS(L); StrView RHS(R); path& Ref = LHS.append(RHS); assert(LHS == E); assert(&Ref == &LHS); } // Char* { path LHS(L); Str RHS(R); path& Ref = (LHS /= RHS); assert(LHS == E); assert(&Ref == &LHS); } { path LHS(L); Ptr RHS(R); path& Ref = LHS.append(RHS); assert(LHS == E); assert(&Ref == &LHS); } { path LHS(L); Ptr RHS(R); path& Ref = LHS.append(RHS, StrEnd(RHS)); assert(LHS == E); assert(&Ref == &LHS); } // iterators { path LHS(L); InputIter RHS(R); path& Ref = (LHS /= RHS); assert(LHS == E); assert(&Ref == &LHS); } { path LHS(L); InputIter RHS(R); path& Ref = LHS.append(RHS); assert(LHS == E); assert(&Ref == &LHS); } { path LHS(L); InputIter RHS(R); InputIter REnd(StrEnd(R)); path& Ref = LHS.append(RHS, REnd); assert(LHS == E); assert(&Ref == &LHS); } } template <class It, class = decltype(fs::path{}.append(std::declval<It>()))> constexpr bool has_append(int) { return true; } template <class It> constexpr bool has_append(long) { return false; } template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))> constexpr bool has_append_op(int) { return true; } template <class It> constexpr bool has_append_op(long) { return false; } template <class It> constexpr bool has_append() { static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same"); return has_append<It>(0) && has_append_op<It>(0); } void test_sfinae() { using namespace fs; { using It = const char* const; static_assert(has_append<It>(), ""); } { using It = input_iterator<const char*>; static_assert(has_append<It>(), ""); } { struct Traits { using iterator_category = std::input_iterator_tag; using value_type = const char; using pointer = const char*; using reference = const char&; using difference_type = std::ptrdiff_t; }; using It = input_iterator<const char*, Traits>; static_assert(has_append<It>(), ""); } { using It = output_iterator<const char*>; static_assert(!has_append<It>(), ""); } { static_assert(!has_append<int*>(), ""); } { static_assert(!has_append<char>(), ""); static_assert(!has_append<const char>(), ""); } } int main() { using namespace fs; for (auto const & TC : Cases) { { path LHS((const char*)TC.lhs); path RHS((const char*)TC.rhs); path& Ref = (LHS /= RHS); assert(LHS == (const char*)TC.expect); assert(&Ref == &LHS); } doAppendSourceTest<char> (TC); doAppendSourceTest<wchar_t> (TC); doAppendSourceTest<char16_t>(TC); doAppendSourceTest<char32_t>(TC); } for (auto const & TC : LongLHSCases) { doAppendSourceAllocTest<char>(TC); doAppendSourceAllocTest<wchar_t>(TC); } test_sfinae(); }