thrust / dependencies /libcudacxx /libcxx /test /std /input.output /filesystems /class.path /path.member /path.concat.pass.cpp
| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // UNSUPPORTED: c++98, c++03 | |
| // <filesystem> | |
| // class path | |
| // path& operator+=(const path& x); | |
| // path& operator+=(const string_type& x); | |
| // path& operator+=(string_view x); | |
| // path& operator+=(const value_type* x); | |
| // path& operator+=(value_type x); | |
| // template <class Source> | |
| // path& operator+=(const Source& x); | |
| // template <class EcharT> | |
| // path& operator+=(EcharT x); | |
| // template <class Source> | |
| // path& concat(const Source& x); | |
| // template <class InputIterator> | |
| // path& concat(InputIterator first, InputIterator last); | |
| struct ConcatOperatorTestcase { | |
| MultiStringType lhs; | |
| MultiStringType rhs; | |
| MultiStringType expect; | |
| }; | |
| const ConcatOperatorTestcase Cases[] = | |
| { | |
| {S(""), S(""), S("")} | |
| , {S("p1"), S("p2"), S("p1p2")} | |
| , {S("p1/"), S("/p2"), S("p1//p2")} | |
| , {S(""), S("\\foo/bar/baz"), S("\\foo/bar/baz")} | |
| , {S("c:\\foo"), S(""), S("c:\\foo")} | |
| , {S(LONGSTR), S("foo"), S(LONGSTR "foo")} | |
| , {S("abcdefghijklmnopqrstuvwxyz/\\"), S("/\\123456789"), S("abcdefghijklmnopqrstuvwxyz/\\/\\123456789")} | |
| }; | |
| const ConcatOperatorTestcase LongLHSCases[] = | |
| { | |
| {S(""), S(LONGSTR), S(LONGSTR)} | |
| , {S("p1/"), S(LONGSTR), S("p1/" LONGSTR)} | |
| }; | |
| const ConcatOperatorTestcase CharTestCases[] = | |
| { | |
| {S(""), S("P"), S("P")} | |
| , {S("/fooba"), S("r"), S("/foobar")} | |
| }; | |
| // The concat 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. Concat `RHS` to `LHS` and check for the expected allocation behavior. | |
| template <class CharT> | |
| void doConcatSourceAllocTest(ConcatOperatorTestcase 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; | |
| std::size_t ReserveSize = StrLen(E) + 1; | |
| // 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.concat(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.concat(RHS, REnd); | |
| } | |
| assert(LHS == E); | |
| } | |
| } | |
| template <class CharT> | |
| void doConcatSourceTest(ConcatOperatorTestcase 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.concat(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.concat(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.concat(RHS); | |
| assert(LHS == E); | |
| assert(&Ref == &LHS); | |
| } | |
| { | |
| path LHS(L); | |
| Ptr RHS(R); | |
| path& Ref = LHS.concat(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.concat(RHS); | |
| assert(LHS == E); | |
| assert(&Ref == &LHS); | |
| } | |
| { | |
| path LHS(L); | |
| InputIter RHS(R); | |
| InputIter REnd(StrEnd(R)); | |
| path& Ref = LHS.concat(RHS, REnd); | |
| assert(LHS == E); | |
| assert(&Ref == &LHS); | |
| } | |
| } | |
| template <class CharT> | |
| void doConcatECharTest(ConcatOperatorTestcase const& TC) | |
| { | |
| using namespace fs; | |
| using Ptr = CharT const*; | |
| const Ptr RStr = TC.rhs; | |
| assert(StrLen(RStr) == 1); | |
| const Ptr L = TC.lhs; | |
| const CharT R = RStr[0]; | |
| const Ptr E = TC.expect; | |
| { | |
| path LHS(L); | |
| path& Ref = (LHS += R); | |
| assert(LHS == E); | |
| assert(&Ref == &LHS); | |
| } | |
| } | |
| template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))> | |
| constexpr bool has_concat(int) { return true; } | |
| template <class It> | |
| constexpr bool has_concat(long) { return false; } | |
| template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))> | |
| constexpr bool has_concat_op(int) { return true; } | |
| template <class It> | |
| constexpr bool has_concat_op(long) { return false; } | |
| template <class It> | |
| constexpr bool has_concat_op() { return has_concat_op<It>(0); } | |
| template <class It> | |
| constexpr bool has_concat() { | |
| static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same"); | |
| return has_concat<It>(0) && has_concat_op<It>(0); | |
| } | |
| void test_sfinae() { | |
| using namespace fs; | |
| { | |
| static_assert(has_concat_op<char>(), ""); | |
| static_assert(has_concat_op<const char>(), ""); | |
| static_assert(has_concat_op<char16_t>(), ""); | |
| static_assert(has_concat_op<const char16_t>(), ""); | |
| } | |
| { | |
| using It = const char* const; | |
| static_assert(has_concat<It>(), ""); | |
| } | |
| { | |
| using It = input_iterator<const char*>; | |
| static_assert(has_concat<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_concat<It>(), ""); | |
| } | |
| { | |
| using It = output_iterator<const char*>; | |
| static_assert(!has_concat<It>(), ""); | |
| } | |
| { | |
| static_assert(!has_concat<int>(0), ""); | |
| // operator+=(int) is well formed since it converts to operator+=(value_type) | |
| // but concat(int) isn't valid because there is no concat(value_type). | |
| // This should probably be addressed by a LWG issue. | |
| static_assert(has_concat_op<int>(), ""); | |
| } | |
| { | |
| static_assert(!has_concat<int*>(), ""); | |
| } | |
| } | |
| int main(int, char**) | |
| { | |
| 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); | |
| } | |
| { | |
| path LHS((const char*)TC.lhs); | |
| std::string_view RHS((const char*)TC.rhs); | |
| path& Ref = (LHS += RHS); | |
| assert(LHS == (const char*)TC.expect); | |
| assert(&Ref == &LHS); | |
| } | |
| doConcatSourceTest<char> (TC); | |
| doConcatSourceTest<wchar_t> (TC); | |
| doConcatSourceTest<char16_t>(TC); | |
| doConcatSourceTest<char32_t>(TC); | |
| } | |
| for (auto const & TC : LongLHSCases) { | |
| // Do path test | |
| { | |
| path LHS((const char*)TC.lhs); | |
| path RHS((const char*)TC.rhs); | |
| const char* E = TC.expect; | |
| PathReserve(LHS, StrLen(E) + 5); | |
| { | |
| DisableAllocationGuard g; | |
| path& Ref = (LHS += RHS); | |
| assert(&Ref == &LHS); | |
| } | |
| assert(LHS == E); | |
| } | |
| { | |
| path LHS((const char*)TC.lhs); | |
| std::string_view RHS((const char*)TC.rhs); | |
| const char* E = TC.expect; | |
| PathReserve(LHS, StrLen(E) + 5); | |
| { | |
| DisableAllocationGuard g; | |
| path& Ref = (LHS += RHS); | |
| assert(&Ref == &LHS); | |
| } | |
| assert(LHS == E); | |
| } | |
| doConcatSourceAllocTest<char>(TC); | |
| doConcatSourceAllocTest<wchar_t>(TC); | |
| } | |
| for (auto const& TC : CharTestCases) { | |
| doConcatECharTest<char>(TC); | |
| doConcatECharTest<wchar_t>(TC); | |
| doConcatECharTest<char16_t>(TC); | |
| doConcatECharTest<char32_t>(TC); | |
| } | |
| test_sfinae(); | |
| return 0; | |
| } | |