This should improve the time it takes to run the test suite a bit. Right now there are only a handful of headers in the modulemap because we're missing a lot of includes in the tests. New headers should be added there from the start, and we should fill up the modulemap over time until it contains all the test support headers.
412 lines
12 KiB
C++
412 lines
12 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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++03, c++11, c++14
|
|
|
|
// These tests require locale for non-char paths
|
|
// UNSUPPORTED: no-localization
|
|
|
|
// In MinGW mode, with optimizations enabled with a DLL, the number of counted
|
|
// allocations mismatches, as some ctor/dtor calls are generated in the
|
|
// calling code, and some are called from the DLL.
|
|
// ADDITIONAL_COMPILE_FLAGS: -DALLOW_MISMATCHING_LIBRRARY_INTERNAL_ALLOCATIONS
|
|
|
|
// <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 <filesystem>
|
|
#include <type_traits>
|
|
#include <string_view>
|
|
#include <cassert>
|
|
#include <utility>
|
|
|
|
// On Windows, the append function converts all inputs (pointers, iterators)
|
|
// to an intermediate path object, causing allocations in cases where no
|
|
// allocations are done on other platforms.
|
|
|
|
#include "../path_helper.h"
|
|
#include "count_new.h"
|
|
#include "make_string.h"
|
|
#include "test_iterators.h"
|
|
#include "test_macros.h"
|
|
namespace fs = std::filesystem;
|
|
|
|
struct AppendOperatorTestcase {
|
|
MultiStringType lhs;
|
|
MultiStringType rhs;
|
|
MultiStringType expect_posix;
|
|
MultiStringType expect_windows;
|
|
|
|
MultiStringType const& expected_result() const {
|
|
#ifdef _WIN32
|
|
return expect_windows;
|
|
#else
|
|
return expect_posix;
|
|
#endif
|
|
}
|
|
};
|
|
|
|
#define S(Str) MKSTR(Str)
|
|
const AppendOperatorTestcase Cases[] =
|
|
{
|
|
{S(""), S(""), S(""), S("")}
|
|
, {S("p1"), S("p2"), S("p1/p2"), S("p1\\p2")}
|
|
, {S("p1/"), S("p2"), S("p1/p2"), S("p1/p2")}
|
|
, {S("p1"), S("/p2"), S("/p2"), S("/p2")}
|
|
, {S("p1/"), S("/p2"), S("/p2"), S("/p2")}
|
|
, {S("p1"), S("\\p2"), S("p1/\\p2"), S("\\p2")}
|
|
, {S("p1\\"), S("p2"), S("p1\\/p2"), S("p1\\p2")}
|
|
, {S("p1\\"), S("\\p2"), S("p1\\/\\p2"), S("\\p2")}
|
|
, {S(""), S("p2"), S("p2"), S("p2")}
|
|
, {S("/p1"), S("p2"), S("/p1/p2"), S("/p1\\p2")}
|
|
, {S("/p1"), S("/p2"), S("/p2"), S("/p2")}
|
|
, {S("/p1/p3"), S("p2"), S("/p1/p3/p2"), S("/p1/p3\\p2")}
|
|
, {S("/p1/p3/"), S("p2"), S("/p1/p3/p2"), S("/p1/p3/p2")}
|
|
, {S("/p1/"), S("p2"), S("/p1/p2"), S("/p1/p2")}
|
|
, {S("/p1/p3/"), S("/p2/p4"), S("/p2/p4"), S("/p2/p4")}
|
|
, {S("/"), S(""), S("/"), S("/")}
|
|
, {S("/p1"), S("/p2/"), S("/p2/"), S("/p2/")}
|
|
, {S("p1"), S(""), S("p1/"), S("p1\\")}
|
|
, {S("p1/"), S(""), S("p1/"), S("p1/")}
|
|
|
|
, {S("//host"), S("foo"), S("//host/foo"), S("//host\\foo")}
|
|
, {S("//host/"), S("foo"), S("//host/foo"), S("//host/foo")}
|
|
, {S("//host"), S(""), S("//host/"), S("//host\\")}
|
|
|
|
, {S("foo"), S("C:/bar"), S("foo/C:/bar"), S("C:/bar")}
|
|
, {S("foo"), S("C:"), S("foo/C:"), S("C:")}
|
|
|
|
, {S("C:"), S(""), S("C:/"), S("C:")}
|
|
, {S("C:foo"), S("/bar"), S("/bar"), S("C:/bar")}
|
|
, {S("C:foo"), S("bar"), S("C:foo/bar"), S("C:foo\\bar")}
|
|
, {S("C:/foo"), S("bar"), S("C:/foo/bar"), S("C:/foo\\bar")}
|
|
, {S("C:/foo"), S("/bar"), S("/bar"), S("C:/bar")}
|
|
|
|
, {S("C:foo"), S("C:/bar"), S("C:foo/C:/bar"), S("C:/bar")}
|
|
, {S("C:foo"), S("C:bar"), S("C:foo/C:bar"), S("C:foo\\bar")}
|
|
, {S("C:/foo"), S("C:/bar"), S("C:/foo/C:/bar"), S("C:/bar")}
|
|
, {S("C:/foo"), S("C:bar"), S("C:/foo/C:bar"), S("C:/foo\\bar")}
|
|
|
|
, {S("C:foo"), S("c:/bar"), S("C:foo/c:/bar"), S("c:/bar")}
|
|
, {S("C:foo"), S("c:bar"), S("C:foo/c:bar"), S("c:bar")}
|
|
, {S("C:/foo"), S("c:/bar"), S("C:/foo/c:/bar"), S("c:/bar")}
|
|
, {S("C:/foo"), S("c:bar"), S("C:/foo/c:bar"), S("c:bar")}
|
|
|
|
, {S("C:/foo"), S("D:bar"), S("C:/foo/D:bar"), S("D:bar")}
|
|
};
|
|
|
|
|
|
const AppendOperatorTestcase LongLHSCases[] =
|
|
{
|
|
{S("p1"), S("p2"), S("p1/p2"), S("p1\\p2")}
|
|
, {S("p1/"), S("p2"), S("p1/p2"), S("p1/p2")}
|
|
, {S("p1"), S("/p2"), S("/p2"), S("/p2")}
|
|
, {S("/p1"), S("p2"), S("/p1/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 = cpp17_input_iterator<Ptr>;
|
|
|
|
const Ptr L = TC.lhs;
|
|
Str RShort = (Ptr)TC.rhs;
|
|
Str EShort = (Ptr)TC.expected_result();
|
|
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);
|
|
{
|
|
TEST_NOT_WIN32(DisableAllocationGuard g);
|
|
LHS /= RHS;
|
|
}
|
|
assert(PathEq(LHS, E));
|
|
}
|
|
// basic_string_view
|
|
{
|
|
path LHS(L); PathReserve(LHS, ReserveSize);
|
|
StrView RHS(R);
|
|
{
|
|
TEST_NOT_WIN32(DisableAllocationGuard g);
|
|
LHS /= RHS;
|
|
}
|
|
assert(PathEq(LHS, E));
|
|
}
|
|
// CharT*
|
|
{
|
|
path LHS(L); PathReserve(LHS, ReserveSize);
|
|
Ptr RHS(R);
|
|
{
|
|
TEST_NOT_WIN32(DisableAllocationGuard g);
|
|
LHS /= RHS;
|
|
}
|
|
assert(PathEq(LHS, E));
|
|
}
|
|
{
|
|
path LHS(L); PathReserve(LHS, ReserveSize);
|
|
Ptr RHS(R);
|
|
{
|
|
TEST_NOT_WIN32(DisableAllocationGuard g);
|
|
LHS.append(RHS, StrEnd(RHS));
|
|
}
|
|
assert(PathEq(LHS, E));
|
|
}
|
|
{
|
|
path LHS(L); PathReserve(LHS, ReserveSize);
|
|
path RHS(R);
|
|
{
|
|
DisableAllocationGuard g;
|
|
LHS /= RHS;
|
|
}
|
|
assert(PathEq(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.
|
|
// On Windows, the append method is more complex and uses intermediate
|
|
// path objects, which causes extra allocations. This is checked by comparing
|
|
// path::value_type with "char" - on Windows, it's wchar_t.
|
|
#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS
|
|
// Only check allocations if we can pick up allocations done within the
|
|
// library implementation.
|
|
bool ExpectNoAllocations = std::is_same<CharT, char>::value &&
|
|
std::is_same<path::value_type, char>::value;
|
|
#endif
|
|
{
|
|
path LHS(L); PathReserve(LHS, ReserveSize);
|
|
InputIter RHS(R);
|
|
{
|
|
RequireAllocationGuard g(0); // require "at least zero" allocations by default
|
|
#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS
|
|
if (ExpectNoAllocations)
|
|
g.requireExactly(0);
|
|
#endif
|
|
LHS /= RHS;
|
|
}
|
|
assert(PathEq(LHS, E));
|
|
}
|
|
{
|
|
path LHS(L); PathReserve(LHS, ReserveSize);
|
|
InputIter RHS(R);
|
|
InputIter REnd(StrEnd(R));
|
|
{
|
|
RequireAllocationGuard g(0); // require "at least zero" allocations by default
|
|
#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS
|
|
if (ExpectNoAllocations)
|
|
g.requireExactly(0);
|
|
#endif
|
|
LHS.append(RHS, REnd);
|
|
}
|
|
assert(PathEq(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 = cpp17_input_iterator<Ptr>;
|
|
const Ptr L = TC.lhs;
|
|
const Ptr R = TC.rhs;
|
|
const Ptr E = TC.expected_result();
|
|
// basic_string
|
|
{
|
|
path Result(L);
|
|
Str RHS(R);
|
|
path& Ref = (Result /= RHS);
|
|
assert(Result == E);
|
|
assert(&Ref == &Result);
|
|
}
|
|
{
|
|
path LHS(L);
|
|
Str RHS(R);
|
|
path& Ref = LHS.append(RHS);
|
|
assert(PathEq(LHS, E));
|
|
assert(&Ref == &LHS);
|
|
}
|
|
// basic_string_view
|
|
{
|
|
path LHS(L);
|
|
StrView RHS(R);
|
|
path& Ref = (LHS /= RHS);
|
|
assert(PathEq(LHS, E));
|
|
assert(&Ref == &LHS);
|
|
}
|
|
{
|
|
path LHS(L);
|
|
StrView RHS(R);
|
|
path& Ref = LHS.append(RHS);
|
|
assert(PathEq(LHS, E));
|
|
assert(&Ref == &LHS);
|
|
}
|
|
// Char*
|
|
{
|
|
path LHS(L);
|
|
Str RHS(R);
|
|
path& Ref = (LHS /= RHS);
|
|
assert(PathEq(LHS, E));
|
|
assert(&Ref == &LHS);
|
|
}
|
|
{
|
|
path LHS(L);
|
|
Ptr RHS(R);
|
|
path& Ref = LHS.append(RHS);
|
|
assert(PathEq(LHS, E));
|
|
assert(&Ref == &LHS);
|
|
}
|
|
{
|
|
path LHS(L);
|
|
Ptr RHS(R);
|
|
path& Ref = LHS.append(RHS, StrEnd(RHS));
|
|
assert(PathEq(LHS, E));
|
|
assert(&Ref == &LHS);
|
|
}
|
|
// iterators
|
|
{
|
|
path LHS(L);
|
|
InputIter RHS(R);
|
|
path& Ref = (LHS /= RHS);
|
|
assert(PathEq(LHS, E));
|
|
assert(&Ref == &LHS);
|
|
}
|
|
{
|
|
path LHS(L); InputIter RHS(R);
|
|
path& Ref = LHS.append(RHS);
|
|
assert(PathEq(LHS, E));
|
|
assert(&Ref == &LHS);
|
|
}
|
|
{
|
|
path LHS(L);
|
|
InputIter RHS(R);
|
|
InputIter REnd(StrEnd(R));
|
|
path& Ref = LHS.append(RHS, REnd);
|
|
assert(PathEq(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 = cpp17_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 = cpp17_input_iterator<const char*, Traits>;
|
|
static_assert(has_append<It>(), "");
|
|
}
|
|
{
|
|
using It = cpp17_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(int, char**)
|
|
{
|
|
using namespace fs;
|
|
for (auto const & TC : Cases) {
|
|
{
|
|
const char* LHS_In = TC.lhs;
|
|
const char* RHS_In = TC.rhs;
|
|
path LHS(LHS_In);
|
|
path RHS(RHS_In);
|
|
path& Res = (LHS /= RHS);
|
|
assert(PathEq(Res, (const char*)TC.expected_result()));
|
|
assert(&Res == &LHS);
|
|
}
|
|
doAppendSourceTest<char> (TC);
|
|
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
|
doAppendSourceTest<wchar_t> (TC);
|
|
#endif
|
|
doAppendSourceTest<char16_t>(TC);
|
|
doAppendSourceTest<char32_t>(TC);
|
|
}
|
|
for (auto const & TC : LongLHSCases) {
|
|
(void)TC;
|
|
LIBCPP_ONLY(doAppendSourceAllocTest<char>(TC));
|
|
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
|
LIBCPP_ONLY(doAppendSourceAllocTest<wchar_t>(TC));
|
|
#endif
|
|
}
|
|
test_sfinae();
|
|
|
|
return 0;
|
|
}
|