diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-04-02 23:03:41 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-04-02 23:03:41 +0000 |
commit | d7fae181c3954886257fb33d0a57b954b26da745 (patch) | |
tree | 841df55a810bc7446cf7afdd6811b3f1d79a3fc8 | |
parent | 298ffc609bf5c67079dbeb77e3f75e5bc18fde7a (diff) | |
download | bcm5719-llvm-d7fae181c3954886257fb33d0a57b954b26da745.tar.gz bcm5719-llvm-d7fae181c3954886257fb33d0a57b954b26da745.zip |
Implement filesystem NB comments, relative paths, and related issues.
This is a fairly large patch that implements all of the filesystem NB comments
and the relative paths changes (ex. adding weakly_canonical). These issues
and papers are all interrelated so their implementation couldn't be split up
nicely.
This patch upgrades <experimental/filesystem> to match the C++17 spec and not
the published experimental TS spec. Some of the changes in this patch are both
API and ABI breaking, however libc++ makes no guarantee about stability for
experimental implementations.
The major changes in this patch are:
* Implement NB comments for filesystem (P0492R2), including:
* Implement `perm_options` enum as part of NB comments, and update the
`permissions` function to match.
* Implement changes to `remove_filename` and `replace_filename`
* Implement changes to `path::stem()` and `path::extension()` which support
splitting examples like `.profile`.
* Change path iteration to return an empty path instead of '.' for trailing
separators.
* Change `operator/=` to handle absolute paths on the RHS.
* Change `absolute` to no longer accept a current path argument.
* Implement relative paths according to NB comments (P0219r1)
* Combine `path.cpp` and `operations.cpp` since some path functions require
access to the operations internals, and some fs operations require access
to the path parser.
llvm-svn: 329028
30 files changed, 1795 insertions, 884 deletions
diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt index 8a154d86f7f..f557d4aea39 100644 --- a/libcxx/benchmarks/CMakeLists.txt +++ b/libcxx/benchmarks/CMakeLists.txt @@ -68,7 +68,7 @@ set(BENCHMARK_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(BENCHMARK_LIBCXX_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/benchmark-libcxx) set(BENCHMARK_NATIVE_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/benchmark-native) set(BENCHMARK_TEST_COMPILE_FLAGS - -std=c++14 -O2 + -std=c++17 -O2 -I${BENCHMARK_LIBCXX_INSTALL}/include -I${LIBCXX_SOURCE_DIR}/test/support ) diff --git a/libcxx/benchmarks/GenerateInput.hpp b/libcxx/benchmarks/GenerateInput.hpp index 9d5adac4af4..8c97f5881f1 100644 --- a/libcxx/benchmarks/GenerateInput.hpp +++ b/libcxx/benchmarks/GenerateInput.hpp @@ -29,14 +29,16 @@ inline std::default_random_engine& getRandomEngine() { return RandEngine; } + inline char getRandomChar() { std::uniform_int_distribution<> LettersDist(0, LettersSize-1); return Letters[LettersDist(getRandomEngine())]; } template <class IntT> -inline IntT getRandomInteger() { - std::uniform_int_distribution<IntT> dist; +inline IntT getRandomInteger(IntT Min = 0, + IntT Max = std::numeric_limits<IntT>::max()) { + std::uniform_int_distribution<IntT> dist(Min, Max); return dist(getRandomEngine()); } diff --git a/libcxx/benchmarks/filesystem.bench.cpp b/libcxx/benchmarks/filesystem.bench.cpp index 67719803517..3e495605915 100644 --- a/libcxx/benchmarks/filesystem.bench.cpp +++ b/libcxx/benchmarks/filesystem.bench.cpp @@ -1,17 +1,14 @@ -#include <experimental/filesystem> - #include "benchmark/benchmark.h" #include "GenerateInput.hpp" #include "test_iterators.h" - -namespace fs = std::experimental::filesystem; +#include "filesystem_include.hpp" static const size_t TestNumInputs = 1024; template <class GenInputs> void BM_PathConstructString(benchmark::State &st, GenInputs gen) { - using namespace fs; + using fs::path; const auto in = gen(st.range(0)); path PP; for (auto& Part : in) @@ -21,14 +18,15 @@ void BM_PathConstructString(benchmark::State &st, GenInputs gen) { const path P(PP.native()); benchmark::DoNotOptimize(P.native().data()); } + st.SetComplexityN(st.range(0)); } BENCHMARK_CAPTURE(BM_PathConstructString, large_string, - getRandomStringInputs)->Arg(TestNumInputs); + getRandomStringInputs)->Range(8, TestNumInputs)->Complexity(); template <class GenInputs> void BM_PathConstructCStr(benchmark::State &st, GenInputs gen) { - using namespace fs; + using fs::path; const auto in = gen(st.range(0)); path PP; for (auto& Part : in) @@ -45,7 +43,7 @@ BENCHMARK_CAPTURE(BM_PathConstructCStr, large_string, template <template <class...> class ItType, class GenInputs> void BM_PathConstructIter(benchmark::State &st, GenInputs gen) { - using namespace fs; + using fs::path; using Iter = ItType<std::string::const_iterator>; const auto in = gen(st.range(0)); path PP; @@ -60,6 +58,7 @@ void BM_PathConstructIter(benchmark::State &st, GenInputs gen) { const path P(Start, End); benchmark::DoNotOptimize(P.native().data()); } + st.SetComplexityN(st.range(0)); } template <class GenInputs> void BM_PathConstructInputIter(benchmark::State &st, GenInputs gen) { @@ -70,14 +69,14 @@ void BM_PathConstructForwardIter(benchmark::State &st, GenInputs gen) { BM_PathConstructIter<forward_iterator>(st, gen); } BENCHMARK_CAPTURE(BM_PathConstructInputIter, large_string, - getRandomStringInputs)->Arg(TestNumInputs); + getRandomStringInputs)->Range(8, TestNumInputs)->Complexity(); BENCHMARK_CAPTURE(BM_PathConstructForwardIter, large_string, - getRandomStringInputs)->Arg(TestNumInputs); + getRandomStringInputs)->Range(8, TestNumInputs)->Complexity(); template <class GenInputs> void BM_PathIterateMultipleTimes(benchmark::State &st, GenInputs gen) { - using namespace fs; + using fs::path; const auto in = gen(st.range(0)); path PP; for (auto& Part : in) @@ -89,14 +88,15 @@ void BM_PathIterateMultipleTimes(benchmark::State &st, GenInputs gen) { } benchmark::ClobberMemory(); } + st.SetComplexityN(st.range(0)); } BENCHMARK_CAPTURE(BM_PathIterateMultipleTimes, iterate_elements, - getRandomStringInputs)->Arg(TestNumInputs); + getRandomStringInputs)->Range(8, TestNumInputs)->Complexity(); template <class GenInputs> void BM_PathIterateOnce(benchmark::State &st, GenInputs gen) { - using namespace fs; + using fs::path; const auto in = gen(st.range(0)); path PP; for (auto& Part : in) @@ -109,13 +109,14 @@ void BM_PathIterateOnce(benchmark::State &st, GenInputs gen) { } benchmark::ClobberMemory(); } + st.SetComplexityN(st.range(0)); } BENCHMARK_CAPTURE(BM_PathIterateOnce, iterate_elements, - getRandomStringInputs)->Arg(TestNumInputs); + getRandomStringInputs)->Range(8, TestNumInputs)->Complexity(); template <class GenInputs> void BM_PathIterateOnceBackwards(benchmark::State &st, GenInputs gen) { - using namespace fs; + using fs::path; const auto in = gen(st.range(0)); path PP; for (auto& Part : in) @@ -135,4 +136,28 @@ void BM_PathIterateOnceBackwards(benchmark::State &st, GenInputs gen) { BENCHMARK_CAPTURE(BM_PathIterateOnceBackwards, iterate_elements, getRandomStringInputs)->Arg(TestNumInputs); +static fs::path getRandomPaths(int NumParts, int PathLen) { + fs::path Result; + while (NumParts--) { + std::string Part = getRandomString(PathLen); + Result /= Part; + } + return Result; +} + +template <class GenInput> +void BM_LexicallyNormal(benchmark::State &st, GenInput gen, size_t PathLen) { + using fs::path; + auto In = gen(st.range(0), PathLen); + benchmark::DoNotOptimize(&In); + while (st.KeepRunning()) { + benchmark::DoNotOptimize(In.lexically_normal()); + } + st.SetComplexityN(st.range(0)); +} +BENCHMARK_CAPTURE(BM_LexicallyNormal, small_path, + getRandomPaths, /*PathLen*/5)->RangeMultiplier(2)->Range(2, 256)->Complexity(); +BENCHMARK_CAPTURE(BM_LexicallyNormal, large_path, + getRandomPaths, /*PathLen*/32)->RangeMultiplier(2)->Range(2, 256)->Complexity(); + BENCHMARK_MAIN(); diff --git a/libcxx/include/experimental/filesystem b/libcxx/include/experimental/filesystem index 47cc0c5aa70..411b98121f0 100644 --- a/libcxx/include/experimental/filesystem +++ b/libcxx/include/experimental/filesystem @@ -76,11 +76,11 @@ // operational functions - path absolute(const path& p, const path& base=current_path()); + path absolute(const path& p); + path absolute(const path& p, error_code &ec); - path canonical(const path& p, const path& base = current_path()); + path canonical(const path& p); path canonical(const path& p, error_code& ec); - path canonical(const path& p, const path& base, error_code& ec); void copy(const path& from, const path& to); void copy(const path& from, const path& to, error_code& ec); @@ -185,9 +185,17 @@ void permissions(const path& p, perms prms, perm_options opts, error_code& ec); + path proximate(const path& p, error_code& ec); + path proximate(const path& p, const path& base = current_path()); + path proximate(const path& p, const path& base, error_code &ec); + path read_symlink(const path& p); path read_symlink(const path& p, error_code& ec); + path relative(const path& p, error_code& ec); + path relative(const path& p, const path& base=current_path()); + path relative(const path& p, const path& base, error_code& ec); + bool remove(const path& p); bool remove(const path& p, error_code& ec) _NOEXCEPT; @@ -211,12 +219,13 @@ file_status symlink_status(const path& p); file_status symlink_status(const path& p, error_code& ec) _NOEXCEPT; - path system_complete(const path& p); - path system_complete(const path& p, error_code& ec); - path temp_directory_path(); path temp_directory_path(error_code& ec); + path weakly_canonical(path const& p); + path weakly_canonical(path const& p, error_code& ec); + + } } } } // namespaces std::experimental::filesystem::v1 */ @@ -796,26 +805,26 @@ public: private: template <class _ECharT> - void __append_sep_if_needed(_ECharT __first_or_null) { - const _ECharT __null_val = {}; - bool __append_sep = !empty() && - !__is_separator(__pn_.back()) && - __first_or_null != __null_val && // non-empty - !__is_separator(__first_or_null); - if (__append_sep) - __pn_ += preferred_separator; + static bool __source_is_absolute(_ECharT __first_or_null) { + return __is_separator(__first_or_null); } public: // appends path& operator/=(const path& __p) { - _LIBCPP_ASSERT(!__p.has_root_name(), - "cannot append to a path with a root name"); - __append_sep_if_needed(__p.empty() ? char{} : __p.__pn_[0]); + if (__p.is_absolute()) { + __pn_ = __p.__pn_; + return *this; + } + if (has_filename()) + __pn_ += preferred_separator; __pn_ += __p.native(); return *this; } + // FIXME: Use _LIBCPP_DIAGNOSE_WARNING to produce a diagnostic when __src + // is known at compile time to be "/' since the user almost certainly intended + // to append a separator instead of overwriting the path with "/" template <class _Source> _LIBCPP_INLINE_VISIBILITY _EnableIfPathable<_Source> @@ -828,7 +837,10 @@ public: append(const _Source& __src) { using _Traits = __is_pathable<_Source>; using _CVT = _PathCVT<_SourceChar<_Source>>; - __append_sep_if_needed(_Traits::__first_or_null(__src)); + if (__source_is_absolute(_Traits::__first_or_null(__src))) + __pn_.clear(); + else if (has_filename()) + __pn_ += preferred_separator; _CVT::__append_source(__pn_, __src); return *this; } @@ -838,10 +850,11 @@ public: typedef typename iterator_traits<_InputIt>::value_type _ItVal; static_assert(__can_convert_char<_ItVal>::value, "Must convertible"); using _CVT = _PathCVT<_ItVal>; - if (__first != __last) { - __append_sep_if_needed(*__first); - _CVT::__append_range(__pn_, __first, __last); - } + if (__first != __last && __source_is_absolute(*__first)) + __pn_.clear(); + else if (has_filename()) + __pn_ += preferred_separator; + _CVT::__append_range(__pn_, __first, __last); return *this; } @@ -916,10 +929,9 @@ public: _LIBCPP_INLINE_VISIBILITY path& remove_filename() { - if (__pn_.size() == __root_path_raw().size()) - clear(); - else - __pn_ = __parent_path(); + auto __fname = __filename(); + if (!__fname.empty()) + __pn_.erase(__fname.data() - __pn_.data()); return *this; } @@ -935,6 +947,10 @@ public: __pn_.swap(__rhs.__pn_); } + // private helper to allow reserving memory in the path + _LIBCPP_INLINE_VISIBILITY + void __reserve(size_t __s) { __pn_.reserve(__s); } + // native format observers _LIBCPP_INLINE_VISIBILITY const string_type& native() const _NOEXCEPT { @@ -1023,6 +1039,17 @@ public: _LIBCPP_INLINE_VISIBILITY bool is_absolute() const { return has_root_directory(); } _LIBCPP_INLINE_VISIBILITY bool is_relative() const { return !is_absolute(); } + // relative paths + path lexically_normal() const; + path lexically_relative(const path& __base) const; + + _LIBCPP_INLINE_VISIBILITY path lexically_proximate(const path& __base) const { + path __result = this->lexically_relative(__base); + if (__result.native().empty()) + return *this; + return __result; + } + // iterators class _LIBCPP_TYPE_VIS iterator; typedef iterator const_iterator; @@ -1277,7 +1304,9 @@ void __throw_filesystem_error(_Args&&...) // operational functions _LIBCPP_FUNC_VIS -path __canonical(const path&, const path&, error_code *__ec=nullptr); +path __absolute(const path&, error_code *__ec=nullptr); +_LIBCPP_FUNC_VIS +path __canonical(const path&, error_code *__ec=nullptr); _LIBCPP_FUNC_VIS void __copy(const path& __from, const path& __to, copy_options __opt, error_code *__ec=nullptr); @@ -1342,6 +1371,8 @@ _LIBCPP_FUNC_VIS path __system_complete(const path&, error_code *__ec=nullptr); _LIBCPP_FUNC_VIS path __temp_directory_path(error_code *__ec=nullptr); +_LIBCPP_FUNC_VIS +path __weakly_canonical(path const& __p, error_code *__ec=nullptr); inline _LIBCPP_INLINE_VISIBILITY path current_path() { @@ -1363,24 +1394,24 @@ void current_path(const path& __p, error_code& __ec) _NOEXCEPT { __current_path(__p, &__ec); } -_LIBCPP_FUNC_VIS -path absolute(const path&, const path& __p2 = current_path()); +inline _LIBCPP_INLINE_VISIBILITY +path absolute(const path& __p) { + return __absolute(__p); +} inline _LIBCPP_INLINE_VISIBILITY -path canonical(const path& __p, const path& __base = current_path()) { - return __canonical(__p, __base); +path absolute(const path& __p, error_code &__ec) { + return __absolute(__p, &__ec); } inline _LIBCPP_INLINE_VISIBILITY -path canonical(const path& __p, error_code& __ec) { - path __base = __current_path(&__ec); - if (__ec) return {}; - return __canonical(__p, __base, &__ec); +path canonical(const path& __p) { + return __canonical(__p); } inline _LIBCPP_INLINE_VISIBILITY -path canonical(const path& __p, const path& __base, error_code& __ec) { - return __canonical(__p, __base, &__ec); +path canonical(const path& __p, error_code& __ec) { + return __canonical(__p, &__ec); } inline _LIBCPP_INLINE_VISIBILITY @@ -1492,7 +1523,8 @@ void create_symlink(const path& __to, const path& __new) { } inline _LIBCPP_INLINE_VISIBILITY -void create_symlink(const path& __to, const path& __new, error_code& __ec) _NOEXCEPT { +void create_symlink(const path& __to, const path& __new, + error_code& __ec) _NOEXCEPT { return __create_symlink(__to, __new, &__ec); } @@ -1716,6 +1748,27 @@ void permissions(const path& __p, perms __prms, perm_options __opts, } inline _LIBCPP_INLINE_VISIBILITY +path proximate(const path& __p, const path& __base, error_code& __ec) { + path __tmp = __weakly_canonical(__p, &__ec); + if (__ec) + return {}; + path __tmp_base = __weakly_canonical(__base, &__ec); + if (__ec) + return {}; + return __tmp.lexically_proximate(__tmp_base); +} + +inline _LIBCPP_INLINE_VISIBILITY +path proximate(const path& __p, error_code& __ec) { + return proximate(__p, current_path(), __ec); +} + +inline _LIBCPP_INLINE_VISIBILITY +path proximate(const path& __p, const path& __base = current_path()) { + return __weakly_canonical(__p).lexically_proximate(__weakly_canonical(__base)); +} + +inline _LIBCPP_INLINE_VISIBILITY path read_symlink(const path& __p) { return __read_symlink(__p); } @@ -1725,6 +1778,29 @@ path read_symlink(const path& __p, error_code& __ec) { return __read_symlink(__p, &__ec); } + +inline _LIBCPP_INLINE_VISIBILITY +path relative(const path& __p, const path& __base, error_code& __ec) { + path __tmp = __weakly_canonical(__p, &__ec); + if (__ec) + return path(); + path __tmpbase = __weakly_canonical(__base, &__ec); + if (__ec) + return path(); + return __tmp.lexically_relative(__tmpbase); +} + +inline _LIBCPP_INLINE_VISIBILITY +path relative(const path& __p, error_code& __ec) { + return relative(__p, current_path(), __ec); +} + +inline _LIBCPP_INLINE_VISIBILITY +path relative(const path& __p, const path& __base=current_path()) { + return __weakly_canonical(__p).lexically_relative(__weakly_canonical(__base)); +} + + inline _LIBCPP_INLINE_VISIBILITY bool remove(const path& __p) { return __remove(__p); @@ -1796,23 +1872,23 @@ file_status symlink_status(const path& __p, error_code& __ec) _NOEXCEPT { } inline _LIBCPP_INLINE_VISIBILITY -path system_complete(const path& __p) { - return __system_complete(__p); +path temp_directory_path() { + return __temp_directory_path(); } inline _LIBCPP_INLINE_VISIBILITY -path system_complete(const path& __p, error_code& __ec) { - return __system_complete(__p, &__ec); +path temp_directory_path(error_code& __ec) { + return __temp_directory_path(&__ec); } inline _LIBCPP_INLINE_VISIBILITY -path temp_directory_path() { - return __temp_directory_path(); +path weakly_canonical(path const& __p) { + return __weakly_canonical(__p); } inline _LIBCPP_INLINE_VISIBILITY -path temp_directory_path(error_code& __ec) { - return __temp_directory_path(&__ec); +path weakly_canonical(path const& __p, error_code& __ec) { + return __weakly_canonical(__p, &__ec); } diff --git a/libcxx/src/experimental/filesystem/operations.cpp b/libcxx/src/experimental/filesystem/operations.cpp index b52022ac3f2..70284ab654a 100644 --- a/libcxx/src/experimental/filesystem/operations.cpp +++ b/libcxx/src/experimental/filesystem/operations.cpp @@ -10,8 +10,10 @@ #include "experimental/filesystem" #include "iterator" #include "fstream" -#include "type_traits" #include "random" /* for unique_path */ +#include "string_view" +#include "type_traits" +#include "vector" #include "cstdlib" #include "climits" @@ -57,6 +59,250 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM filesystem_error::~filesystem_error() {} +namespace { namespace parser +{ + +using string_view_t = path::__string_view; +using string_view_pair = pair<string_view_t, string_view_t>; +using PosPtr = path::value_type const*; + +struct PathParser { + enum ParserState : unsigned char { + // Zero is a special sentinel value used by default constructed iterators. + PS_BeforeBegin = 1, + PS_InRootName, + PS_InRootDir, + PS_InFilenames, + PS_InTrailingSep, + PS_AtEnd + }; + + const string_view_t Path; + string_view_t RawEntry; + ParserState State; + +private: + PathParser(string_view_t P, ParserState State) noexcept + : Path(P), State(State) {} + +public: + PathParser(string_view_t P, string_view_t E, unsigned char S) + : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) { + // S cannot be '0' or PS_BeforeBegin. + } + + static PathParser CreateBegin(string_view_t P) noexcept { + PathParser PP(P, PS_BeforeBegin); + PP.increment(); + return PP; + } + + static PathParser CreateEnd(string_view_t P) noexcept { + PathParser PP(P, PS_AtEnd); + return PP; + } + + PosPtr peek() const noexcept { + auto TkEnd = getNextTokenStartPos(); + auto End = getAfterBack(); + return TkEnd == End ? nullptr : TkEnd; + } + + void increment() noexcept { + const PosPtr End = getAfterBack(); + const PosPtr Start = getNextTokenStartPos(); + if (Start == End) + return makeState(PS_AtEnd); + + switch (State) { + case PS_BeforeBegin: { + PosPtr TkEnd = consumeSeparator(Start, End); + if (TkEnd) + return makeState(PS_InRootDir, Start, TkEnd); + else + return makeState(PS_InFilenames, Start, consumeName(Start, End)); + } + case PS_InRootDir: + return makeState(PS_InFilenames, Start, consumeName(Start, End)); + + case PS_InFilenames: { + PosPtr SepEnd = consumeSeparator(Start, End); + if (SepEnd != End) { + PosPtr TkEnd = consumeName(SepEnd, End); + if (TkEnd) + return makeState(PS_InFilenames, SepEnd, TkEnd); + } + return makeState(PS_InTrailingSep, Start, SepEnd); + } + + case PS_InTrailingSep: + return makeState(PS_AtEnd); + + case PS_InRootName: + case PS_AtEnd: + _LIBCPP_UNREACHABLE(); + } + } + + void decrement() noexcept { + const PosPtr REnd = getBeforeFront(); + const PosPtr RStart = getCurrentTokenStartPos() - 1; + if (RStart == REnd) // we're decrementing the begin + return makeState(PS_BeforeBegin); + + switch (State) { + case PS_AtEnd: { + // Try to consume a trailing separator or root directory first. + if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) { + if (SepEnd == REnd) + return makeState(PS_InRootDir, Path.data(), RStart + 1); + return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1); + } else { + PosPtr TkStart = consumeName(RStart, REnd); + return makeState(PS_InFilenames, TkStart + 1, RStart + 1); + } + } + case PS_InTrailingSep: + return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1); + case PS_InFilenames: { + PosPtr SepEnd = consumeSeparator(RStart, REnd); + if (SepEnd == REnd) + return makeState(PS_InRootDir, Path.data(), RStart + 1); + PosPtr TkEnd = consumeName(SepEnd, REnd); + return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1); + } + case PS_InRootDir: + // return makeState(PS_InRootName, Path.data(), RStart + 1); + case PS_InRootName: + case PS_BeforeBegin: + _LIBCPP_UNREACHABLE(); + } + } + + /// \brief Return a view with the "preferred representation" of the current + /// element. For example trailing separators are represented as a '.' + string_view_t operator*() const noexcept { + switch (State) { + case PS_BeforeBegin: + case PS_AtEnd: + return ""; + case PS_InRootDir: + return "/"; + case PS_InTrailingSep: + return ""; + case PS_InRootName: + case PS_InFilenames: + return RawEntry; + } + _LIBCPP_UNREACHABLE(); + } + + explicit operator bool() const noexcept { + return State != PS_BeforeBegin && State != PS_AtEnd; + } + + PathParser& operator++() noexcept { + increment(); + return *this; + } + + PathParser& operator--() noexcept { + decrement(); + return *this; + } + + bool inRootPath() const noexcept { + return State == PS_InRootDir || State == PS_InRootName; + } + +private: + void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept { + State = NewState; + RawEntry = string_view_t(Start, End - Start); + } + void makeState(ParserState NewState) noexcept { + State = NewState; + RawEntry = {}; + } + + PosPtr getAfterBack() const noexcept { + return Path.data() + Path.size(); + } + + PosPtr getBeforeFront() const noexcept { + return Path.data() - 1; + } + + /// \brief Return a pointer to the first character after the currently + /// lexed element. + PosPtr getNextTokenStartPos() const noexcept { + switch (State) { + case PS_BeforeBegin: + return Path.data(); + case PS_InRootName: + case PS_InRootDir: + case PS_InFilenames: + return &RawEntry.back() + 1; + case PS_InTrailingSep: + case PS_AtEnd: + return getAfterBack(); + } + _LIBCPP_UNREACHABLE(); + } + + /// \brief Return a pointer to the first character in the currently lexed + /// element. + PosPtr getCurrentTokenStartPos() const noexcept { + switch (State) { + case PS_BeforeBegin: + case PS_InRootName: + return &Path.front(); + case PS_InRootDir: + case PS_InFilenames: + case PS_InTrailingSep: + return &RawEntry.front(); + case PS_AtEnd: + return &Path.back() + 1; + } + _LIBCPP_UNREACHABLE(); + } + + PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept { + if (P == End || *P != '/') + return nullptr; + const int Inc = P < End ? 1 : -1; + P += Inc; + while (P != End && *P == '/') + P += Inc; + return P; + } + + PosPtr consumeName(PosPtr P, PosPtr End) const noexcept { + if (P == End || *P == '/') + return nullptr; + const int Inc = P < End ? 1 : -1; + P += Inc; + while (P != End && *P != '/') + P += Inc; + return P; + } +}; + +string_view_pair separate_filename(string_view_t const & s) { + if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""}; + auto pos = s.find_last_of('.'); + if (pos == string_view_t::npos || pos == 0) + return string_view_pair{s, string_view_t{}}; + return string_view_pair{s.substr(0, pos), s.substr(pos)}; +} + +string_view_t createView(PosPtr S, PosPtr E) noexcept { + return {S, static_cast<size_t>(E - S) + 1}; +} + +}} // namespace parser + + // POSIX HELPERS namespace detail { namespace { @@ -186,14 +432,33 @@ bool copy_file_impl(const path& from, const path& to, perms from_perms, }} // end namespace detail using detail::set_or_throw; +using parser::string_view_t; +using parser::PathParser; +using parser::createView; + +static path __do_absolute(const path& p, path *cwd, std::error_code *ec) { + if (ec) ec->clear(); + if (p.is_absolute()) + return p; + *cwd = __current_path(ec); + if (ec && *ec) + return {}; + return (*cwd) / p; +} -path __canonical(path const & orig_p, const path& base, std::error_code *ec) +path __absolute(const path& p, std::error_code *ec) { + path cwd; + return __do_absolute(p, &cwd, ec); +} + +path __canonical(path const & orig_p, std::error_code *ec) { - path p = absolute(orig_p, base); + path cwd; + path p = __do_absolute(orig_p, &cwd, ec); char buff[PATH_MAX + 1]; char *ret; if ((ret = ::realpath(p.c_str(), buff)) == nullptr) { - set_or_throw(ec, "canonical", orig_p, base); + set_or_throw(ec, "canonical", orig_p, cwd); return {}; } if (ec) ec->clear(); @@ -791,11 +1056,6 @@ file_status __symlink_status(const path& p, std::error_code *ec) { return detail::posix_lstat(p, ec); } -path __system_complete(const path& p, std::error_code *ec) { - if (ec) ec->clear(); - return absolute(p, current_path()); -} - path __temp_directory_path(std::error_code* ec) { const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; const char* ret = nullptr; @@ -820,34 +1080,393 @@ path __temp_directory_path(std::error_code* ec) { return p; } -// An absolute path is composed according to the table in [fs.op.absolute]. -path absolute(const path& p, const path& base) { - auto root_name = p.root_name(); - auto root_dir = p.root_directory(); - if (!root_name.empty() && !root_dir.empty()) - return p; +path __weakly_canonical(const path& p, std::error_code *ec) { + if (p.empty()) + return __canonical("", ec); + + path result; + path tmp; + tmp.__reserve(p.native().size()); + auto PP = PathParser::CreateEnd(p.native()); + --PP; + std::vector<string_view_t> DNEParts; + + while (PP.State != PathParser::PS_BeforeBegin) { + tmp.assign(createView(p.native().data(), &PP.RawEntry.back())); + std::error_code m_ec; + file_status st = __status(tmp, &m_ec); + if (!status_known(st)) { + set_or_throw(m_ec, ec, "weakly_canonical", p); + return {}; + } else if (exists(st)) { + result = __canonical(tmp, ec); + break; + } + DNEParts.push_back(*PP); + --PP; + } + if (PP.State == PathParser::PS_BeforeBegin) + result = __canonical("", ec); + if (ec) ec->clear(); + if (DNEParts.empty()) + return result; + for (auto It=DNEParts.rbegin(); It != DNEParts.rend(); ++It) + result /= *It; + return result.lexically_normal(); +} + - auto abs_base = base.is_absolute() ? base : absolute(base); - /* !has_root_name && !has_root_dir */ - if (root_name.empty() && root_dir.empty()) +/////////////////////////////////////////////////////////////////////////////// +// path definitions +/////////////////////////////////////////////////////////////////////////////// + +constexpr path::value_type path::preferred_separator; + +path & path::replace_extension(path const & replacement) +{ + path p = extension(); + if (not p.empty()) { + __pn_.erase(__pn_.size() - p.native().size()); + } + if (!replacement.empty()) { + if (replacement.native()[0] != '.') { + __pn_ += "."; + } + __pn_.append(replacement.__pn_); + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// +// path.decompose + +string_view_t path::__root_name() const +{ + auto PP = PathParser::CreateBegin(__pn_); + if (PP.State == PathParser::PS_InRootName) + return *PP; + return {}; +} + +string_view_t path::__root_directory() const +{ + auto PP = PathParser::CreateBegin(__pn_); + if (PP.State == PathParser::PS_InRootName) + ++PP; + if (PP.State == PathParser::PS_InRootDir) + return *PP; + return {}; +} + +string_view_t path::__root_path_raw() const +{ + auto PP = PathParser::CreateBegin(__pn_); + if (PP.State == PathParser::PS_InRootName) { + auto NextCh = PP.peek(); + if (NextCh && *NextCh == '/') { + ++PP; + return createView(__pn_.data(), &PP.RawEntry.back()); + } + return PP.RawEntry; + } + if (PP.State == PathParser::PS_InRootDir) + return *PP; + return {}; +} + +static bool ConsumeRootDir(PathParser* PP) { + while (PP->State <= PathParser::PS_InRootDir) + ++(*PP); + return PP->State == PathParser::PS_AtEnd; +} + +string_view_t path::__relative_path() const +{ + auto PP = PathParser::CreateBegin(__pn_); + if (ConsumeRootDir(&PP)) + return {}; + return createView(PP.RawEntry.data(), &__pn_.back()); +} + +string_view_t path::__parent_path() const +{ + if (empty()) + return {}; + // Determine if we have a root path but not a relative path. In that case + // return *this. { - return abs_base / p; + auto PP = PathParser::CreateBegin(__pn_); + if (ConsumeRootDir(&PP)) + return __pn_; } - else if (!root_name.empty()) /* has_root_name && !has_root_dir */ + // Otherwise remove a single element from the end of the path, and return + // a string representing that path { - return root_name / abs_base.root_directory() - / - abs_base.relative_path() / p.relative_path(); + auto PP = PathParser::CreateEnd(__pn_); + --PP; + if (PP.RawEntry.data() == __pn_.data()) + return {}; + --PP; + return createView(__pn_.data(), &PP.RawEntry.back()); } - else /* !has_root_name && has_root_dir */ +} + +string_view_t path::__filename() const +{ + if (empty()) return {}; { - if (abs_base.has_root_name()) - return abs_base.root_name() / p; - // else p is absolute, return outside of block + PathParser PP = PathParser::CreateBegin(__pn_); + if (ConsumeRootDir(&PP)) + return {}; + } + return *(--PathParser::CreateEnd(__pn_)); +} + +string_view_t path::__stem() const +{ + return parser::separate_filename(__filename()).first; +} + +string_view_t path::__extension() const +{ + return parser::separate_filename(__filename()).second; +} + +//////////////////////////////////////////////////////////////////////////// +// path.gen + + +enum PathPartKind : unsigned char { + PK_None, + PK_RootSep, + PK_Filename, + PK_Dot, + PK_DotDot, + PK_TrailingSep +}; + +static PathPartKind ClassifyPathPart(string_view_t Part) { + if (Part.empty()) + return PK_TrailingSep; + if (Part == ".") + return PK_Dot; + if (Part == "..") + return PK_DotDot; + if (Part == "/") + return PK_RootSep; + return PK_Filename; +} + +path path::lexically_normal() const { + if (__pn_.empty()) + return *this; + + using PartKindPair = std::pair<string_view_t, PathPartKind>; + std::vector<PartKindPair> Parts; + // Guess as to how many elements the path has to avoid reallocating. + Parts.reserve(32); + + // Track the total size of the parts as we collect them. This allows the + // resulting path to reserve the correct amount of memory. + size_t NewPathSize = 0; + auto AddPart = [&](PathPartKind K, string_view_t P) { + NewPathSize += P.size(); + Parts.emplace_back(P, K); + }; + auto LastPartKind = [&]() { + if (Parts.empty()) + return PK_None; + return Parts.back().second; + }; + + bool MaybeNeedTrailingSep = false; + // Build a stack containing the remaining elements of the path, popping off + // elements which occur before a '..' entry. + for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) { + auto Part = *PP; + PathPartKind Kind = ClassifyPathPart(Part); + switch (Kind) { + case PK_Filename: + case PK_RootSep: { + // Add all non-dot and non-dot-dot elements to the stack of elements. + AddPart(Kind, Part); + MaybeNeedTrailingSep = false; + break; + } + case PK_DotDot: { + // Only push a ".." element if there are no elements preceding the "..", + // or if the preceding element is itself "..". + auto LastKind = LastPartKind(); + if (LastKind == PK_Filename) { + NewPathSize -= Parts.back().first.size(); + Parts.pop_back(); + } else if (LastKind != PK_RootSep) + AddPart(PK_DotDot, ".."); + MaybeNeedTrailingSep = LastKind == PK_Filename; + break; + } + case PK_Dot: + case PK_TrailingSep: { + MaybeNeedTrailingSep = true; + break; } - return p; + case PK_None: + _LIBCPP_UNREACHABLE(); + } + } + // [fs.path.generic]p6.8: If the path is empty, add a dot. + if (Parts.empty()) + return "."; + + // [fs.path.generic]p6.7: If the last filename is dot-dot, remove any + // trailing directory-separator. + bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename; + + path Result; + Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep); + for (auto &PK : Parts) + Result /= PK.first; + + if (NeedTrailingSep) + Result /= ""; + + return Result; } +static int DetermineLexicalElementCount(PathParser PP) { + int Count = 0; + for (; PP; ++PP) { + auto Elem = *PP; + if (Elem == "..") + --Count; + else if (Elem != ".") + ++Count; + } + return Count; +} + +path path::lexically_relative(const path& base) const { + { // perform root-name/root-directory mismatch checks + auto PP = PathParser::CreateBegin(__pn_); + auto PPBase = PathParser::CreateBegin(base.__pn_); + auto CheckIterMismatchAtBase = [&]() { + return PP.State != PPBase.State && ( + PP.inRootPath() || PPBase.inRootPath()); + }; + if (PP.State == PathParser::PS_InRootName && + PPBase.State == PathParser::PS_InRootName) { + if (*PP != *PPBase) + return {}; + } else if (CheckIterMismatchAtBase()) + return {}; + + if (PP.inRootPath()) ++PP; + if (PPBase.inRootPath()) ++PPBase; + if (CheckIterMismatchAtBase()) + return {}; + } + + // Find the first mismatching element + auto PP = PathParser::CreateBegin(__pn_); + auto PPBase = PathParser::CreateBegin(base.__pn_); + while (PP && PPBase && PP.State == PPBase.State && + *PP == *PPBase) { + ++PP; + ++PPBase; + } + + // If there is no mismatch, return ".". + if (!PP && !PPBase) + return "."; + + // Otherwise, determine the number of elements, 'n', which are not dot or + // dot-dot minus the number of dot-dot elements. + int ElemCount = DetermineLexicalElementCount(PPBase); + if (ElemCount < 0) + return {}; + + // return a path constructed with 'n' dot-dot elements, followed by the the + // elements of '*this' after the mismatch. + path Result; + // FIXME: Reserve enough room in Result that it won't have to re-allocate. + while (ElemCount--) + Result /= ".."; + for (; PP; ++PP) + Result /= *PP; + return Result; +} + +//////////////////////////////////////////////////////////////////////////// +// path.comparisons +int path::__compare(string_view_t __s) const { + auto PP = PathParser::CreateBegin(__pn_); + auto PP2 = PathParser::CreateBegin(__s); + while (PP && PP2) { + int res = (*PP).compare(*PP2); + if (res != 0) return res; + ++PP; ++PP2; + } + if (PP.State == PP2.State && !PP) + return 0; + if (!PP) + return -1; + return 1; +} + +//////////////////////////////////////////////////////////////////////////// +// path.nonmembers +size_t hash_value(const path& __p) noexcept { + auto PP = PathParser::CreateBegin(__p.native()); + size_t hash_value = 0; + std::hash<string_view_t> hasher; + while (PP) { + hash_value = __hash_combine(hash_value, hasher(*PP)); + ++PP; + } + return hash_value; +} + +//////////////////////////////////////////////////////////////////////////// +// path.itr +path::iterator path::begin() const +{ + auto PP = PathParser::CreateBegin(__pn_); + iterator it; + it.__path_ptr_ = this; + it.__state_ = PP.State; + it.__entry_ = PP.RawEntry; + it.__stashed_elem_.__assign_view(*PP); + return it; +} + +path::iterator path::end() const +{ + iterator it{}; + it.__state_ = PathParser::PS_AtEnd; + it.__path_ptr_ = this; + return it; +} + +path::iterator& path::iterator::__increment() { + static_assert(__at_end == PathParser::PS_AtEnd, ""); + PathParser PP(__path_ptr_->native(), __entry_, __state_); + ++PP; + __state_ = PP.State; + __entry_ = PP.RawEntry; + __stashed_elem_.__assign_view(*PP); + return *this; +} + +path::iterator& path::iterator::__decrement() { + PathParser PP(__path_ptr_->native(), __entry_, __state_); + --PP; + __state_ = PP.State; + __entry_ = PP.RawEntry; + __stashed_elem_.__assign_view(*PP); + return *this; +} + + _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM diff --git a/libcxx/src/experimental/filesystem/path.cpp b/libcxx/src/experimental/filesystem/path.cpp deleted file mode 100644 index dd4026cfe13..00000000000 --- a/libcxx/src/experimental/filesystem/path.cpp +++ /dev/null @@ -1,448 +0,0 @@ -//===--------------------- filesystem/path.cpp ----------------------------===// -// -// 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. -// -//===----------------------------------------------------------------------===// -#include "experimental/filesystem" -#include "string_view" -#include "utility" - -namespace { namespace parser -{ -using namespace std; -using namespace std::experimental::filesystem; - -using string_view_t = path::__string_view; -using string_view_pair = pair<string_view_t, string_view_t>; -using PosPtr = path::value_type const*; - -struct PathParser { - enum ParserState : unsigned char { - // Zero is a special sentinel value used by default constructed iterators. - PS_BeforeBegin = 1, - PS_InRootName, - PS_InRootDir, - PS_InFilenames, - PS_InTrailingSep, - PS_AtEnd - }; - - const string_view_t Path; - string_view_t RawEntry; - ParserState State; - -private: - PathParser(string_view_t P, ParserState State) noexcept - : Path(P), State(State) {} - -public: - PathParser(string_view_t P, string_view_t E, unsigned char S) - : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) { - // S cannot be '0' or PS_BeforeBegin. - } - - static PathParser CreateBegin(string_view_t P) noexcept { - PathParser PP(P, PS_BeforeBegin); - PP.increment(); - return PP; - } - - static PathParser CreateEnd(string_view_t P) noexcept { - PathParser PP(P, PS_AtEnd); - return PP; - } - - PosPtr peek() const noexcept { - auto TkEnd = getNextTokenStartPos(); - auto End = getAfterBack(); - return TkEnd == End ? nullptr : TkEnd; - } - - void increment() noexcept { - const PosPtr End = getAfterBack(); - const PosPtr Start = getNextTokenStartPos(); - if (Start == End) - return makeState(PS_AtEnd); - - switch (State) { - case PS_BeforeBegin: { - PosPtr TkEnd = consumeSeparator(Start, End); - // If we consumed exactly two separators we have a root name. - if (TkEnd && TkEnd == Start + 2) { - // FIXME Do we need to consume a name or is '//' a root name on its own? - // what about '//.', '//..', '//...'? - auto NameEnd = consumeName(TkEnd, End); - if (NameEnd) - TkEnd = NameEnd; - return makeState(PS_InRootName, Start, TkEnd); - } - else if (TkEnd) - return makeState(PS_InRootDir, Start, TkEnd); - else - return makeState(PS_InFilenames, Start, consumeName(Start, End)); - } - - case PS_InRootName: - return makeState(PS_InRootDir, Start, consumeSeparator(Start, End)); - case PS_InRootDir: - return makeState(PS_InFilenames, Start, consumeName(Start, End)); - - case PS_InFilenames: { - PosPtr SepEnd = consumeSeparator(Start, End); - if (SepEnd != End) { - PosPtr TkEnd = consumeName(SepEnd, End); - if (TkEnd) - return makeState(PS_InFilenames, SepEnd, TkEnd); - } - return makeState(PS_InTrailingSep, Start, SepEnd); - } - - case PS_InTrailingSep: - return makeState(PS_AtEnd); - - case PS_AtEnd: - _LIBCPP_UNREACHABLE(); - } - } - - void decrement() noexcept { - const PosPtr REnd = getBeforeFront(); - const PosPtr RStart = getCurrentTokenStartPos() - 1; - - switch (State) { - case PS_AtEnd: { - // Try to consume a trailing separator or root directory first. - if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) { - if (SepEnd == REnd) - return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir, - Path.data(), RStart + 1); - // Check if we're seeing the root directory separator - auto PP = CreateBegin(Path); - bool InRootDir = PP.State == PS_InRootName && - &PP.RawEntry.back() == SepEnd; - return makeState(InRootDir ? PS_InRootDir : PS_InTrailingSep, - SepEnd + 1, RStart + 1); - } else { - PosPtr TkStart = consumeName(RStart, REnd); - if (TkStart == REnd + 2 && consumeSeparator(TkStart, REnd) == REnd) - return makeState(PS_InRootName, Path.data(), RStart + 1); - else - return makeState(PS_InFilenames, TkStart + 1, RStart + 1); - } - } - case PS_InTrailingSep: - return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1); - case PS_InFilenames: { - PosPtr SepEnd = consumeSeparator(RStart, REnd); - if (SepEnd == REnd) - return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir, - Path.data(), RStart + 1); - PosPtr TkEnd = consumeName(SepEnd, REnd); - if (TkEnd == REnd + 2 && consumeSeparator(TkEnd, REnd) == REnd) - return makeState(PS_InRootDir, SepEnd + 1, RStart + 1); - return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1); - } - case PS_InRootDir: - return makeState(PS_InRootName, Path.data(), RStart + 1); - case PS_InRootName: - case PS_BeforeBegin: - _LIBCPP_UNREACHABLE(); - } - } - - /// \brief Return a view with the "preferred representation" of the current - /// element. For example trailing separators are represented as a '.' - string_view_t operator*() const noexcept { - switch (State) { - case PS_BeforeBegin: - case PS_AtEnd: - return ""; - case PS_InRootDir: - return "/"; - case PS_InTrailingSep: - return "."; - case PS_InRootName: - case PS_InFilenames: - return RawEntry; - } - _LIBCPP_UNREACHABLE(); - } - - explicit operator bool() const noexcept { - return State != PS_BeforeBegin && State != PS_AtEnd; - } - - PathParser& operator++() noexcept { - increment(); - return *this; - } - - PathParser& operator--() noexcept { - decrement(); - return *this; - } - -private: - void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept { - State = NewState; - RawEntry = string_view_t(Start, End - Start); - } - void makeState(ParserState NewState) noexcept { - State = NewState; - RawEntry = {}; - } - - PosPtr getAfterBack() const noexcept { - return Path.data() + Path.size(); - } - - PosPtr getBeforeFront() const noexcept { - return Path.data() - 1; - } - - /// \brief Return a pointer to the first character after the currently - /// lexed element. - PosPtr getNextTokenStartPos() const noexcept { - switch (State) { - case PS_BeforeBegin: - return Path.data(); - case PS_InRootName: - case PS_InRootDir: - case PS_InFilenames: - return &RawEntry.back() + 1; - case PS_InTrailingSep: - case PS_AtEnd: - return getAfterBack(); - } - _LIBCPP_UNREACHABLE(); - } - - /// \brief Return a pointer to the first character in the currently lexed - /// element. - PosPtr getCurrentTokenStartPos() const noexcept { - switch (State) { - case PS_BeforeBegin: - case PS_InRootName: - return &Path.front(); - case PS_InRootDir: - case PS_InFilenames: - case PS_InTrailingSep: - return &RawEntry.front(); - case PS_AtEnd: - return &Path.back() + 1; - } - _LIBCPP_UNREACHABLE(); - } - - PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept { - if (P == End || *P != '/') - return nullptr; - const int Inc = P < End ? 1 : -1; - P += Inc; - while (P != End && *P == '/') - P += Inc; - return P; - } - - PosPtr consumeName(PosPtr P, PosPtr End) const noexcept { - if (P == End || *P == '/') - return nullptr; - const int Inc = P < End ? 1 : -1; - P += Inc; - while (P != End && *P != '/') - P += Inc; - return P; - } -}; - -string_view_pair separate_filename(string_view_t const & s) { - if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""}; - auto pos = s.find_last_of('.'); - if (pos == string_view_t::npos) - return string_view_pair{s, string_view_t{}}; - return string_view_pair{s.substr(0, pos), s.substr(pos)}; -} - -string_view_t createView(PosPtr S, PosPtr E) noexcept { - return {S, static_cast<size_t>(E - S) + 1}; -} - -}} // namespace parser - -_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM - -using parser::string_view_t; -using parser::string_view_pair; -using parser::PathParser; -using parser::createView; - -/////////////////////////////////////////////////////////////////////////////// -// path definitions -/////////////////////////////////////////////////////////////////////////////// - -constexpr path::value_type path::preferred_separator; - -path & path::replace_extension(path const & replacement) -{ - path p = extension(); - if (not p.empty()) { - __pn_.erase(__pn_.size() - p.native().size()); - } - if (!replacement.empty()) { - if (replacement.native()[0] != '.') { - __pn_ += "."; - } - __pn_.append(replacement.__pn_); - } - return *this; -} - -/////////////////////////////////////////////////////////////////////////////// -// path.decompose - -string_view_t path::__root_name() const -{ - auto PP = PathParser::CreateBegin(__pn_); - if (PP.State == PathParser::PS_InRootName) - return *PP; - return {}; -} - -string_view_t path::__root_directory() const -{ - auto PP = PathParser::CreateBegin(__pn_); - if (PP.State == PathParser::PS_InRootName) - ++PP; - if (PP.State == PathParser::PS_InRootDir) - return *PP; - return {}; -} - -string_view_t path::__root_path_raw() const -{ - auto PP = PathParser::CreateBegin(__pn_); - if (PP.State == PathParser::PS_InRootName) { - auto NextCh = PP.peek(); - if (NextCh && *NextCh == '/') { - ++PP; - return createView(__pn_.data(), &PP.RawEntry.back()); - } - return PP.RawEntry; - } - if (PP.State == PathParser::PS_InRootDir) - return *PP; - return {}; -} - -string_view_t path::__relative_path() const -{ - auto PP = PathParser::CreateBegin(__pn_); - while (PP.State <= PathParser::PS_InRootDir) - ++PP; - if (PP.State == PathParser::PS_AtEnd) - return {}; - return createView(PP.RawEntry.data(), &__pn_.back()); -} - -string_view_t path::__parent_path() const -{ - if (empty()) - return {}; - auto PP = PathParser::CreateEnd(__pn_); - --PP; - if (PP.RawEntry.data() == __pn_.data()) - return {}; - --PP; - return createView(__pn_.data(), &PP.RawEntry.back()); -} - -string_view_t path::__filename() const -{ - if (empty()) return {}; - return *(--PathParser::CreateEnd(__pn_)); -} - -string_view_t path::__stem() const -{ - return parser::separate_filename(__filename()).first; -} - -string_view_t path::__extension() const -{ - return parser::separate_filename(__filename()).second; -} - -//////////////////////////////////////////////////////////////////////////// -// path.comparisons -int path::__compare(string_view_t __s) const { - auto PP = PathParser::CreateBegin(__pn_); - auto PP2 = PathParser::CreateBegin(__s); - while (PP && PP2) { - int res = (*PP).compare(*PP2); - if (res != 0) return res; - ++PP; ++PP2; - } - if (PP.State == PP2.State && PP.State == PathParser::PS_AtEnd) - return 0; - if (PP.State == PathParser::PS_AtEnd) - return -1; - return 1; -} - -//////////////////////////////////////////////////////////////////////////// -// path.nonmembers -size_t hash_value(const path& __p) noexcept { - auto PP = PathParser::CreateBegin(__p.native()); - size_t hash_value = 0; - std::hash<string_view_t> hasher; - while (PP) { - hash_value = __hash_combine(hash_value, hasher(*PP)); - ++PP; - } - return hash_value; -} - -//////////////////////////////////////////////////////////////////////////// -// path.itr -path::iterator path::begin() const -{ - auto PP = PathParser::CreateBegin(__pn_); - iterator it; - it.__path_ptr_ = this; - it.__state_ = PP.State; - it.__entry_ = PP.RawEntry; - it.__stashed_elem_.__assign_view(*PP); - return it; -} - -path::iterator path::end() const -{ - iterator it{}; - it.__state_ = PathParser::PS_AtEnd; - it.__path_ptr_ = this; - return it; -} - -path::iterator& path::iterator::__increment() { - static_assert(__at_end == PathParser::PS_AtEnd, ""); - PathParser PP(__path_ptr_->native(), __entry_, __state_); - ++PP; - __state_ = PP.State; - __entry_ = PP.RawEntry; - __stashed_elem_.__assign_view(*PP); - return *this; -} - -path::iterator& path::iterator::__decrement() { - PathParser PP(__path_ptr_->native(), __entry_, __state_); - --PP; - __state_ = PP.State; - __entry_ = PP.RawEntry; - __stashed_elem_.__assign_view(*PP); - return *this; -} - -_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM diff --git a/libcxx/test/libcxx/experimental/filesystem/class.path/path.member/path.append.pass.cpp b/libcxx/test/libcxx/experimental/filesystem/class.path/path.member/path.append.pass.cpp deleted file mode 100644 index c43ea078f98..00000000000 --- a/libcxx/test/libcxx/experimental/filesystem/class.path/path.member/path.append.pass.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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&) -// path operator/(path const&, path const&) - - -#define _LIBCPP_DEBUG 0 -#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : (void)::AssertCount++) -int AssertCount = 0; - -#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; - -int main() -{ - using namespace fs; - { - path lhs("//foo"); - path rhs("/bar"); - assert(AssertCount == 0); - lhs /= rhs; - assert(AssertCount == 0); - } - { - path lhs("//foo"); - path rhs("/bar"); - assert(AssertCount == 0); - (void)(lhs / rhs); - assert(AssertCount == 0); - } - { - path lhs("//foo"); - path rhs("//bar"); - assert(AssertCount == 0); - lhs /= rhs; - assert(AssertCount == 1); - AssertCount = 0; - } - { - path lhs("//foo"); - path rhs("//bar"); - assert(AssertCount == 0); - (void)(lhs / rhs); - assert(AssertCount == 1); - } - // FIXME The same error is not diagnosed for the append(Source) and - // append(It, It) overloads. -} diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp index e5b3c848255..0ea672bdf1e 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.itr/iterator.pass.cpp @@ -83,14 +83,14 @@ void checkBeginEndBasic() { } { path p("//root_name//first_dir////second_dir"); - const path expect[] = {"//root_name", "/", "first_dir", "second_dir"}; + const path expect[] = {"/", "root_name", "first_dir", "second_dir"}; assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect))); assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect))); } { path p("////foo/bar/baz///"); - const path expect[] = {"/", "foo", "bar", "baz", "."}; + const path expect[] = {"/", "foo", "bar", "baz", ""}; assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect))); assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect))); diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp index ca072d16114..11bc5dd9b86 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp @@ -31,6 +31,7 @@ #include "test_iterators.h" #include "count_new.hpp" #include "filesystem_test_helper.hpp" +#include "verbose_assert.h" struct AppendOperatorTestcase { @@ -45,13 +46,22 @@ 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("/p2")} + , {S("p1/"), S("/p2"), S("/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")} + , {S("/p1"), S("p2"), S("/p1/p2")} + , {S("/p1"), S("/p2"), S("/p2")} + , {S("/p1/p3"), S("p2"), S("/p1/p3/p2")} + , {S("/p1/p3/"), S("p2"), S("/p1/p3/p2")} + , {S("/p1/"), S("p2"), S("/p1/p2")} + , {S("/p1/p3/"), S("/p2/p4"), S("/p2/p4")} + , {S("/"), S(""), S("/")} + , {S("/p1"), S("/p2/"), S("/p2/")} + , {S("p1"), S(""), S("p1/")} + , {S("p1/"), S(""), S("p1/")} }; @@ -59,7 +69,8 @@ const AppendOperatorTestcase LongLHSCases[] = { {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("/p2")} + , {S("/p1"), S("p2"), S("/p1/p2")} }; #undef S @@ -98,7 +109,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) DisableAllocationGuard g; LHS /= RHS; } - assert(LHS == E); + ASSERT_PRED(PathEq, LHS , E); } // basic_string_view { @@ -108,7 +119,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) DisableAllocationGuard g; LHS /= RHS; } - assert(LHS == E); + assert(PathEq(LHS, E)); } // CharT* { @@ -118,7 +129,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) DisableAllocationGuard g; LHS /= RHS; } - assert(LHS == E); + assert(PathEq(LHS, E)); } { path LHS(L); PathReserve(LHS, ReserveSize); @@ -127,7 +138,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) DisableAllocationGuard g; LHS.append(RHS, StrEnd(RHS)); } - assert(LHS == E); + 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 @@ -143,7 +154,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) if (DisableAllocations) g.requireExactly(0); LHS /= RHS; } - assert(LHS == E); + assert(PathEq(LHS, E)); } { path LHS(L); PathReserve(LHS, ReserveSize); @@ -154,7 +165,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) if (DisableAllocations) g.requireExactly(0); LHS.append(RHS, REnd); } - assert(LHS == E); + assert(PathEq(LHS, E)); } } @@ -171,17 +182,18 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC) const Ptr E = TC.expect; // basic_string { - path LHS(L); + path Result(L); Str RHS(R); - path& Ref = (LHS /= RHS); - assert(LHS == E); - assert(&Ref == &LHS); + path& Ref = (Result /= RHS); + ASSERT_EQ(Result, E) + << DISPLAY(L) << DISPLAY(R); + assert(&Ref == &Result); } { path LHS(L); Str RHS(R); path& Ref = LHS.append(RHS); - assert(LHS == E); + assert(PathEq(LHS, E)); assert(&Ref == &LHS); } // basic_string_view @@ -189,14 +201,14 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC) path LHS(L); StrView RHS(R); path& Ref = (LHS /= RHS); - assert(LHS == E); + assert(PathEq(LHS, E)); assert(&Ref == &LHS); } { path LHS(L); StrView RHS(R); path& Ref = LHS.append(RHS); - assert(LHS == E); + assert(PathEq(LHS, E)); assert(&Ref == &LHS); } // Char* @@ -204,21 +216,22 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC) path LHS(L); Str RHS(R); path& Ref = (LHS /= RHS); - assert(LHS == E); + assert(PathEq(LHS, E)); assert(&Ref == &LHS); } { path LHS(L); Ptr RHS(R); path& Ref = LHS.append(RHS); - assert(LHS == E); + assert(PathEq(LHS, E)); assert(&Ref == &LHS); } { path LHS(L); Ptr RHS(R); path& Ref = LHS.append(RHS, StrEnd(RHS)); - assert(LHS == E); + ASSERT_PRED(PathEq, LHS, E) + << DISPLAY(L) << DISPLAY(R); assert(&Ref == &LHS); } // iterators @@ -226,13 +239,13 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC) path LHS(L); InputIter RHS(R); path& Ref = (LHS /= RHS); - assert(LHS == E); + assert(PathEq(LHS, E)); assert(&Ref == &LHS); } { path LHS(L); InputIter RHS(R); path& Ref = LHS.append(RHS); - assert(LHS == E); + assert(PathEq(LHS, E)); assert(&Ref == &LHS); } { @@ -240,7 +253,7 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC) InputIter RHS(R); InputIter REnd(StrEnd(R)); path& Ref = LHS.append(RHS, REnd); - assert(LHS == E); + assert(PathEq(LHS, E)); assert(&Ref == &LHS); } } @@ -304,11 +317,14 @@ 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); + 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_PRED(PathEq, Res, (const char*)TC.expect) + << DISPLAY(LHS_In) << DISPLAY(RHS_In); + assert(&Res == &LHS); } doAppendSourceTest<char> (TC); doAppendSourceTest<wchar_t> (TC); diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp index ea356782678..a2af094128d 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp @@ -26,6 +26,7 @@ // // size_t hash_value(path const&) noexcept; + #include "filesystem_include.hpp" #include <type_traits> #include <vector> @@ -35,7 +36,7 @@ #include "test_iterators.h" #include "count_new.hpp" #include "filesystem_test_helper.hpp" - +#include "verbose_assert.h" struct PathCompareTest { const char* LHS; @@ -57,8 +58,9 @@ const PathCompareTest CompareTestCases[] = {"a/b/c", "b/a/c", -1}, {"a/b", "a/b/c", -1}, {"a/b/c", "a/b", 1}, - {"a/b/", "a/b/.", 0}, - {"a/b//////", "a/b/////.", 0}, + {"a/b/", "a/b/.", -1}, + {"a/b/", "a/b", 1}, + {"a/b//////", "a/b/////.", -1}, {"a/.././b", "a///..//.////b", 0}, {"//foo//bar///baz////", "//foo/bar/baz/", 0}, // duplicate separators {"///foo/bar", "/foo/bar", 0}, // "///" is not a root directory @@ -94,8 +96,13 @@ int main() int ret2 = normalize_ret(p1.compare(R)); int ret3 = normalize_ret(p1.compare(TC.RHS)); int ret4 = normalize_ret(p1.compare(RV)); - assert(ret1 == ret2 && ret1 == ret3 && ret1 == ret4); - assert(ret1 == E); + + g.release(); + ASSERT_EQ(ret1, ret2); + ASSERT_EQ(ret1, ret3); + ASSERT_EQ(ret1, ret4); + ASSERT_EQ(ret1, E) + << DISPLAY(TC.LHS) << DISPLAY(TC.RHS); // check signatures ASSERT_NOEXCEPT(p1.compare(p2)); diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp index 377b94e52c7..ecc4c0c30a0 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.decompose/empty.fail.cpp @@ -18,6 +18,7 @@ // UNSUPPORTED: clang-3.3, clang-3.4, clang-3.5, clang-3.6, clang-3.7, clang-3.8 #include "filesystem_include.hpp" + #include "test_macros.h" int main () diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp index 6dd24b51156..f959e43a304 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.decompose/path.decompose.pass.cpp @@ -53,6 +53,14 @@ #include "test_iterators.h" #include "count_new.hpp" #include "filesystem_test_helper.hpp" +#include "assert_checkpoint.h" +#include "verbose_assert.h" + +struct ComparePathExact { + bool operator()(std::string const& LHS, std::string const& RHS) const { + return LHS == RHS; + } +}; struct PathDecomposeTestcase { @@ -72,80 +80,89 @@ const PathDecomposeTestcase PathTestCases[] = , {".", {"."}, "", "", "", ".", "", "."} , {"..", {".."}, "", "", "", "..", "", ".."} , {"foo", {"foo"}, "", "", "", "foo", "", "foo"} - , {"/", {"/"}, "/", "", "/", "", "", "/"} + , {"/", {"/"}, "/", "", "/", "", "/", ""} , {"/foo", {"/", "foo"}, "/", "", "/", "foo", "/", "foo"} - , {"foo/", {"foo", "."}, "", "", "", "foo/", "foo", "."} - , {"/foo/", {"/", "foo", "."}, "/", "", "/", "foo/", "/foo", "."} + , {"foo/", {"foo", ""}, "", "", "", "foo/", "foo", ""} + , {"/foo/", {"/", "foo", ""}, "/", "", "/", "foo/", "/foo", ""} , {"foo/bar", {"foo","bar"}, "", "", "", "foo/bar", "foo", "bar"} , {"/foo//bar", {"/","foo","bar"}, "/", "", "/", "foo/bar", "/foo", "bar"} - , {"//net", {"//net"}, "//net", "//net", "", "", "", "//net"} - , {"//net/foo", {"//net", "/", "foo"}, "//net/", "//net", "/", "foo", "//net/", "foo"} - , {"///foo///", {"/", "foo", "."}, "/", "", "/", "foo///", "///foo", "."} + , {"//net", {"/", "net"}, "/", "", "/", "net", "/", "net"} + , {"//net/foo", {"/", "net", "foo"}, "/", "", "/", "net/foo", "/net", "foo"} + , {"///foo///", {"/", "foo", ""}, "/", "", "/", "foo///", "///foo", ""} , {"///foo///bar", {"/", "foo", "bar"}, "/", "", "/", "foo///bar", "///foo", "bar"} , {"/.", {"/", "."}, "/", "", "/", ".", "/", "."} - , {"./", {".", "."}, "", "", "", "./", ".", "."} + , {"./", {".", ""}, "", "", "", "./", ".", ""} , {"/..", {"/", ".."}, "/", "", "/", "..", "/", ".."} - , {"../", {"..", "."}, "", "", "", "../", "..", "."} + , {"../", {"..", ""}, "", "", "", "../", "..", ""} , {"foo/.", {"foo", "."}, "", "", "", "foo/.", "foo", "."} , {"foo/..", {"foo", ".."}, "", "", "", "foo/..", "foo", ".."} - , {"foo/./", {"foo", ".", "."}, "", "", "", "foo/./", "foo/.", "."} + , {"foo/./", {"foo", ".", ""}, "", "", "", "foo/./", "foo/.", ""} , {"foo/./bar", {"foo", ".", "bar"}, "", "", "", "foo/./bar", "foo/.", "bar"} - , {"foo/../", {"foo", "..", "."}, "", "", "", "foo/../", "foo/..", "."} + , {"foo/../", {"foo", "..", ""}, "", "", "", "foo/../", "foo/..", ""} , {"foo/../bar", {"foo", "..", "bar"}, "", "", "", "foo/../bar", "foo/..", "bar"} , {"c:", {"c:"}, "", "", "", "c:", "", "c:"} - , {"c:/", {"c:", "."}, "", "", "", "c:/", "c:", "."} + , {"c:/", {"c:", ""}, "", "", "", "c:/", "c:", ""} , {"c:foo", {"c:foo"}, "", "", "", "c:foo", "", "c:foo"} , {"c:/foo", {"c:", "foo"}, "", "", "", "c:/foo", "c:", "foo"} - , {"c:foo/", {"c:foo", "."}, "", "", "", "c:foo/", "c:foo", "."} - , {"c:/foo/", {"c:", "foo", "."}, "", "", "", "c:/foo/", "c:/foo", "."} + , {"c:foo/", {"c:foo", ""}, "", "", "", "c:foo/", "c:foo", ""} + , {"c:/foo/", {"c:", "foo", ""}, "", "", "", "c:/foo/", "c:/foo", ""} , {"c:/foo/bar", {"c:", "foo", "bar"}, "", "", "", "c:/foo/bar", "c:/foo", "bar"} , {"prn:", {"prn:"}, "", "", "", "prn:", "", "prn:"} , {"c:\\", {"c:\\"}, "", "", "", "c:\\", "", "c:\\"} , {"c:\\foo", {"c:\\foo"}, "", "", "", "c:\\foo", "", "c:\\foo"} , {"c:foo\\", {"c:foo\\"}, "", "", "", "c:foo\\", "", "c:foo\\"} , {"c:\\foo\\", {"c:\\foo\\"}, "", "", "", "c:\\foo\\", "", "c:\\foo\\"} - , {"c:\\foo/", {"c:\\foo", "."}, "", "", "", "c:\\foo/", "c:\\foo", "."} + , {"c:\\foo/", {"c:\\foo", ""}, "", "", "", "c:\\foo/", "c:\\foo", ""} , {"c:/foo\\bar", {"c:", "foo\\bar"}, "", "", "", "c:/foo\\bar", "c:", "foo\\bar"} - , {"//", {"//"}, "//", "//", "", "", "", "//"} + , {"//", {"/"}, "/", "", "/", "", "/", ""} }; void decompPathTest() { using namespace fs; for (auto const & TC : PathTestCases) { - path p(TC.raw); - assert(p == TC.raw); + CHECKPOINT(TC.raw.c_str()); + fs::path p(TC.raw); + ASSERT(p == TC.raw); - assert(p.root_path() == TC.root_path); - assert(p.has_root_path() != TC.root_path.empty()); + ASSERT_EQ(p.root_path(), TC.root_path); + ASSERT_NEQ(p.has_root_path(), TC.root_path.empty()); - assert(p.root_name() == TC.root_name); - assert(p.has_root_name() != TC.root_name.empty()); + ASSERT(p.root_name().native().empty()) + << DISPLAY(p.root_name()); + ASSERT_EQ(p.root_name(),TC.root_name); + ASSERT_NEQ(p.has_root_name(), TC.root_name.empty()); - assert(p.root_directory() == TC.root_directory); - assert(p.has_root_directory() != TC.root_directory.empty()); + ASSERT_EQ(p.root_directory(), TC.root_directory); + ASSERT_NEQ(p.has_root_directory(), TC.root_directory.empty()); - assert(p.relative_path() == TC.relative_path); - assert(p.has_relative_path() != TC.relative_path.empty()); + ASSERT_EQ(p.relative_path(), TC.relative_path); + ASSERT_NEQ(p.has_relative_path(), TC.relative_path.empty()); - assert(p.parent_path() == TC.parent_path); - assert(p.has_parent_path() != TC.parent_path.empty()); + ASSERT_EQ(p.parent_path(), TC.parent_path); + ASSERT_NEQ(p.has_parent_path(), TC.parent_path.empty()); - assert(p.filename() == TC.filename); - assert(p.has_filename() != TC.filename.empty()); + ASSERT_EQ(p.filename(), TC.filename); + ASSERT_NEQ(p.has_filename(), TC.filename.empty()); - assert(p.is_absolute() == p.has_root_directory()); - assert(p.is_relative() != p.is_absolute()); + ASSERT_EQ(p.is_absolute(), p.has_root_directory()); + ASSERT_NEQ(p.is_relative(), p.is_absolute()); + if (p.empty()) + ASSERT(p.is_relative()); - assert(checkCollectionsEqual(p.begin(), p.end(), - TC.elements.begin(), TC.elements.end())); + ASSERT_COLLECTION_EQ_COMP( + p.begin(), p.end(), + TC.elements.begin(), TC.elements.end(), + ComparePathExact() + ); // check backwards std::vector<fs::path> Parts; for (auto it = p.end(); it != p.begin(); ) Parts.push_back(*--it); - assert(checkCollectionsEqual(Parts.begin(), Parts.end(), - TC.elements.rbegin(), TC.elements.rend())); + ASSERT_COLLECTION_EQ_COMP(Parts.begin(), Parts.end(), + TC.elements.rbegin(), TC.elements.rend(), + ComparePathExact()); } } @@ -163,10 +180,12 @@ const FilenameDecompTestcase FilenameTestCases[] = {"", "", "", ""} , {".", ".", ".", ""} , {"..", "..", "..", ""} - , {"/", "/", "/", ""} + , {"/", "", "", ""} , {"foo", "foo", "foo", ""} , {"/foo/bar.txt", "bar.txt", "bar", ".txt"} , {"foo..txt", "foo..txt", "foo.", ".txt"} + , {".profile", ".profile", ".profile", ""} + , {".profile.txt", ".profile.txt", ".profile", ".txt"} }; @@ -174,18 +193,19 @@ void decompFilenameTest() { using namespace fs; for (auto const & TC : FilenameTestCases) { - path p(TC.raw); - assert(p == TC.raw); + CHECKPOINT(TC.raw.c_str()); + fs::path p(TC.raw); + ASSERT_EQ(p, TC.raw); ASSERT_NOEXCEPT(p.empty()); - assert(p.filename() == TC.filename); - assert(p.has_filename() != TC.filename.empty()); + ASSERT_EQ(p.filename(), TC.filename); + ASSERT_NEQ(p.has_filename(), TC.filename.empty()); - assert(p.stem() == TC.stem); - assert(p.has_stem() != TC.stem.empty()); + ASSERT_EQ(p.stem(), TC.stem); + ASSERT_NEQ(p.has_stem(), TC.stem.empty()); - assert(p.extension() == TC.extension); - assert(p.has_extension() != TC.extension.empty()); + ASSERT_EQ(p.extension(), TC.extension); + ASSERT_NEQ(p.has_extension(), TC.extension.empty()); } } diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_normal.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_normal.pass.cpp new file mode 100644 index 00000000000..c897ab60f73 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_normal.pass.cpp @@ -0,0 +1,142 @@ +//===----------------------------------------------------------------------===// +// +// 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 lexically_normal() const; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +int main() { + // clang-format off + struct { + std::string input; + std::string expect; + } TestCases[] = { + {"", ""}, + {"/a/b/c", "/a/b/c"}, + {"/a/b//c", "/a/b/c"}, + {"foo/./bar/..", "foo/"}, + {"foo/.///bar/../", "foo/"}, + {"/a/b/", "/a/b/"}, + {"a/b", "a/b"}, + {"a/b/.", "a/b/"}, + {"a/b/./", "a/b/"}, + {"a/..", "."}, + {".", "."}, + {"./", "."}, + {"./.", "."}, + {"./..", ".."}, + {"..", ".."}, + {"../..", "../.."}, + {"/../", "/"}, + {"/../..", "/"}, + {"/../../", "/"}, + {"..", ".."}, + {"../", ".."}, + {"/a/b/c/../", "/a/b/"}, + {"/a/b/./", "/a/b/"}, + {"/a/b/c/../d", "/a/b/d"}, + {"/a/b/c/../d/", "/a/b/d/"}, + {"//a/", "/a/"}, + {"//a/b/", "/a/b/"}, + {"//a/b/.", "/a/b/"}, + {"//a/..", "/"}, + ///===---------------------------------------------------------------===// + /// Tests specifically for the clauses under [fs.path.generic]p6 + ///===---------------------------------------------------------------===// + // p1: If the path is empty, stop. + {"", ""}, + // p2: Replace each slash character in the root-name with a preferred + // separator. + {"NO_ROOT_NAME_ON_LINUX", "NO_ROOT_NAME_ON_LINUX"}, + // p3: Replace each directory-separator with a preferred-separator. + // [ Note: The generic pathname grammar ([fs.path.generic]) defines + // directory-separator as one or more slashes and preferred-separators. + // — end note ] + {"/", "/"}, + {"//", "/"}, + {"///", "/"}, + {"a/b", "a/b"}, + {"a//b", "a/b"}, + {"a///b", "a/b"}, + {"a/b/", "a/b/"}, + {"a/b//", "a/b/"}, + {"a/b///", "a/b/"}, + {"///a////b//////", "/a/b/"}, + // p4: Remove each dot filename and any immediately following directory + // separators + {"foo/.", "foo/"}, + {"foo/./bar/.", "foo/bar/"}, + {"./foo/././bar/./", "foo/bar/"}, + {".///foo//.////./bar/.///", "foo/bar/"}, + // p5: As long as any appear, remove a non-dot-dot filename immediately + // followed by a directory-separator and a dot-dot filename, along with + // any immediately following directory separator. + {"foo/..", "."}, + {"foo/../", "."}, + {"foo/bar/..", "foo/"}, + {"foo/bar/../", "foo/"}, + {"foo/bar/../..", "."}, + {"foo/bar/../../", "."}, + {"foo/bar/baz/../..", "foo/"}, + {"foo/bar/baz/../../", "foo/"}, + {"foo/bar/./..", "foo/"}, + {"foo/bar/./../", "foo/"}, + // p6: If there is a root-directory, remove all dot-dot filenames and any + // directory-separators immediately following them. [ Note: These dot-dot + // filenames attempt to refer to nonexistent parent directories. — end note ] + {"/..", "/"}, + {"/../", "/"}, + {"/foo/../..", "/"}, + {"/../foo", "/foo"}, + {"/../foo/../..", "/"}, + // p7: If the last filename is dot-dot, remove any trailing + // directory-separator. + {"../", ".."}, + {"../../", "../.."}, + {"foo/../bar/../..///", ".."}, + {"foo/../bar/..//..///../", "../.."}, + // p8: If the path is empty, add a dot + {".", "."}, + {"./", "."}, + {"foo/..", "."} + }; + // clang-format on + int ID = 0; + bool Failed = false; + for (auto& TC : TestCases) { + ++ID; + fs::path p(TC.input); + const fs::path output = p.lexically_normal(); + if (!PathEq(output, TC.expect)) { + Failed = true; + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + std::cerr << " Output: '" << output.native() << "'"; + std::cerr << std::endl; + } + } + return Failed; +} diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp new file mode 100644 index 00000000000..8b56a19b716 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// 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 lexically_relative(const path& p) const; +// path lexically_proximate(const path& p) const; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +int main() { + // clang-format off + struct { + std::string input; + std::string base; + std::string expect; + } TestCases[] = { + {"", "", "."}, + {"/", "a", ""}, + {"a", "/", ""}, + {"//net", "a", ""}, + {"a", "//net", ""}, + {"//net/", "//net", ""}, + {"//net", "//net/", ".."}, + {"//base", "a", ""}, + {"a", "a", "."}, + {"a/b", "a/b", "."}, + {"a/b/c/", "a/b/c/", "."}, + {"//net", "//net", "."}, + {"//net/", "//net/", "."}, + {"//net/a/b", "//net/a/b", "."}, + {"/a/d", "/a/b/c", "../../d"}, + {"/a/b/c", "/a/d", "../b/c"}, + {"a/b/c", "a", "b/c"}, + {"a/b/c", "a/b/c/x/y", "../.."}, + {"a/b/c", "a/b/c", "."}, + {"a/b", "c/d", "../../a/b"} + }; + // clang-format on + int ID = 0; + bool Failed = false; + for (auto& TC : TestCases) { + ++ID; + const fs::path p(TC.input); + const fs::path output = p.lexically_relative(TC.base); + auto ReportErr = [&](const char* Testing, fs::path const& Output, + fs::path const& Expected) { + Failed = true; + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Testing: " << Testing << "\n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Base: '" << TC.base << "'\n"; + std::cerr << " Expected: '" << Expected << "'\n"; + std::cerr << " Output: '" << Output.native() << "'"; + std::cerr << std::endl; + }; + if (!PathEq(output, TC.expect)) + ReportErr("path::lexically_relative", output, TC.expect); + const fs::path proximate_output = p.lexically_proximate(TC.base); + // [path.gen] lexically_proximate + // Returns: If the value of lexically_relative(base) is not an empty path, + // return it.Otherwise return *this. + const fs::path proximate_expected = output.native().empty() ? p + : output; + if (!PathEq(proximate_expected, proximate_output)) + ReportErr("path::lexically_proximate", proximate_output, proximate_expected); + } + return Failed; +} diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp index 7e329376c93..a757cbb88fd 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp @@ -28,7 +28,6 @@ #include "min_allocator.h" #include "filesystem_test_helper.hpp" - MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp index d3df09ed97e..56b2787fbb6 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.generic.obs/named_overloads.pass.cpp @@ -30,7 +30,6 @@ #include "min_allocator.h" #include "filesystem_test_helper.hpp" - MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); int main() diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp index 84cdd521429..ec885f15c8c 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.modifiers/remove_filename.pass.cpp @@ -23,7 +23,7 @@ #include "test_iterators.h" #include "count_new.hpp" #include "filesystem_test_helper.hpp" - +#include "verbose_assert.h" struct RemoveFilenameTestcase { const char* value; @@ -33,27 +33,29 @@ struct RemoveFilenameTestcase { const RemoveFilenameTestcase TestCases[] = { {"", ""} - , {"/", ""} - , {"//", ""} - , {"///", ""} + , {"/", "/"} + , {"//", "//"} + , {"///", "///"} , {"\\", ""} , {".", ""} , {"..", ""} , {"/foo", "/"} - , {"//foo", ""} - , {"//foo/", ""} - , {"//foo///", ""} - , {"///foo", "/"} - , {"///foo/", "///foo"} - , {"/foo/", "/foo"} - , {"/foo/.", "/foo"} - , {"/foo/..", "/foo"} - , {"/foo/////", "/foo"} + , {"foo/bar", "foo/"} + , {"foo/", "foo/"} + , {"//foo", "//"} + , {"//foo/", "//foo/"} + , {"//foo///", "//foo///"} + , {"///foo", "///"} + , {"///foo/", "///foo/"} + , {"/foo/", "/foo/"} + , {"/foo/.", "/foo/"} + , {"/foo/..", "/foo/"} + , {"/foo/////", "/foo/////"} , {"/foo\\\\", "/"} - , {"/foo//\\/", "/foo//\\"} - , {"///foo", "/"} + , {"/foo//\\/", "/foo//\\/"} + , {"///foo", "///"} , {"file.txt", ""} - , {"bar/../baz/./file.txt", "bar/../baz/."} + , {"bar/../baz/./file.txt", "bar/../baz/./"} }; int main() @@ -64,16 +66,8 @@ int main() path p(p_orig); assert(p == TC.value); path& Ref = (p.remove_filename()); - assert(p == TC.expect); + ASSERT_EQ(p, TC.expect) << DISPLAY(p_orig); assert(&Ref == &p); - { - const path parentp = p_orig.parent_path(); - if (parentp == p_orig.root_name()) { - - assert(p.empty()); - } else { - assert(p == parentp); - } - } + assert(!p.has_filename()); } } diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp index fb7741110cc..bfb3b6a19cd 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.modifiers/replace_filename.pass.cpp @@ -23,7 +23,8 @@ #include "test_iterators.h" #include "count_new.hpp" #include "filesystem_test_helper.hpp" - +#include "assert_checkpoint.h" +#include "verbose_assert.h" struct ReplaceFilenameTestcase { const char* value; @@ -36,9 +37,9 @@ const ReplaceFilenameTestcase TestCases[] = {"/foo", "/bar", "bar"} , {"/foo", "/", ""} , {"foo", "bar", "bar"} - , {"/", "bar", "bar"} + , {"/", "/bar", "bar"} , {"\\", "bar", "bar"} - , {"///", "bar", "bar"} + , {"///", "///bar", "bar"} , {"\\\\", "bar", "bar"} , {"\\/\\", "\\/bar", "bar"} , {".", "bar", "bar"} @@ -52,9 +53,11 @@ int main() using namespace fs; for (auto const & TC : TestCases) { path p(TC.value); - assert(p == TC.value); + ASSERT_EQ(p, TC.value); path& Ref = (p.replace_filename(TC.filename)); - assert(p == TC.expect); + ASSERT_EQ(p, TC.expect) + << DISPLAY(TC.value) + << DISPLAY(TC.filename); assert(&Ref == &p); // Tests Effects "as-if": remove_filename() append(filename) { @@ -62,7 +65,7 @@ int main() path replace(TC.filename); p2.remove_filename(); p2 /= replace; - assert(p2 == p); + ASSERT_EQ(p, p2); } } } diff --git a/libcxx/test/std/experimental/filesystem/fs.enum/enum.perm_options.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.enum/enum.perm_options.pass.cpp new file mode 100644 index 00000000000..c1066b63ba3 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/fs.enum/enum.perm_options.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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> + +// enum class perm_options; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> +#include <sys/stat.h> + +#include "test_macros.h" +#include "check_bitmask_types.hpp" + + +constexpr fs::perm_options ME(int val) { + return static_cast<fs::perm_options>(val); +} + +int main() { + typedef fs::perm_options E; + static_assert(std::is_enum<E>::value, ""); + + // Check that E is a scoped enum by checking for conversions. + typedef std::underlying_type<E>::type UT; + static_assert(!std::is_convertible<E, UT>::value, ""); + + static_assert(std::is_same<UT, unsigned char >::value, ""); // Implementation detail + + typedef check_bitmask_type<E, E::replace, E::nofollow> BitmaskTester; + assert(BitmaskTester::check()); + + static_assert( + E::replace == ME(1) && + E::add == ME(2) && + E::remove == ME(4) && + E::nofollow == ME(8), + "Expected enumeration values do not match"); +} diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp index 97d168a4a53..9b73aedc8ef 100644 --- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.absolute/absolute.pass.cpp @@ -28,89 +28,30 @@ TEST_SUITE(filesystem_absolute_path_test_suite) TEST_CASE(absolute_signature_test) { const path p; ((void)p); + std::error_code ec; ASSERT_NOT_NOEXCEPT(absolute(p)); - ASSERT_NOT_NOEXCEPT(absolute(p, p)); + ASSERT_NOT_NOEXCEPT(absolute(p, ec)); } -// There are 4 cases is the proposal for absolute path. -// Each scope tests one of the cases. -TEST_CASE(absolute_path_test) -{ - // has_root_name() && has_root_directory() - { - const path p("//net/foo"); - const path base("//net/bar/baz"); - TEST_REQUIRE(p.has_root_name()); - TEST_REQUIRE(p.has_root_directory()); - TEST_CHECK(p.is_absolute()); - path ret = absolute(p, base); - TEST_CHECK(ret.is_absolute()); - TEST_CHECK(ret == p); - } - // !has_root_name() && has_root_directory() - { - const path p("/foo"); - const path base("//net/bar"); - TEST_REQUIRE(not p.has_root_name()); - TEST_REQUIRE(p.has_root_directory()); - TEST_CHECK(p.is_absolute()); - // ensure absolute(base) is not recursively called - TEST_REQUIRE(base.has_root_name()); - TEST_REQUIRE(base.has_root_directory()); - - path ret = absolute(p, base); - TEST_CHECK(ret.is_absolute()); - TEST_CHECK(ret.has_root_name()); - TEST_CHECK(ret.root_name() == path("//net")); - TEST_CHECK(ret.has_root_directory()); - TEST_CHECK(ret.root_directory() == path("/")); - TEST_CHECK(ret == path("//net/foo")); - } - // has_root_name() && !has_root_directory() - { - const path p("//net"); - const path base("//net/foo/bar"); - TEST_REQUIRE(p.has_root_name()); - TEST_REQUIRE(not p.has_root_directory()); - TEST_CHECK(not p.is_absolute()); - // absolute is called recursively on base. The following conditions - // must be true for it to return base unmodified - TEST_REQUIRE(base.has_root_name()); - TEST_REQUIRE(base.has_root_directory()); - path ret = absolute(p, base); - const path expect("//net/foo/bar"); - TEST_CHECK(ret.is_absolute()); - TEST_CHECK(ret == path("//net/foo/bar")); - } - // !has_root_name() && !has_root_directory() - { - const path p("bar/baz"); - const path base("//net/foo"); - TEST_REQUIRE(not p.has_root_name()); - TEST_REQUIRE(not p.has_root_directory()); - TEST_REQUIRE(base.has_root_name()); - TEST_REQUIRE(base.has_root_directory()); - - path ret = absolute(p, base); - TEST_CHECK(ret.is_absolute()); - TEST_CHECK(ret == path("//net/foo/bar/baz")); - } -} -TEST_CASE(absolute_path_with_default_base) +TEST_CASE(basic_test) { - const path testCases[] = { - "//net/foo", // has_root_name() && has_root_directory() - "/foo", // !has_root_name() && has_root_directory() - "//net", // has_root_name() && !has_root_directory() - "bar/baz" // !has_root_name() && !has_root_directory() + const fs::path cwd = fs::current_path(); + const struct { + std::string input; + std::string expect; + } TestCases [] = { + {"", cwd / ""}, + {"foo", cwd / "foo"}, + {"foo/", cwd / "foo/"}, + {"/already_absolute", "/already_absolute"} }; - const path base = current_path(); - for (auto& p : testCases) { - const path ret = absolute(p); - const path expect = absolute(p, base); + for (auto& TC : TestCases) { + std::error_code ec = GetTestEC(); + const path ret = absolute(TC.input, ec); + TEST_CHECK(!ec); TEST_CHECK(ret.is_absolute()); - TEST_CHECK(ret == expect); + TEST_CHECK(PathEq(ret, TC.expect)); } } diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp index 0872b7b30dd..c9c9128aa2a 100644 --- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.canonical/canonical.pass.cpp @@ -11,9 +11,8 @@ // <experimental/filesystem> -// path canonical(const path& p, const path& base = current_path()); +// path canonical(const path& p); // path canonical(const path& p, error_code& ec); -// path canonical(const path& p, const path& base, error_code& ec); #include "filesystem_include.hpp" #include <type_traits> @@ -25,6 +24,15 @@ using namespace fs; +struct CWDGuard { + path OldCWD; + CWDGuard() : OldCWD(fs::current_path()) { } + ~CWDGuard() { fs::current_path(OldCWD); } + + CWDGuard(CWDGuard const&) = delete; + CWDGuard& operator=(CWDGuard const&) = delete; +}; + TEST_SUITE(filesystem_canonical_path_test_suite) TEST_CASE(signature_test) @@ -32,15 +40,14 @@ TEST_CASE(signature_test) const path p; ((void)p); std::error_code ec; ((void)ec); ASSERT_NOT_NOEXCEPT(canonical(p)); - ASSERT_NOT_NOEXCEPT(canonical(p, p)); ASSERT_NOT_NOEXCEPT(canonical(p, ec)); - ASSERT_NOT_NOEXCEPT(canonical(p, p, ec)); } // There are 4 cases is the proposal for absolute path. // Each scope tests one of the cases. TEST_CASE(test_canonical) { + CWDGuard guard; // has_root_name() && has_root_directory() const path Root = StaticEnv::Root; const path RootName = Root.filename(); @@ -65,54 +72,51 @@ TEST_CASE(test_canonical) { SymlinkName, StaticEnv::File, StaticEnv::Root} }; for (auto& TC : testCases) { - std::error_code ec; - const path ret = canonical(TC.p, TC.base, ec); + std::error_code ec = GetTestEC(); + fs::current_path(TC.base); + const path ret = canonical(TC.p, ec); TEST_REQUIRE(!ec); - const path ret2 = canonical(TC.p, TC.base); - TEST_CHECK(ret == TC.expect); - TEST_CHECK(ret == ret2); + const path ret2 = canonical(TC.p); + TEST_CHECK(PathEq(ret, TC.expect)); + TEST_CHECK(PathEq(ret, ret2)); TEST_CHECK(ret.is_absolute()); } } TEST_CASE(test_dne_path) { - std::error_code ec; + std::error_code ec = GetTestEC(); { const path ret = canonical(StaticEnv::DNE, ec); - TEST_REQUIRE(ec); - TEST_CHECK(ret == path{}); - } - ec.clear(); - { - const path ret = canonical(StaticEnv::DNE, StaticEnv::Root, ec); + TEST_CHECK(ec != GetTestEC()); TEST_REQUIRE(ec); TEST_CHECK(ret == path{}); } { TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE)); - TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE, StaticEnv::Root)); } } TEST_CASE(test_exception_contains_paths) { #ifndef TEST_HAS_NO_EXCEPTIONS + CWDGuard guard; const path p = "blabla/dne"; - const path base = StaticEnv::Root; try { - canonical(p, base); + canonical(p); TEST_REQUIRE(false); } catch (filesystem_error const& err) { TEST_CHECK(err.path1() == p); - TEST_CHECK(err.path2() == base); + // libc++ provides the current path as the second path in the exception + LIBCPP_ONLY(TEST_CHECK(err.path2() == current_path())); } + fs::current_path(StaticEnv::Dir); try { canonical(p); TEST_REQUIRE(false); } catch (filesystem_error const& err) { TEST_CHECK(err.path1() == p); - TEST_CHECK(err.path2() == current_path()); + LIBCPP_ONLY(TEST_CHECK(err.path2() == StaticEnv::Dir)); } #endif } diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp index b842b4a1896..ce763a6c391 100644 --- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp @@ -17,7 +17,6 @@ // void last_write_time(const path& p, file_time_type new_type, // std::error_code& ec) noexcept; - #include "filesystem_include.hpp" #include <type_traits> #include <chrono> @@ -33,7 +32,6 @@ using namespace fs; - std::pair<std::time_t, std::time_t> GetTimes(path const& p) { using Clock = file_time_type::clock; struct ::stat st; diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/proximate.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/proximate.pass.cpp new file mode 100644 index 00000000000..7b526507f0a --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.proximate/proximate.pass.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// 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> + +// path proximate(const path& p, error_code &ec) +// path proximate(const path& p, const path& base = current_path()) +// path proximate(const path& p, const path& base, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + + +static int count_path_elems(const fs::path& p) { + int count = 0; + for (auto& elem : p) { + if (elem != "/" && elem != "") + ++count; + } + return count; +} + +TEST_SUITE(filesystem_proximate_path_test_suite) + + +TEST_CASE(signature_test) +{ + using fs::path; + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(proximate(p)); + ASSERT_NOT_NOEXCEPT(proximate(p, p)); + ASSERT_NOT_NOEXCEPT(proximate(p, ec)); + ASSERT_NOT_NOEXCEPT(proximate(p, p, ec)); +} + +TEST_CASE(basic_test) { + using fs::path; + const path cwd = fs::current_path(); + const path parent_cwd = cwd.parent_path(); + const path curdir = cwd.filename(); + TEST_REQUIRE(!cwd.native().empty()); + int cwd_depth = count_path_elems(cwd); + path dot_dot_to_root; + for (int i=0; i < cwd_depth; ++i) + dot_dot_to_root /= ".."; + path relative_cwd = cwd.native().substr(1); + // clang-format off + struct { + std::string input; + std::string base; + std::string expect; + } TestCases[] = { + {"", "", "."}, + {cwd, "a", ".."}, + {parent_cwd, "a", "../.."}, + {"a", cwd, "a"}, + {"a", parent_cwd, "fs.op.proximate/a"}, + {"/", "a", dot_dot_to_root / ".."}, + {"/", "a/b", dot_dot_to_root / "../.."}, + {"/", "a/b/", dot_dot_to_root / "../../.."}, + {"a", "/", relative_cwd / "a"}, + {"a/b", "/", relative_cwd / "a/b"}, + {"a", "/net", ".." / relative_cwd / "a"}, + {"//net/", "//net", "/net/"}, + {"//net", "//net/", ".."}, + {"//net", "//net", "."}, + {"//net/", "//net/", "."}, + {"//base", "a", dot_dot_to_root / "../base"}, + {"a", "a", "."}, + {"a/b", "a/b", "."}, + {"a/b/c/", "a/b/c/", "."}, + {"//net/a/b", "//net/a/b", "."}, + {"/a/d", "/a/b/c", "../../d"}, + {"/a/b/c", "/a/d", "../b/c"}, + {"a/b/c", "a", "b/c"}, + {"a/b/c", "a/b/c/x/y", "../.."}, + {"a/b/c", "a/b/c", "."}, + {"a/b", "c/d", "../../a/b"} + }; + // clang-format on + int ID = 0; + for (auto& TC : TestCases) { + ++ID; + std::error_code ec = GetTestEC(); + fs::path p(TC.input); + const fs::path output = fs::proximate(p, TC.base, ec); + TEST_CHECK(!ec); + TEST_CHECK(PathEq(output, TC.expect)); + if (!PathEq(output, TC.expect)) { + const path canon_input = fs::weakly_canonical(TC.input); + const path canon_base = fs::weakly_canonical(TC.base); + const path lexically_p = canon_input.lexically_proximate(canon_base); + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Base: '" << TC.base << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + std::cerr << " Output: '" << output.native() << "'\n"; + std::cerr << " Lex Prox: '" << lexically_p.native() << "'\n"; + std::cerr << " Canon Input: " << canon_input << "\n"; + std::cerr << " Canon Base: " << canon_base << "\n"; + + std::cerr << std::endl; + } + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/relative.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/relative.pass.cpp new file mode 100644 index 00000000000..62bc32a732b --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.relative/relative.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// 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> + +// path proximate(const path& p, error_code &ec) +// path proximate(const path& p, const path& base = current_path()) +// path proximate(const path& p, const path& base, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + + +TEST_SUITE(filesystem_proximate_path_test_suite) + +TEST_CASE(test_signature) { + +} +int main() { + // clang-format off + struct { + std::string input; + std::string expect; + } TestCases[] = { + {"", fs::current_path()}, + {".", fs::current_path()}, + {StaticEnv::File, StaticEnv::File}, + {StaticEnv::Dir, StaticEnv::Dir}, + {StaticEnv::SymlinkToDir, StaticEnv::Dir}, + {StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"}, + // FIXME? If the trailing separator occurs in a part of the path that exists, + // it is ommitted. Otherwise it is added to the end of the result. + {StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"}, + {StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"}, + {StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2}, + {StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""}, + {StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"}, + {StaticEnv::Dir / "../dir1", StaticEnv::Dir}, + {StaticEnv::Dir / "./.", StaticEnv::Dir}, + {StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"} + }; + // clang-format on + int ID = 0; + bool Failed = false; + for (auto& TC : TestCases) { + ++ID; + fs::path p(TC.input); + const fs::path output = fs::weakly_canonical(p); + if (output != TC.expect) { + Failed = true; + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + std::cerr << " Output: '" << output.native() << "'"; + std::cerr << std::endl; + } + } + return Failed; +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.system_complete/system_complete.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.system_complete/system_complete.pass.cpp deleted file mode 100644 index b4fb1f19af9..00000000000 --- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.system_complete/system_complete.pass.cpp +++ /dev/null @@ -1,58 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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> - -// path system_complete(const path& p); -// path system_complete(const path& p, error_code& ec); - -// Note: For POSIX based operating systems, 'system_complete(p)' has the -// same semantics as 'absolute(p, current_path())'. - -#include "filesystem_include.hpp" -#include <type_traits> -#include <cassert> - -#include "test_macros.h" -#include "rapid-cxx-test.hpp" -#include "filesystem_test_helper.hpp" - -using namespace fs; - -TEST_SUITE(filesystem_system_complete_test_suite) - -TEST_CASE(signature_test) -{ - const path p; ((void)p); - std::error_code ec; ((void)ec); - ASSERT_NOT_NOEXCEPT(system_complete(p)); - ASSERT_NOT_NOEXCEPT(system_complete(p, ec)); -} - - -TEST_CASE(basic_system_complete_tests) -{ - const path testCases[] = { - "//net/foo", // has_root_name() && has_root_directory() - "/foo", // !has_root_name() && has_root_directory() - "//net", // has_root_name() && !has_root_directory() - "bar/baz" // !has_root_name() && !has_root_directory() - }; - const path base = current_path(); - for (auto& p : testCases) { - const path ret = system_complete(p); - const path expect = absolute(p, base); - TEST_CHECK(ret.is_absolute()); - TEST_CHECK(ret == expect); - } -} - -TEST_SUITE_END() diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp new file mode 100644 index 00000000000..b4cb81d5bce --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// 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> + +// path weakly_canonical(const path& p); +// path weakly_canonical(const path& p, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +int main() { + // clang-format off + struct { + std::string input; + std::string expect; + } TestCases[] = { + {"", fs::current_path()}, + {".", fs::current_path()}, + {"/", "/"}, + {"/foo", "/foo"}, + {"/.", "/"}, + {"/./", "/"}, + {"a/b", fs::current_path() / "a/b"}, + {"a", fs::current_path() / "a"}, + {"a/b/", fs::current_path() / "a/b/"}, + {StaticEnv::File, StaticEnv::File}, + {StaticEnv::Dir, StaticEnv::Dir}, + {StaticEnv::SymlinkToDir, StaticEnv::Dir}, + {StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"}, + // FIXME? If the trailing separator occurs in a part of the path that exists, + // it is ommitted. Otherwise it is added to the end of the result. + {StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"}, + {StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"}, + {StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2}, + {StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""}, + {StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"}, + {StaticEnv::Dir / "../dir1", StaticEnv::Dir}, + {StaticEnv::Dir / "./.", StaticEnv::Dir}, + {StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"} + }; + // clang-format on + int ID = 0; + bool Failed = false; + for (auto& TC : TestCases) { + ++ID; + fs::path p(TC.input); + const fs::path output = fs::weakly_canonical(p); + if (!PathEq(output, TC.expect)) { + Failed = true; + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + std::cerr << " Output: '" << output.native() << "'"; + std::cerr << std::endl; + } + } + return Failed; +} diff --git a/libcxx/test/support/filesystem_test_helper.hpp b/libcxx/test/support/filesystem_test_helper.hpp index 622a60d1c96..5a8a32a406c 100644 --- a/libcxx/test/support/filesystem_test_helper.hpp +++ b/libcxx/test/support/filesystem_test_helper.hpp @@ -9,7 +9,6 @@ #include <random> #include <chrono> - // static test helpers #ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT @@ -400,4 +399,8 @@ void SleepFor(std::chrono::seconds dur) { ; } +inline bool PathEq(fs::path const& LHS, fs::path const& RHS) { + return LHS.native() == RHS.native(); +} + #endif /* FILESYSTEM_TEST_HELPER_HPP */ diff --git a/libcxx/test/support/verbose_assert.h b/libcxx/test/support/verbose_assert.h new file mode 100644 index 00000000000..353e71cfc67 --- /dev/null +++ b/libcxx/test/support/verbose_assert.h @@ -0,0 +1,222 @@ +#ifndef TEST_SUPPORT_VERBOSE_ASSERT +#define TEST_SUPPORT_VERBOSE_ASSERT + +#include <iostream> +#include <cstdio> +#include <sstream> +#include <string> +#include "test_macros.h" + +namespace verbose_assert { + +typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&); + +template <class Stream, class Tp, + class = decltype(std::declval<Stream&>() << std::declval<Tp const&>())> +std::true_type IsStreamableImp(int); +template <class Stream, class Tp> std::false_type IsStreamableImp(long); + +template <class Stream, class Tp> +struct IsStreamable : decltype(IsStreamableImp<Stream, Tp>(0)) {}; + +template <class Tp, int ST = (IsStreamable<decltype(std::cerr), Tp>::value ? 1 + : (IsStreamable<decltype(std::wcerr), Tp>::value ? 2 : -1))> +struct SelectStream { + static_assert(ST == -1, "specialization required for ST != -1"); + static void Print(Tp const&) { std::clog << "Value Not Streamable!\n"; } +}; + +template <class Tp> +struct SelectStream<Tp, 1> { + static void Print(Tp const& val) { std::cerr << val; } +}; + +template <class Tp> +struct SelectStream<Tp, 2> { + static void Print(Tp const& val) { std::wcerr << val; } +}; + +struct AssertData { + AssertData(const char* xcheck, const char* xfile, const char* xfunc, + unsigned long xline, bool xpassed = true) + : passed(xpassed), check(xcheck), file(xfile), func(xfunc), line(xline), + msg() {} + + AssertData& SetFailed(std::string xmsg = std::string()) { + msg = xmsg; + passed = false; + return *this; + } + + void PrintFailed() const { + std::fprintf(stderr, "%s:%lu %s: Assertion '%s' failed.\n", file, line, + func, check); + if (!msg.empty()) + std::fprintf(stderr, "%s\n", msg.data()); + } + + bool passed; + const char* check; + const char* file; + const char* func; + unsigned long line; + std::string msg; +}; + +// AssertHandler is the class constructed by failing CHECK macros. AssertHandler +// will log information about the failures and abort when it is destructed. +class AssertHandler { +public: + AssertHandler(AssertData const& Data) + : passed(Data.passed) { + if (!passed) + Data.PrintFailed(); + } + + ~AssertHandler() TEST_NOEXCEPT_FALSE { + if (!passed) { + error_log << std::endl; + std::abort(); + } + } + + class LogType { + friend class AssertHandler; + + template <class Tp> + friend LogType& operator<<(LogType& log, Tp const& value) { + if (!log.is_disabled) { + SelectStream<Tp>::Print(value); + } + return log; + } + + friend LogType& operator<<(LogType& log, EndLType* m) { + if (!log.is_disabled) { + SelectStream<EndLType*>::Print(m); + } + return log; + } + + private: + LogType(bool disable) : is_disabled(disable) {} + bool is_disabled; + + LogType(LogType const&); + LogType& operator=(LogType const&); + }; + + LogType& GetLog() { + if (passed) + return null_log; + return error_log; + } + +private: + static LogType null_log; + static LogType error_log; + + AssertHandler& operator=(const AssertHandler&) = delete; + AssertHandler(const AssertHandler&) = delete; + AssertHandler() = delete; + +private: + bool passed; +}; + +AssertHandler::LogType AssertHandler::null_log(true); +AssertHandler::LogType AssertHandler::error_log(false); + +template <class It1> +std::string PrintRange(const char* Name, It1 F, It1 E) { + std::stringstream ss; + ss << " " << Name << " = ["; + while (F != E) { + ss << *F; + ++F; + if (F != E) + ss << ", "; + } + ss << "]\n"; + return ss.str(); +} + +template <class Tp, class Up> +std::string PrintMismatch(Tp const& LHS, Up const& RHS, int Elem) { + std::stringstream ss; + ss << " Element " << Elem << " mismatched: `" << LHS << "` != `" << RHS + << "`!\n"; + return ss.str(); +}; + +struct EqualToComp { + template <class Tp, class Up> + bool operator()(Tp const& LHS, Up const& RHS) const { + return LHS == RHS; + } +}; + +template <class It1, class It2, class Comp> +AssertData CheckCollectionsEqual(It1 F1, It1 E1, It2 F2, It2 E2, + AssertData Data, Comp C = EqualToComp()) { + const It1 F1Orig = F1; + const It2 F2Orig = F2; + bool Failed = false; + std::string ErrorMsg; + int Idx = 0; + while (F1 != E1 && F2 != E2) { + if (!(C(*F1, *F2))) { + ErrorMsg += PrintMismatch(*F1, *F2, Idx); + Failed = true; + break; + } + ++Idx; + ++F1; + ++F2; + } + if (!Failed && (F1 != E1 || F2 != E2)) { + ErrorMsg += " Ranges have different sizes!\n"; + Failed = true; + } + if (Failed) { + ErrorMsg += PrintRange("LHS", F1Orig, E1); + ErrorMsg += PrintRange("RHS", F2Orig, E2); + Data.SetFailed(ErrorMsg); + } + return Data; +} +} // namespace verbose_assert + +#ifdef __GNUC__ +#define ASSERT_FN_NAME() __PRETTY_FUNCTION__ +#else +#define ASSERT_FN_NAME() __func__ +#endif + +#define DISPLAY(...) " " #__VA_ARGS__ " = " << (__VA_ARGS__) << "\n" + +#define ASSERT(...) \ + ::verbose_assert::AssertHandler(::verbose_assert::AssertData( \ + #__VA_ARGS__, __FILE__, ASSERT_FN_NAME(), __LINE__,(__VA_ARGS__))).GetLog() + +#define ASSERT_EQ(LHS, RHS) \ + ASSERT(LHS == RHS) << DISPLAY(LHS) << DISPLAY(RHS) +#define ASSERT_NEQ(LHS, RHS) \ + ASSERT(LHS != RHS) << DISPLAY(LHS) << DISPLAY(RHS) +#define ASSERT_PRED(PRED, LHS, RHS) \ + ASSERT(PRED(LHS, RHS)) << DISPLAY(LHS) << DISPLAY(RHS) + +#define ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, Comp) \ + (::verbose_assert::AssertHandler( \ + ::verbose_assert::CheckCollectionsEqual( \ + F1, E1, F2, E2, \ + ::verbose_assert::AssertData("CheckCollectionsEqual(" #F1 ", " #E1 \ + ", " #F2 ", " #E2 ")", \ + __FILE__, ASSERT_FN_NAME(), __LINE__), \ + Comp)) \ + .GetLog()) + +#define ASSERT_COLLECTION_EQ(F1, E1, F2, E2) \ + ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, ::verbose_assert::EqualToComp()) + +#endif diff --git a/libcxx/www/cxx1z_status.html b/libcxx/www/cxx1z_status.html index 29f99c059c4..93d6b046a67 100644 --- a/libcxx/www/cxx1z_status.html +++ b/libcxx/www/cxx1z_status.html @@ -111,7 +111,7 @@ <tr><td><a href="https://wg21.link/p0180r2">p0180r2</a></td><td>LWG</td><td>Reserve a New Library Namespace for Future Standardization</td><td>Oulu</td><td><i>Nothing to do</i></td><td>n/a</td></tr> <tr><td><a href="https://wg21.link/p0181r1">p0181r1</a></td><td>LWG</td><td>Ordered by Default</td><td>Oulu</td><td><i>Removed in Kona</i></td><td>n/a</td></tr> <tr><td><a href="https://wg21.link/p0209r2">p0209r2</a></td><td>LWG</td><td>make_from_tuple: apply for construction</td><td>Oulu</td><td>Complete</td><td>3.9</td></tr> - <tr><td><a href="https://wg21.link/p0219r1">p0219r1</a></td><td>LWG</td><td>Relative Paths for Filesystem</td><td>Oulu</td><td></td><td></td></tr> + <tr><td><a href="https://wg21.link/p0219r1">p0219r1</a></td><td>LWG</td><td>Relative Paths for Filesystem</td><td>Oulu</td><td>Complete</td><td>7.0</td></tr> <tr><td><a href="https://wg21.link/p0254r2">p0254r2</a></td><td>LWG</td><td>Integrating std::string_view and std::string</td><td>Oulu</td><td>Complete</td><td>4.0</td></tr> <tr><td><a href="https://wg21.link/p0258r2">p0258r2</a></td><td>LWG</td><td>has_unique_object_representations</td><td>Oulu</td><td>Complete</td><td>6.0</td></tr> <tr><td><a href="https://wg21.link/p0295r0">p0295r0</a></td><td>LWG</td><td>Adopt Selected Library Fundamentals V2 Components for C++17</td><td>Oulu</td><td>Complete</td><td>4.0</td></tr> @@ -153,7 +153,7 @@ <tr><td><a href="https://wg21.link/P0433R2">P0433R2</a></td><td>LWG</td><td>Toward a resolution of US7 and US14: Integrating template deduction for class templates into the standard library</td><td>Kona</td><td><i>In progress</i></td><td>7.0</td></tr> <tr><td><a href="https://wg21.link/P0452R1">P0452R1</a></td><td>LWG</td><td>Unifying <numeric> Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr> <tr><td><a href="https://wg21.link/P0467R2">P0467R2</a></td><td>LWG</td><td>Iterator Concerns for Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr> - <tr><td><a href="https://wg21.link/P0492R2">P0492R2</a></td><td>LWG</td><td>Proposed Resolution of C++17 National Body Comments for Filesystems</td><td>Kona</td><td></td><td></td></tr> + <tr><td><a href="https://wg21.link/P0492R2">P0492R2</a></td><td>LWG</td><td>Proposed Resolution of C++17 National Body Comments for Filesystems</td><td>Kona</td><td>Complete</td><td>7.0</td></tr> <tr><td><a href="https://wg21.link/P0518R1">P0518R1</a></td><td>LWG</td><td>Allowing copies as arguments to function objects given to parallel algorithms in response to CH11</td><td>Kona</td><td></td><td></td></tr> <tr><td><a href="https://wg21.link/P0523R1">P0523R1</a></td><td>LWG</td><td>Wording for CH 10: Complexity of parallel algorithms</td><td>Kona</td><td></td><td></td></tr> <tr><td><a href="https://wg21.link/P0548R1">P0548R1</a></td><td>LWG</td><td>common_type and duration</td><td>Kona</td><td>Complete</td><td>5.0</td></tr> diff --git a/libcxx/www/cxx2a_status.html b/libcxx/www/cxx2a_status.html index 4ecfc795d7b..a29a98fbfe1 100644 --- a/libcxx/www/cxx2a_status.html +++ b/libcxx/www/cxx2a_status.html @@ -164,7 +164,7 @@ <tr><td><a href="https://wg21.link/LWG3015">3015</a></td><td><tt>copy_options::<i>unspecified</i></tt> underspecified</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr> <tr><td><a href="https://wg21.link/LWG3017">3017</a></td><td><tt>list splice</tt> functions should use <tt>addressof</tt></td><td>Jacksonville</td><td></td></tr> <tr><td><a href="https://wg21.link/LWG3020">3020</a></td><td>[networking.ts] Remove spurious nested <tt>value_type</tt> buffer sequence requirement</td><td>Jacksonville</td><td></td></tr> - <tr><td><a href="https://wg21.link/LWG3026">3026</a></td><td><tt>filesystem::weakly_canonical</tt> still defined in terms of <tt>canonical(p, base)</tt></td><td>Jacksonville</td><td></td></tr> + <tr><td><a href="https://wg21.link/LWG3026">3026</a></td><td><tt>filesystem::weakly_canonical</tt> still defined in terms of <tt>canonical(p, base)</tt></td><td>Jacksonville</td><td>Complete</td></tr> <tr><td><a href="https://wg21.link/LWG3030">3030</a></td><td>Who shall meet the requirements of <tt>try_lock</tt>?</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr> <tr><td><a href="https://wg21.link/LWG3034">3034</a></td><td>P0767R1 breaks previously-standard-layout types</td><td>Jacksonville</td><td>Complete</td></tr> <tr><td><a href="https://wg21.link/LWG3035">3035</a></td><td><tt>std::allocator</tt>'s constructors should be <tt>constexpr</tt></td><td>Jacksonville</td><td>Complete</td></tr> |