[libc++] Implement Directory Entry Caching -- Sort of.

Summary:
This patch implements directory_entry caching *almost* as specified in P0317r1. However, I explicitly chose to deviate from the standard as I'll explain below.

The approach I decided to take is a fully caching one. When `refresh()` is called, the cache is populated by calls to `stat` and `lstat` as needed.
During directory iteration the cache is only populated with the `file_type` as reported by `readdir`.
The cache can be in the following states:

* `_Empty`: There is nothing in the cache (likely due to an error)
* `_IterSymlink`: Created by directory iteration when we walk onto a symlink only the symlink file type is known.
* `_IterNonSymlink`: Created by directory iteration when we walk onto a non-symlink. Both the regular file type and symlink file type are known.
* `_RefreshSymlink` and `_RefreshNonSymlink`: A full cache created by `refresh()`.  This case includes dead symlinks.
* `_RefreshSymlinkUnresolved`: A partial cache created by refresh when we fail to resolve the file pointed to by a symlink (likely due to permissions). Symlink attributes are cached, but attributes about the linked entity are not.

As mentioned, this implementation purposefully deviates from the standard. According to some readings of the specification, and the Windows filesystem implementation, the constructors and modifiers which don't pass an `error_code` must throw when the `directory_entry` points to a entity which doesn't exist. or when attribute resolution fails for another reason. 

@BillyONeal  has proposed a more reasonable set of requirements, where modifiers other than refresh ignore errors. This is the behavior libc++ currently implements, with the expectation some form of the new language will be accepted into the standard.

Some additional semantics which differ from the Windows implementation:

1. `refresh` will not throw when the entry doesn't exist. In this case we can still meet the functions specification, so we don't treat it as an error.
2. We don't clear the path name when a constructor fails via refresh (this will hopefully be changed in the standard as well).

It should be noted that libstdc++'s current implementation has the same behavior as libc++, except for point (2).

If the changes to the specification don't get accepted, we'll be able to make the changes later.

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0317r1.html

Reviewers: mclow.lists, gromer, ldionne, aaron.ballman

Subscribers: BillyONeal, christof, cfe-commits

Differential Revision: https://reviews.llvm.org/D49530

llvm-svn: 337516
This commit is contained in:
Eric Fiselier 2018-07-20 01:22:32 +00:00
parent 40fa4a1a55
commit c16998649e
27 changed files with 3107 additions and 460 deletions

View File

@ -1903,6 +1903,10 @@ path weakly_canonical(path const& __p, error_code& __ec) {
}
class directory_iterator;
class recursive_directory_iterator;
class __dir_stream;
class directory_entry
{
typedef _VSTD_FS::path _Path;
@ -1914,7 +1918,15 @@ public:
directory_entry(directory_entry&&) _NOEXCEPT = default;
_LIBCPP_INLINE_VISIBILITY
explicit directory_entry(_Path const& __p) : __p_(__p) {}
explicit directory_entry(_Path const& __p) : __p_(__p) {
error_code __ec;
__refresh(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
directory_entry(_Path const& __p, error_code &__ec) : __p_(__p) {
__refresh(&__ec);
}
~directory_entry() {}
@ -1924,13 +1936,37 @@ public:
_LIBCPP_INLINE_VISIBILITY
void assign(_Path const& __p) {
__p_ = __p;
error_code __ec;
__refresh(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
void assign(_Path const& __p, error_code& __ec) {
__p_ = __p;
__refresh(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
void replace_filename(_Path const& __p) {
__p_ = __p_.parent_path() / __p;
__p_.replace_filename(__p);
error_code __ec;
__refresh(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
void replace_filename(_Path const& __p, error_code &__ec) {
__p_ = __p_.parent_path() / __p;
__refresh(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
void refresh() {
__refresh();
}
_LIBCPP_INLINE_VISIBILITY
void refresh(error_code& __ec) _NOEXCEPT { __refresh(&__ec); }
_LIBCPP_INLINE_VISIBILITY
_Path const& path() const _NOEXCEPT {
return __p_;
@ -1941,24 +1977,143 @@ public:
return __p_;
}
_LIBCPP_INLINE_VISIBILITY
bool exists() const {
return _VSTD_FS::exists(file_status{__get_ft()});
}
_LIBCPP_INLINE_VISIBILITY
bool exists(error_code& __ec) const noexcept {
return _VSTD_FS::exists(file_status{__get_ft(&__ec)});
}
_LIBCPP_INLINE_VISIBILITY
bool is_block_file() const {
return __get_ft() == file_type::block;
}
_LIBCPP_INLINE_VISIBILITY
bool is_block_file(error_code& __ec) const noexcept {
return __get_ft(&__ec) == file_type::block;
}
_LIBCPP_INLINE_VISIBILITY
bool is_character_file() const {
return __get_ft() == file_type::character;
}
_LIBCPP_INLINE_VISIBILITY
bool is_character_file(error_code& __ec) const noexcept {
return __get_ft(&__ec) == file_type::character;
}
_LIBCPP_INLINE_VISIBILITY
bool is_directory() const {
return __get_ft() == file_type::directory;
}
_LIBCPP_INLINE_VISIBILITY
bool is_directory(error_code& __ec) const noexcept {
return __get_ft(&__ec) == file_type::directory;
}
_LIBCPP_INLINE_VISIBILITY
bool is_fifo() const {
return __get_ft() == file_type::fifo;
}
_LIBCPP_INLINE_VISIBILITY
bool is_fifo(error_code& __ec) const noexcept {
return __get_ft(&__ec) == file_type::fifo;
}
_LIBCPP_INLINE_VISIBILITY
bool is_other() const {
return _VSTD_FS::is_other(file_status{__get_ft()});
}
_LIBCPP_INLINE_VISIBILITY
bool is_other(error_code& __ec) const noexcept {
return _VSTD_FS::is_other(file_status{__get_ft(&__ec)});
}
_LIBCPP_INLINE_VISIBILITY
bool is_regular_file() const {
return __get_ft() == file_type::regular;
}
_LIBCPP_INLINE_VISIBILITY
bool is_regular_file(error_code& __ec) const noexcept {
return __get_ft(&__ec) == file_type::regular;
}
_LIBCPP_INLINE_VISIBILITY
bool is_socket() const {
return __get_ft() == file_type::socket;
}
_LIBCPP_INLINE_VISIBILITY
bool is_socket(error_code& __ec) const noexcept {
return __get_ft(&__ec) == file_type::socket;
}
_LIBCPP_INLINE_VISIBILITY
bool is_symlink() const {
return __get_sym_ft() == file_type::symlink;
}
_LIBCPP_INLINE_VISIBILITY
bool is_symlink(error_code& __ec) const noexcept {
return __get_sym_ft(&__ec) == file_type::symlink;
}
_LIBCPP_INLINE_VISIBILITY
uintmax_t file_size() const {
return __get_size();
}
_LIBCPP_INLINE_VISIBILITY
uintmax_t file_size(error_code& __ec) const noexcept {
return __get_size(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
uintmax_t hard_link_count() const {
return __get_nlink();
}
_LIBCPP_INLINE_VISIBILITY
uintmax_t hard_link_count(error_code& __ec) const noexcept {
return __get_nlink(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
file_time_type last_write_time() const {
return __get_write_time();
}
_LIBCPP_INLINE_VISIBILITY
file_time_type last_write_time(error_code& __ec) const noexcept {
return __get_write_time(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
file_status status() const {
return _VSTD_FS::status(__p_);
return __get_status();
}
_LIBCPP_INLINE_VISIBILITY
file_status status(error_code& __ec) const _NOEXCEPT {
return _VSTD_FS::status(__p_, __ec);
return __get_status(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
file_status symlink_status() const {
return _VSTD_FS::symlink_status(__p_);
return __get_symlink_status();
}
_LIBCPP_INLINE_VISIBILITY
file_status symlink_status(error_code& __ec) const _NOEXCEPT {
return _VSTD_FS::symlink_status(__p_, __ec);
return __get_symlink_status(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
@ -1990,15 +2145,231 @@ public:
bool operator>=(directory_entry const& __rhs) const _NOEXCEPT {
return __p_ >= __rhs.__p_;
}
private:
friend class directory_iterator;
friend class recursive_directory_iterator;
friend class __dir_stream;
enum _CacheType : unsigned char {
_Empty,
_IterSymlink,
_IterNonSymlink,
_RefreshSymlink,
_RefreshSymlinkUnresolved,
_RefreshNonSymlink
};
struct __cached_data {
uintmax_t __size_;
uintmax_t __nlink_;
file_time_type __write_time_;
perms __sym_perms_;
perms __non_sym_perms_;
file_type __type_;
_CacheType __cache_type_;
_LIBCPP_INLINE_VISIBILITY
__cached_data() noexcept { __reset(); }
_LIBCPP_INLINE_VISIBILITY
void __reset() {
__cache_type_ = _Empty;
__type_ = file_type::none;
__sym_perms_ = __non_sym_perms_ = perms::unknown;
__size_ = __nlink_ = uintmax_t(-1);
__write_time_ = file_time_type::min();
}
};
_LIBCPP_INLINE_VISIBILITY
static __cached_data __create_iter_result(file_type __ft) {
__cached_data __data;
__data.__type_ = __ft;
__data.__cache_type_ =
__ft == file_type::symlink ? _IterSymlink : _IterNonSymlink;
return __data;
}
_LIBCPP_INLINE_VISIBILITY
void __assign_iter_entry(_Path&& __p, __cached_data __dt) {
__p_ = std::move(__p);
__data_ = __dt;
}
_LIBCPP_FUNC_VIS
error_code __do_refresh() noexcept;
_LIBCPP_INLINE_VISIBILITY
static bool __is_dne_error(error_code const& __ec) {
if (!__ec)
return true;
switch (static_cast<errc>(__ec.value())) {
case errc::no_such_file_or_directory:
case errc::not_a_directory:
return true;
default:
return false;
}
}
_LIBCPP_INLINE_VISIBILITY
void __handle_error(const char* __msg, error_code* __dest_ec,
error_code const& __ec,
bool __allow_dne = false) const {
if (__dest_ec) {
*__dest_ec = __ec;
return;
}
if (__ec && (!__allow_dne || !__is_dne_error(__ec)))
__throw_filesystem_error(__msg, __p_, _Path{}, __ec);
}
_LIBCPP_INLINE_VISIBILITY
void __refresh(error_code* __ec = nullptr) {
__handle_error("refresh", __ec, __do_refresh(), /*allow_dne*/ true);
}
_LIBCPP_INLINE_VISIBILITY
file_type __get_sym_ft(error_code *__ec = nullptr) const {
switch (__data_.__cache_type_) {
case _Empty:
return __symlink_status(__p_, __ec).type();
case _IterSymlink:
case _RefreshSymlink:
case _RefreshSymlinkUnresolved:
if (__ec)
__ec->clear();
return file_type::symlink;
case _IterNonSymlink:
case _RefreshNonSymlink:
file_status __st(__data_.__type_);
if (__ec && !_VSTD_FS::exists(__st))
*__ec = make_error_code(errc::no_such_file_or_directory);
else if (__ec)
__ec->clear();
return __data_.__type_;
}
}
_LIBCPP_INLINE_VISIBILITY
file_type __get_ft(error_code *__ec = nullptr) const {
switch (__data_.__cache_type_) {
case _Empty:
case _IterSymlink:
case _RefreshSymlinkUnresolved:
return __status(__p_, __ec).type();
case _IterNonSymlink:
case _RefreshNonSymlink:
case _RefreshSymlink: {
file_status __st(__data_.__type_);
if (__ec && !_VSTD_FS::exists(__st))
*__ec = make_error_code(errc::no_such_file_or_directory);
else if (__ec)
__ec->clear();
return __data_.__type_;
}
}
}
_LIBCPP_INLINE_VISIBILITY
file_status __get_status(error_code *__ec = nullptr) const {
switch (__data_.__cache_type_) {
case _Empty:
case _IterNonSymlink:
case _IterSymlink:
case _RefreshSymlinkUnresolved:
return __status(__p_, __ec);
case _RefreshNonSymlink:
case _RefreshSymlink:
return file_status(__get_ft(__ec), __data_.__non_sym_perms_);
}
}
_LIBCPP_INLINE_VISIBILITY
file_status __get_symlink_status(error_code *__ec = nullptr) const {
switch (__data_.__cache_type_) {
case _Empty:
case _IterNonSymlink:
case _IterSymlink:
return __symlink_status(__p_, __ec);
case _RefreshNonSymlink:
return file_status(__get_sym_ft(__ec), __data_.__non_sym_perms_);
case _RefreshSymlink:
case _RefreshSymlinkUnresolved:
return file_status(__get_sym_ft(__ec), __data_.__sym_perms_);
}
}
_LIBCPP_INLINE_VISIBILITY
uintmax_t __get_size(error_code *__ec = nullptr) const {
switch (__data_.__cache_type_) {
case _Empty:
case _IterNonSymlink:
case _IterSymlink:
case _RefreshSymlinkUnresolved:
return _VSTD_FS::__file_size(__p_, __ec);
case _RefreshSymlink:
case _RefreshNonSymlink: {
error_code __m_ec;
file_status __st(__get_ft(&__m_ec));
__handle_error("directory_entry::file_size", __ec, __m_ec);
if (_VSTD_FS::exists(__st) && !_VSTD_FS::is_regular_file(__st)) {
errc __err_kind = _VSTD_FS::is_directory(__st) ? errc::is_a_directory
: errc::not_supported;
__handle_error("directory_entry::file_size", __ec,
make_error_code(__err_kind));
}
return __data_.__size_;
}
}
}
_LIBCPP_INLINE_VISIBILITY
uintmax_t __get_nlink(error_code *__ec = nullptr) const {
switch (__data_.__cache_type_) {
case _Empty:
case _IterNonSymlink:
case _IterSymlink:
case _RefreshSymlinkUnresolved:
return _VSTD_FS::__hard_link_count(__p_, __ec);
case _RefreshSymlink:
case _RefreshNonSymlink: {
error_code __m_ec;
(void)__get_ft(&__m_ec);
__handle_error("directory_entry::hard_link_count", __ec, __m_ec);
return __data_.__nlink_;
}
}
}
_LIBCPP_INLINE_VISIBILITY
file_time_type __get_write_time(error_code *__ec = nullptr) const {
switch (__data_.__cache_type_) {
case _Empty:
case _IterNonSymlink:
case _IterSymlink:
case _RefreshSymlinkUnresolved:
return _VSTD_FS::__last_write_time(__p_, __ec);
case _RefreshSymlink:
case _RefreshNonSymlink: {
error_code __m_ec;
file_status __st(__get_ft(&__m_ec));
__handle_error("directory_entry::last_write_time", __ec, __m_ec);
if (_VSTD_FS::exists(__st) &&
__data_.__write_time_ == file_time_type::min())
__handle_error("directory_entry::last_write_time", __ec,
make_error_code(errc::value_too_large));
return __data_.__write_time_;
}
}
}
private:
_Path __p_;
__cached_data __data_;
};
class directory_iterator;
class recursive_directory_iterator;
class __dir_stream;
class __dir_element_proxy {
public:

View File

@ -17,32 +17,43 @@
#endif
#include <errno.h>
#include "filesystem_common.h"
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
namespace { namespace detail {
namespace detail {
namespace {
#if !defined(_LIBCPP_WIN32API)
inline error_code capture_errno() {
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
return error_code{errno, std::generic_category()};
template <class DirEntT, class = decltype(DirEntT::d_type)>
static file_type get_file_type(DirEntT *ent, int) {
switch (ent->d_type) {
case DT_BLK:
return file_type::block;
case DT_CHR:
return file_type::character;
case DT_DIR:
return file_type::directory;
case DT_FIFO:
return file_type::fifo;
case DT_LNK:
return file_type::symlink;
case DT_REG:
return file_type::regular;
case DT_SOCK:
return file_type::socket;
case DT_UNKNOWN:
return file_type::unknown;
}
return file_type::none;
}
#endif
template <class ...Args>
inline bool set_or_throw(std::error_code& my_ec,
std::error_code* user_ec,
const char* msg, Args&&... args)
{
if (user_ec) {
*user_ec = my_ec;
return true;
}
__throw_filesystem_error(msg, std::forward<Args>(args)..., my_ec);
return false;
template <class DirEntT>
static file_type get_file_type(DirEntT *ent, long) {
return file_type::unknown;
}
#if !defined(_LIBCPP_WIN32API)
inline path::string_type posix_readdir(DIR *dir_stream, error_code& ec) {
static pair<string_view, file_type>
posix_readdir(DIR *dir_stream, error_code& ec) {
struct dirent* dir_entry_ptr = nullptr;
errno = 0; // zero errno in order to detect errors
ec.clear();
@ -51,18 +62,38 @@ inline path::string_type posix_readdir(DIR *dir_stream, error_code& ec) {
ec = capture_errno();
return {};
} else {
return dir_entry_ptr->d_name;
return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};
}
}
#else
static file_type get_file_type(const WIN32_FIND_DATA& data) {
//auto attrs = data.dwFileAttributes;
// FIXME(EricWF)
return file_type::unknown;
}
static uintmax_t get_file_size(const WIN32_FIND_DATA& data) {
return (data.nFileSizeHight * (MAXDWORD+1)) + data.nFileSizeLow;
}
static file_time_type get_write_time(const WIN32_FIND_DATA& data) {
ULARGE_INTEGER tmp;
FILETIME& time = data.ftLastWriteTime;
tmp.u.LowPart = time.dwLowDateTime;
tmp.u.HighPart = time.dwHighDateTime;
return file_time_type(file_time_type::duration(time.QuadPart));
}
#endif
}} // namespace detail
} // namespace
} // namespace detail
using detail::set_or_throw;
#if defined(_LIBCPP_WIN32API)
class __dir_stream {
public:
__dir_stream() = delete;
__dir_stream& operator=(const __dir_stream&) = delete;
@ -74,7 +105,7 @@ public:
__dir_stream(const path& root, directory_options opts, error_code& ec)
: __stream_(INVALID_HANDLE_VALUE), __root_(root) {
__stream_ = ::FindFirstFile(root.c_str(), &__data_);
__stream_ = ::FindFirstFileEx(root.c_str(), &__data_);
if (__stream_ == INVALID_HANDLE_VALUE) {
ec = error_code(::GetLastError(), std::generic_category());
const bool ignore_permission_denied =
@ -97,7 +128,14 @@ public:
while (::FindNextFile(__stream_, &__data_)) {
if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, ".."))
continue;
__entry_.assign(__root_ / __data_.cFileName);
// FIXME: Cache more of this
//directory_entry::__cached_data cdata;
//cdata.__type_ = get_file_type(__data_);
//cdata.__size_ = get_file_size(__data_);
//cdata.__write_time_ = get_write_time(__data_);
__entry_.__assign_iter_entry(
__root_ / __data_.cFileName,
directory_entry::__create_iter_result(get_file_type(__data)));
return true;
}
ec = error_code(::GetLastError(), std::generic_category());
@ -157,15 +195,18 @@ public:
bool advance(error_code &ec) {
while (true) {
auto str = detail::posix_readdir(__stream_, ec);
auto str_type_pair = detail::posix_readdir(__stream_, ec);
auto& str = str_type_pair.first;
if (str == "." || str == "..") {
continue;
} else if (ec || str.empty()) {
close();
return false;
} else {
__entry_.assign(__root_ / str);
return true;
__entry_.__assign_iter_entry(
__root_ / str,
directory_entry::__create_iter_result(str_type_pair.second));
return true;
}
}
}

View File

@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===////
#ifndef FILESYSTEM_TIME_HELPER_H
#define FILESYSTEM_TIME_HELPER_H
#ifndef FILESYSTEM_COMMON_H
#define FILESYSTEM_COMMON_H
#include "experimental/__config"
#include "chrono"
@ -17,13 +17,77 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <fcntl.h> /* values for fchmodat */
#include <experimental/filesystem>
#if (__APPLE__)
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
#define _LIBCXX_USE_UTIMENSAT
#endif
#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
#define _LIBCXX_USE_UTIMENSAT
#endif
#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
#define _LIBCXX_USE_UTIMENSAT
#endif
#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
#define _LIBCXX_USE_UTIMENSAT
#endif
#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
#else
// We can use the presence of UTIME_OMIT to detect platforms that provide
// utimensat.
#if defined(UTIME_OMIT)
#define _LIBCXX_USE_UTIMENSAT
#endif
#endif // __APPLE__
#if !defined(_LIBCXX_USE_UTIMENSAT)
#include <sys/time.h> // for ::utimes as used in __last_write_time
#endif
#if !defined(UTIME_OMIT)
#include <sys/time.h> // for ::utimes as used in __last_write_time
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
namespace time_detail { namespace {
namespace detail {
namespace {
std::error_code capture_errno() {
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
return std::error_code(errno, std::generic_category());
}
void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
const char* msg, path const& p = {}, path const& p2 = {}) {
if (ec) {
*ec = m_ec;
} else {
string msg_s("std::experimental::filesystem::");
msg_s += msg;
__throw_filesystem_error(msg_s, p, p2, m_ec);
}
}
void set_or_throw(std::error_code* ec, const char* msg, path const& p = {},
path const& p2 = {}) {
return set_or_throw(capture_errno(), ec, msg, p, p2);
}
namespace time_util {
using namespace chrono;
@ -78,9 +142,8 @@ const long long fs_time_util_base<FileTimeT, true>::min_seconds =
template <class FileTimeT>
const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec =
duration_cast<nanoseconds>((FileTimeT::duration::min() -
seconds(min_seconds)) +
seconds(1))
duration_cast<nanoseconds>(
(FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
.count();
template <class FileTimeT, class TimeT, class TimeSpecT>
@ -145,7 +208,6 @@ public:
template <class SubSecDurT, class SubSecT>
static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out,
FileTimeT tp) {
using namespace chrono;
auto dur = tp.time_since_epoch();
auto sec_dur = duration_cast<seconds>(dur);
auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
@ -163,11 +225,72 @@ public:
}
};
} // end namespace
} // end namespace time_detail
} // namespace time_util
using time_detail::fs_time_util;
using TimeSpec = struct timespec;
using StatT = struct stat;
using FSTime = time_util::fs_time_util<file_time_type, time_t, struct timespec>;
#if defined(__APPLE__)
TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
#else
TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
#endif
#if !defined(_LIBCXX_USE_UTIMENSAT)
using TimeStruct = struct ::timeval;
using TimeStructArray = TimeStruct[2];
#else
using TimeStruct = struct ::timespec;
using TimeStructArray = TimeStruct[2];
#endif
bool SetFileTimes(const path& p, TimeStructArray const& TS,
std::error_code& ec) {
#if !defined(_LIBCXX_USE_UTIMENSAT)
if (::utimes(p.c_str(), TS) == -1)
#else
if (::utimensat(AT_FDCWD, p.c_str(), TS, 0) == -1)
#endif
{
ec = capture_errno();
return true;
}
return false;
}
void SetTimeStructTo(TimeStruct& TS, TimeSpec ToTS) {
using namespace chrono;
TS.tv_sec = ToTS.tv_sec;
#if !defined(_LIBCXX_USE_UTIMENSAT)
TS.tv_usec = duration_cast<microseconds>(nanoseconds(ToTS.tv_nsec)).count();
#else
TS.tv_nsec = ToTS.tv_nsec;
#endif
}
bool SetTimeStructTo(TimeStruct& TS, file_time_type NewTime) {
using namespace chrono;
#if !defined(_LIBCXX_USE_UTIMENSAT)
return !FSTime::set_times_checked<microseconds>(&TS.tv_sec, &TS.tv_usec,
NewTime);
#else
return !FSTime::set_times_checked<nanoseconds>(&TS.tv_sec, &TS.tv_nsec,
NewTime);
#endif
}
} // namespace
} // end namespace detail
_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
#endif // FILESYSTEM_TIME_HELPER_H
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#endif // FILESYSTEM_COMMON_H

View File

@ -17,42 +17,18 @@
#include "cstdlib"
#include "climits"
#include "filesystem_time_helper.h"
#include "filesystem_common.h"
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <fcntl.h> /* values for fchmodat */
#include <experimental/filesystem>
#if (__APPLE__)
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
#define _LIBCXX_USE_UTIMENSAT
#endif
#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
#define _LIBCXX_USE_UTIMENSAT
#endif
#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
#define _LIBCXX_USE_UTIMENSAT
#endif
#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
#define _LIBCXX_USE_UTIMENSAT
#endif
#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
#else
// We can use the presence of UTIME_OMIT to detect platforms that provide
// utimensat.
#if defined(UTIME_OMIT)
#define _LIBCXX_USE_UTIMENSAT
#endif
#endif // __APPLE__
#if !defined(_LIBCXX_USE_UTIMENSAT)
#include <sys/time.h> // for ::utimes as used in __last_write_time
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <cassert>
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
@ -310,96 +286,78 @@ namespace detail { namespace {
using value_type = path::value_type;
using string_type = path::string_type;
inline std::error_code capture_errno() {
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
return std::error_code(errno, std::generic_category());
}
void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
const char* msg, path const& p = {}, path const& p2 = {})
{
if (ec) {
*ec = m_ec;
} else {
string msg_s("std::experimental::filesystem::");
msg_s += msg;
__throw_filesystem_error(msg_s, p, p2, m_ec);
}
}
void set_or_throw(std::error_code* ec, const char* msg,
path const& p = {}, path const& p2 = {})
{
return set_or_throw(capture_errno(), ec, msg, p, p2);
}
perms posix_get_perms(const struct ::stat & st) noexcept {
return static_cast<perms>(st.st_mode) & perms::mask;
perms posix_get_perms(const struct ::stat& st) noexcept {
return static_cast<perms>(st.st_mode) & perms::mask;
}
::mode_t posix_convert_perms(perms prms) {
return static_cast< ::mode_t>(prms & perms::mask);
return static_cast< ::mode_t>(prms & perms::mask);
}
file_status create_file_status(std::error_code& m_ec, path const& p,
struct ::stat& path_stat,
std::error_code* ec)
{
if (ec) *ec = m_ec;
if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
return file_status(file_type::not_found);
}
else if (m_ec) {
set_or_throw(m_ec, ec, "posix_stat", p);
return file_status(file_type::none);
}
// else
struct ::stat& path_stat, std::error_code* ec) {
if (ec)
*ec = m_ec;
// assert(m_ec.value() != ENOTDIR);
if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
return file_status(file_type::not_found);
} else if (m_ec) {
set_or_throw(m_ec, ec, "posix_stat", p);
return file_status(file_type::none);
}
// else
file_status fs_tmp;
auto const mode = path_stat.st_mode;
if (S_ISLNK(mode)) fs_tmp.type(file_type::symlink);
else if (S_ISREG(mode)) fs_tmp.type(file_type::regular);
else if (S_ISDIR(mode)) fs_tmp.type(file_type::directory);
else if (S_ISBLK(mode)) fs_tmp.type(file_type::block);
else if (S_ISCHR(mode)) fs_tmp.type(file_type::character);
else if (S_ISFIFO(mode)) fs_tmp.type(file_type::fifo);
else if (S_ISSOCK(mode)) fs_tmp.type(file_type::socket);
else fs_tmp.type(file_type::unknown);
file_status fs_tmp;
auto const mode = path_stat.st_mode;
if (S_ISLNK(mode))
fs_tmp.type(file_type::symlink);
else if (S_ISREG(mode))
fs_tmp.type(file_type::regular);
else if (S_ISDIR(mode))
fs_tmp.type(file_type::directory);
else if (S_ISBLK(mode))
fs_tmp.type(file_type::block);
else if (S_ISCHR(mode))
fs_tmp.type(file_type::character);
else if (S_ISFIFO(mode))
fs_tmp.type(file_type::fifo);
else if (S_ISSOCK(mode))
fs_tmp.type(file_type::socket);
else
fs_tmp.type(file_type::unknown);
fs_tmp.permissions(detail::posix_get_perms(path_stat));
return fs_tmp;
fs_tmp.permissions(detail::posix_get_perms(path_stat));
return fs_tmp;
}
file_status posix_stat(path const & p, struct ::stat& path_stat,
std::error_code* ec)
{
std::error_code m_ec;
if (::stat(p.c_str(), &path_stat) == -1)
m_ec = detail::capture_errno();
return create_file_status(m_ec, p, path_stat, ec);
file_status posix_stat(path const& p, struct ::stat& path_stat,
std::error_code* ec) {
std::error_code m_ec;
if (::stat(p.c_str(), &path_stat) == -1)
m_ec = detail::capture_errno();
return create_file_status(m_ec, p, path_stat, ec);
}
file_status posix_stat(path const & p, std::error_code* ec) {
struct ::stat path_stat;
return posix_stat(p, path_stat, ec);
file_status posix_stat(path const& p, std::error_code* ec) {
struct ::stat path_stat;
return posix_stat(p, path_stat, ec);
}
file_status posix_lstat(path const & p, struct ::stat & path_stat,
std::error_code* ec)
{
std::error_code m_ec;
if (::lstat(p.c_str(), &path_stat) == -1)
m_ec = detail::capture_errno();
return create_file_status(m_ec, p, path_stat, ec);
file_status posix_lstat(path const& p, struct ::stat& path_stat,
std::error_code* ec) {
std::error_code m_ec;
if (::lstat(p.c_str(), &path_stat) == -1)
m_ec = detail::capture_errno();
return create_file_status(m_ec, p, path_stat, ec);
}
file_status posix_lstat(path const & p, std::error_code* ec) {
struct ::stat path_stat;
return posix_lstat(p, path_stat, ec);
file_status posix_lstat(path const& p, std::error_code* ec) {
struct ::stat path_stat;
return posix_lstat(p, path_stat, ec);
}
bool stat_equivalent(struct ::stat& st1, struct ::stat& st2) {
return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
}
// DETAIL::MISC
@ -622,7 +580,6 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink,
__create_symlink(real_path, new_symlink, ec);
}
bool __create_directories(const path& p, std::error_code *ec)
{
std::error_code m_ec;
@ -755,10 +712,12 @@ std::uintmax_t __file_size(const path& p, std::error_code *ec)
struct ::stat st;
file_status fst = detail::posix_stat(p, st, &m_ec);
if (!exists(fst) || !is_regular_file(fst)) {
if (!m_ec)
m_ec = make_error_code(errc::not_supported);
set_or_throw(m_ec, ec, "file_size", p);
return static_cast<uintmax_t>(-1);
errc error_kind =
is_directory(fst) ? errc::is_a_directory : errc::not_supported;
if (!m_ec)
m_ec = make_error_code(error_kind);
set_or_throw(m_ec, ec, "file_size", p);
return static_cast<uintmax_t>(-1);
}
// is_regular_file(p) == true
if (ec) ec->clear();
@ -806,25 +765,18 @@ bool __fs_is_empty(const path& p, std::error_code *ec)
_LIBCPP_UNREACHABLE();
}
namespace detail { namespace {
using TimeSpec = struct timespec;
using StatT = struct stat;
#if defined(__APPLE__)
TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
__attribute__((unused)) // Suppress warning
TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
#else
TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
__attribute__((unused)) // Suppress warning
TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
#endif
}} // end namespace detail
using FSTime = fs_time_util<file_time_type, time_t, struct timespec>;
static file_time_type __extract_last_write_time(path const& p,
const struct ::stat& st,
error_code *ec) {
using detail::FSTime;
auto ts = detail::extract_mtime(st);
if (!FSTime::is_representable(ts)) {
set_or_throw(make_error_code(errc::value_too_large), ec, "last_write_time",
p);
return file_time_type::min();
}
return FSTime::convert_timespec(ts);
}
file_time_type __last_write_time(const path& p, std::error_code *ec)
{
@ -837,21 +789,17 @@ file_time_type __last_write_time(const path& p, std::error_code *ec)
return file_time_type::min();
}
if (ec) ec->clear();
auto ts = detail::extract_mtime(st);
if (!FSTime::is_representable(ts)) {
set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
"last_write_time", p);
return file_time_type::min();
}
return FSTime::convert_timespec(ts);
return __extract_last_write_time(p, st, ec);
}
void __last_write_time(const path& p, file_time_type new_time,
std::error_code *ec)
{
using namespace std::chrono;
std::error_code m_ec;
using namespace detail;
std::error_code m_ec;
TimeStructArray tbuf;
#if !defined(_LIBCXX_USE_UTIMENSAT)
// This implementation has a race condition between determining the
// last access time and attempting to set it to the same value using
@ -862,37 +810,18 @@ void __last_write_time(const path& p, file_time_type new_time,
set_or_throw(m_ec, ec, "last_write_time", p);
return;
}
auto atime = detail::extract_atime(st);
struct ::timeval tbuf[2];
tbuf[0].tv_sec = atime.tv_sec;
tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
const bool overflowed = !FSTime::set_times_checked<microseconds>(
&tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
if (overflowed) {
set_or_throw(make_error_code(errc::invalid_argument), ec,
"last_write_time", p);
return;
}
if (::utimes(p.c_str(), tbuf) == -1) {
m_ec = detail::capture_errno();
}
SetTimeStructTo(tbuf[0], detail::extract_atime(st));
#else
struct ::timespec tbuf[2];
tbuf[0].tv_sec = 0;
tbuf[0].tv_nsec = UTIME_OMIT;
const bool overflowed = !FSTime::set_times_checked<nanoseconds>(
&tbuf[1].tv_sec, &tbuf[1].tv_nsec, new_time);
if (overflowed) {
set_or_throw(make_error_code(errc::invalid_argument),
ec, "last_write_time", p);
return;
}
if (::utimensat(AT_FDCWD, p.c_str(), tbuf, 0) == -1) {
m_ec = detail::capture_errno();
}
#endif
if (SetTimeStructTo(tbuf[1], new_time)) {
set_or_throw(make_error_code(errc::invalid_argument), ec,
"last_write_time", p);
return;
}
SetFileTimes(p, tbuf, m_ec);
if (m_ec)
set_or_throw(m_ec, ec, "last_write_time", p);
else if (ec)
@ -1116,8 +1045,6 @@ path __weakly_canonical(const path& p, std::error_code *ec) {
return result.lexically_normal();
}
///////////////////////////////////////////////////////////////////////////////
// path definitions
///////////////////////////////////////////////////////////////////////////////
@ -1468,5 +1395,105 @@ path::iterator& path::iterator::__decrement() {
return *this;
}
///////////////////////////////////////////////////////////////////////////////
// directory entry definitions
///////////////////////////////////////////////////////////////////////////////
#ifndef _LIBCPP_WIN32API
error_code directory_entry::__do_refresh() noexcept {
__data_.__reset();
error_code failure_ec;
struct ::stat full_st;
file_status st = detail::posix_lstat(__p_, full_st, &failure_ec);
if (!status_known(st)) {
__data_.__reset();
return failure_ec;
}
if (!_VSTD_FS::exists(st) || !_VSTD_FS::is_symlink(st)) {
__data_.__cache_type_ = directory_entry::_RefreshNonSymlink;
__data_.__type_ = st.type();
__data_.__non_sym_perms_ = st.permissions();
} else { // we have a symlink
__data_.__sym_perms_ = st.permissions();
// Get the information about the linked entity.
// Ignore errors from stat, since we don't want errors regarding symlink
// resolution to be reported to the user.
error_code ignored_ec;
st = detail::posix_stat(__p_, full_st, &ignored_ec);
__data_.__type_ = st.type();
__data_.__non_sym_perms_ = st.permissions();
// If we failed to resolve the link, then only partially populate the
// cache.
if (!status_known(st)) {
__data_.__cache_type_ = directory_entry::_RefreshSymlinkUnresolved;
return error_code{};
}
// Otherwise, we resolved the link as not existing. That's OK.
__data_.__cache_type_ = directory_entry::_RefreshSymlink;
}
if (_VSTD_FS::is_regular_file(st))
__data_.__size_ = static_cast<uintmax_t>(full_st.st_size);
if (_VSTD_FS::exists(st)) {
__data_.__nlink_ = static_cast<uintmax_t>(full_st.st_nlink);
// Attempt to extract the mtime, and fail if it's not representable using
// file_time_type. For now we ignore the error, as we'll report it when
// the value is actually used.
error_code ignored_ec;
__data_.__write_time_ =
__extract_last_write_time(__p_, full_st, &ignored_ec);
}
return failure_ec;
}
#else
error_code directory_entry::__do_refresh() noexcept {
__data_.__reset();
error_code failure_ec;
file_status st = _VSTD_FS::symlink_status(__p_, failure_ec);
if (!status_known(st)) {
__data_.__reset();
return failure_ec;
}
if (!_VSTD_FS::exists(st) || !_VSTD_FS::is_symlink(st)) {
__data_.__cache_type_ = directory_entry::_RefreshNonSymlink;
__data_.__type_ = st.type();
__data_.__non_sym_perms_ = st.permissions();
} else { // we have a symlink
__data_.__sym_perms_ = st.permissions();
// Get the information about the linked entity.
// Ignore errors from stat, since we don't want errors regarding symlink
// resolution to be reported to the user.
error_code ignored_ec;
st = _VSTD_FS::status(__p_, ignored_ec);
__data_.__type_ = st.type();
__data_.__non_sym_perms_ = st.permissions();
// If we failed to resolve the link, then only partially populate the
// cache.
if (!status_known(st)) {
__data_.__cache_type_ = directory_entry::_RefreshSymlinkUnresolved;
return error_code{};
}
// Otherwise, we resolved the link as not existing. That's OK.
__data_.__cache_type_ = directory_entry::_RefreshSymlink;
}
// FIXME: This is currently broken, and the implementation only a placeholder.
// We need to cache last_write_time, file_size, and hard_link_count here before
// the implementation actually works.
return failure_ec;
}
#endif
_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM

View File

@ -0,0 +1,96 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// RUN: %build -I%libcxx_src_root/src/experimental/filesystem
// RUN: %run
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
#include "filesystem_common.h"
using namespace fs::detail;
TEST_SUITE(directory_entry_mods_suite)
TEST_CASE(last_write_time_not_representable_error) {
using namespace fs;
using namespace std::chrono;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
TimeSpec ToTime;
ToTime.tv_sec = std::numeric_limits<decltype(ToTime.tv_sec)>::max();
ToTime.tv_nsec = duration_cast<nanoseconds>(seconds(1)).count() - 1;
TimeStructArray TS;
SetTimeStructTo(TS[0], ToTime);
SetTimeStructTo(TS[1], ToTime);
file_time_type old_time = last_write_time(file);
directory_entry ent(file);
file_time_type start_time = file_time_type::clock::now() - hours(1);
last_write_time(file, start_time);
TEST_CHECK(ent.last_write_time() == old_time);
bool IsRepresentable = true;
file_time_type rep_value;
{
std::error_code ec;
if (SetFileTimes(file, TS, ec)) {
TEST_REQUIRE(false && "unsupported");
}
ec.clear();
rep_value = last_write_time(file, ec);
IsRepresentable = !bool(ec);
}
if (!IsRepresentable) {
std::error_code rec = GetTestEC();
ent.refresh(rec);
TEST_CHECK(!rec);
const std::errc expected_err = std::errc::value_too_large;
std::error_code ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, expected_err));
ec = GetTestEC();
TEST_CHECK(last_write_time(file, ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, expected_err));
ExceptionChecker CheckExcept(file, expected_err);
TEST_CHECK_THROW_RESULT(fs::filesystem_error, CheckExcept,
ent.last_write_time());
} else {
ent.refresh();
std::error_code ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == rep_value);
TEST_CHECK(!ec);
}
}
TEST_SUITE_END()

View File

@ -23,12 +23,12 @@
#include <cstddef>
#include <cassert>
#include "filesystem_time_helper.h"
#include "filesystem_common.h"
using namespace std::chrono;
namespace fs = std::experimental::filesystem;
using fs::file_time_type;
using fs::fs_time_util;
using fs::detail::fs_time_util;
enum TestKind { TK_64Bit, TK_32Bit, TK_FloatingPoint };

View File

@ -1,96 +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 directory_entry
// directory_entry() noexcept = default;
// directory_entry(const directory_entry&) = default;
// directory_entry(directory_entry&&) noexcept = default;
// explicit directory_entry(const path);
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
void test_default_ctor()
{
using namespace fs;
// Default
{
static_assert(std::is_nothrow_default_constructible<directory_entry>::value,
"directory_entry must have a nothrow default constructor");
directory_entry e;
assert(e.path() == path());
}
}
void test_copy_ctor()
{
using namespace fs;
// Copy
{
static_assert(std::is_copy_constructible<directory_entry>::value,
"directory_entry must be copy constructible");
static_assert(!std::is_nothrow_copy_constructible<directory_entry>::value,
"directory_entry's copy constructor cannot be noexcept");
const path p("foo/bar/baz");
const directory_entry e(p);
assert(e.path() == p);
directory_entry e2(e);
assert(e.path() == p);
assert(e2.path() == p);
}
}
void test_move_ctor()
{
using namespace fs;
// Move
{
static_assert(std::is_nothrow_move_constructible<directory_entry>::value,
"directory_entry must be nothrow move constructible");
const path p("foo/bar/baz");
directory_entry e(p);
assert(e.path() == p);
directory_entry e2(std::move(e));
assert(e2.path() == p);
assert(e.path() != p); // Testing moved from state.
}
}
void test_path_ctor() {
using namespace fs;
{
static_assert(std::is_constructible<directory_entry, const path&>::value,
"directory_entry must be constructible from path");
static_assert(!std::is_nothrow_constructible<directory_entry, const path&>::value,
"directory_entry constructor should not be noexcept");
static_assert(!std::is_convertible<path const&, directory_entry>::value,
"directory_entry constructor should be explicit");
}
{
const path p("foo/bar/baz");
const directory_entry e(p);
assert(p == e.path());
}
}
int main() {
test_default_ctor();
test_copy_ctor();
test_move_ctor();
test_path_ctor();
}

View File

@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// directory_entry(const directory_entry&) = default;
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
#include "test_convertible.hpp"
TEST_SUITE(directory_entry_path_ctor_suite)
TEST_CASE(copy_ctor) {
using namespace fs;
// Copy
{
static_assert(std::is_copy_constructible<directory_entry>::value,
"directory_entry must be copy constructible");
static_assert(!std::is_nothrow_copy_constructible<directory_entry>::value,
"directory_entry's copy constructor cannot be noexcept");
const path p("foo/bar/baz");
const directory_entry e(p);
assert(e.path() == p);
directory_entry e2(e);
assert(e.path() == p);
assert(e2.path() == p);
}
}
TEST_CASE(copy_ctor_copies_cache) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path sym = env.create_symlink("dir/file", "sym");
{
directory_entry ent(sym);
fs::remove(sym);
directory_entry ent_cp(ent);
TEST_CHECK(ent_cp.path() == sym);
TEST_CHECK(ent_cp.is_symlink());
}
{
directory_entry ent(file);
fs::remove(file);
directory_entry ent_cp(ent);
TEST_CHECK(ent_cp.path() == file);
TEST_CHECK(ent_cp.is_regular_file());
}
}
TEST_SUITE_END()

View File

@ -0,0 +1,82 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// directory_entry& operator=(directory_entry const&) = default;
// directory_entry& operator=(directory_entry&&) noexcept = default;
// void assign(path const&);
// void replace_filename(path const&);
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
TEST_SUITE(directory_entry_ctor_suite)
TEST_CASE(test_copy_assign_operator) {
using namespace fs;
// Copy
{
static_assert(std::is_copy_assignable<directory_entry>::value,
"directory_entry must be copy assignable");
static_assert(!std::is_nothrow_copy_assignable<directory_entry>::value,
"directory_entry's copy assignment cannot be noexcept");
const path p("foo/bar/baz");
const path p2("abc");
const directory_entry e(p);
directory_entry e2;
assert(e.path() == p && e2.path() == path());
e2 = e;
assert(e.path() == p && e2.path() == p);
directory_entry e3(p2);
e2 = e3;
assert(e2.path() == p2 && e3.path() == p2);
}
}
TEST_CASE(copy_assign_copies_cache) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path sym = env.create_symlink("dir/file", "sym");
{
directory_entry ent(sym);
fs::remove(sym);
directory_entry ent_cp;
ent_cp = ent;
TEST_CHECK(ent_cp.path() == sym);
TEST_CHECK(ent_cp.is_symlink());
}
{
directory_entry ent(file);
fs::remove(file);
directory_entry ent_cp;
ent_cp = ent;
TEST_CHECK(ent_cp.path() == file);
TEST_CHECK(ent_cp.is_regular_file());
}
}
TEST_SUITE_END()

View File

@ -0,0 +1,31 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// directory_entry() noexcept = default;
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
int main() {
using namespace fs;
// Default
{
static_assert(std::is_nothrow_default_constructible<directory_entry>::value,
"directory_entry must have a nothrow default constructor");
directory_entry e;
assert(e.path() == path());
}
}

View File

@ -0,0 +1,72 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// directory_entry(directory_entry&&) noexcept = default;
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
#include "test_convertible.hpp"
TEST_SUITE(directory_entry_path_ctor_suite)
TEST_CASE(move_ctor) {
using namespace fs;
// Move
{
static_assert(std::is_nothrow_move_constructible<directory_entry>::value,
"directory_entry must be nothrow move constructible");
const path p("foo/bar/baz");
directory_entry e(p);
assert(e.path() == p);
directory_entry e2(std::move(e));
assert(e2.path() == p);
assert(e.path() != p); // Testing moved from state.
}
}
TEST_CASE(move_ctor_copies_cache) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path sym = env.create_symlink("dir/file", "sym");
{
directory_entry ent(sym);
fs::remove(sym);
directory_entry ent_cp(std::move(ent));
TEST_CHECK(ent_cp.path() == sym);
TEST_CHECK(ent_cp.is_symlink());
}
{
directory_entry ent(file);
fs::remove(file);
directory_entry ent_cp(std::move(ent));
TEST_CHECK(ent_cp.path() == file);
TEST_CHECK(ent_cp.is_regular_file());
}
}
TEST_SUITE_END()

View File

@ -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>
// class directory_entry
// directory_entry& operator=(directory_entry const&) = default;
// directory_entry& operator=(directory_entry&&) noexcept = default;
// void assign(path const&);
// void replace_filename(path const&);
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
TEST_SUITE(directory_entry_ctor_suite)
TEST_CASE(test_move_assign_operator) {
using namespace fs;
// Copy
{
static_assert(std::is_nothrow_move_assignable<directory_entry>::value,
"directory_entry is noexcept move assignable");
const path p("foo/bar/baz");
const path p2("abc");
directory_entry e(p);
directory_entry e2(p2);
assert(e.path() == p && e2.path() == p2);
e2 = std::move(e);
assert(e2.path() == p);
assert(e.path() != p); // testing moved from state
}
}
TEST_CASE(move_assign_copies_cache) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path sym = env.create_symlink("dir/file", "sym");
{
directory_entry ent(sym);
fs::remove(sym);
directory_entry ent_cp;
ent_cp = std::move(ent);
TEST_CHECK(ent_cp.path() == sym);
TEST_CHECK(ent_cp.is_symlink());
}
{
directory_entry ent(file);
fs::remove(file);
directory_entry ent_cp;
ent_cp = std::move(ent);
TEST_CHECK(ent_cp.path() == file);
TEST_CHECK(ent_cp.is_regular_file());
}
}
TEST_SUITE_END()

View File

@ -0,0 +1,182 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// explicit directory_entry(const path);
// directory_entry(const path&, error_code& ec);
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
#include "test_convertible.hpp"
TEST_SUITE(directory_entry_path_ctor_suite)
TEST_CASE(path_ctor) {
using namespace fs;
{
static_assert(std::is_constructible<directory_entry, const path&>::value,
"directory_entry must be constructible from path");
static_assert(
!std::is_nothrow_constructible<directory_entry, const path&>::value,
"directory_entry constructor should not be noexcept");
static_assert(!std::is_convertible<path const&, directory_entry>::value,
"directory_entry constructor should be explicit");
}
{
const path p("foo/bar/baz");
const directory_entry e(p);
TEST_CHECK(e.path() == p);
}
}
TEST_CASE(path_ec_ctor) {
using namespace fs;
{
static_assert(
std::is_constructible<directory_entry, const path&,
std::error_code&>::value,
"directory_entry must be constructible from path and error_code");
static_assert(!std::is_nothrow_constructible<directory_entry, const path&,
std::error_code&>::value,
"directory_entry constructor should not be noexcept");
static_assert(
test_convertible<directory_entry, const path&, std::error_code&>(),
"directory_entry constructor should not be explicit");
}
{
std::error_code ec = GetTestEC();
const directory_entry e(StaticEnv::File, ec);
TEST_CHECK(e.path() == StaticEnv::File);
TEST_CHECK(!ec);
}
{
const path p("foo/bar/baz");
std::error_code ec = GetTestEC();
const directory_entry e(p, ec);
TEST_CHECK(e.path() == p);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
}
}
TEST_CASE(path_ctor_calls_refresh) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path sym = env.create_symlink("dir/file", "sym");
{
directory_entry ent(file);
std::error_code ec = GetTestEC();
directory_entry ent_ec(file, ec);
TEST_CHECK(!ec);
LIBCPP_ONLY(remove(file));
TEST_CHECK(ent.exists());
TEST_CHECK(ent_ec.exists());
TEST_CHECK(ent.file_size() == 42);
TEST_CHECK(ent_ec.file_size() == 42);
}
env.create_file("dir/file", 101);
{
directory_entry ent(sym);
std::error_code ec = GetTestEC();
directory_entry ent_ec(sym, ec);
TEST_CHECK(!ec);
LIBCPP_ONLY(remove(file));
LIBCPP_ONLY(remove(sym));
TEST_CHECK(ent.is_symlink());
TEST_CHECK(ent_ec.is_symlink());
TEST_CHECK(ent.is_regular_file());
TEST_CHECK(ent_ec.is_regular_file());
TEST_CHECK(ent.file_size() == 101);
TEST_CHECK(ent_ec.file_size() == 101);
}
}
TEST_CASE(path_ctor_dne) {
using namespace fs;
{
std::error_code ec = GetTestEC();
directory_entry ent(StaticEnv::DNE, ec);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
TEST_CHECK(ent.path() == StaticEnv::DNE);
}
// don't report dead symlinks as an error.
{
std::error_code ec = GetTestEC();
directory_entry ent(StaticEnv::BadSymlink, ec);
TEST_CHECK(!ec);
TEST_CHECK(ent.path() == StaticEnv::BadSymlink);
}
// DNE does not cause the constructor to throw
{
directory_entry ent(StaticEnv::DNE);
TEST_CHECK(ent.path() == StaticEnv::DNE);
directory_entry ent_two(StaticEnv::BadSymlink);
TEST_CHECK(ent_two.path() == StaticEnv::BadSymlink);
}
}
TEST_CASE(path_ctor_cannot_resolve) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_out_of_dir = env.create_file("file1", 101);
const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
const path sym_in_dir = env.create_symlink("dir/file1", "dir/sym2");
permissions(dir, perms::none);
{
std::error_code ec = GetTestEC();
directory_entry ent(file, ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
TEST_CHECK(ent.path() == file);
}
{
std::error_code ec = GetTestEC();
directory_entry ent(sym_in_dir, ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
TEST_CHECK(ent.path() == sym_in_dir);
}
{
std::error_code ec = GetTestEC();
directory_entry ent(sym_out_of_dir, ec);
TEST_CHECK(!ec);
TEST_CHECK(ent.path() == sym_out_of_dir);
}
{
TEST_CHECK_NO_THROW(directory_entry(file));
TEST_CHECK_NO_THROW(directory_entry(sym_in_dir));
TEST_CHECK_NO_THROW(directory_entry(sym_out_of_dir));
}
}
TEST_SUITE_END()

View File

@ -1,112 +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 directory_entry
// directory_entry& operator=(directory_entry const&) = default;
// directory_entry& operator=(directory_entry&&) noexcept = default;
// void assign(path const&);
// void replace_filename(path const&);
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
void test_copy_assign_operator()
{
using namespace fs;
// Copy
{
static_assert(std::is_copy_assignable<directory_entry>::value,
"directory_entry must be copy assignable");
static_assert(!std::is_nothrow_copy_assignable<directory_entry>::value,
"directory_entry's copy assignment cannot be noexcept");
const path p("foo/bar/baz");
const path p2("abc");
const directory_entry e(p);
directory_entry e2;
assert(e.path() == p && e2.path() == path());
e2 = e;
assert(e.path() == p && e2.path() == p);
directory_entry e3(p2);
e2 = e3;
assert(e2.path() == p2 && e3.path() == p2);
}
}
void test_move_assign_operator()
{
using namespace fs;
// Copy
{
static_assert(std::is_nothrow_move_assignable<directory_entry>::value,
"directory_entry is noexcept move assignable");
const path p("foo/bar/baz");
const path p2("abc");
directory_entry e(p);
directory_entry e2(p2);
assert(e.path() == p && e2.path() == p2);
e2 = std::move(e);
assert(e2.path() == p);
assert(e.path() != p); // testing moved from state
}
}
void test_path_assign_method()
{
using namespace fs;
const path p("foo/bar/baz");
const path p2("abc");
directory_entry e(p);
{
static_assert(std::is_same<decltype(e.assign(p)), void>::value,
"return type should be void");
static_assert(noexcept(e.assign(p)) == false, "operation must not be noexcept");
}
{
assert(e.path() == p);
e.assign(p2);
assert(e.path() == p2 && e.path() != p);
e.assign(p);
assert(e.path() == p && e.path() != p2);
}
}
void test_replace_filename_method()
{
using namespace fs;
const path p("/path/to/foo.exe");
const path replace("bar.out");
const path expect("/path/to/bar.out");
directory_entry e(p);
{
static_assert(noexcept(e.replace_filename(replace)) == false,
"operation cannot be noexcept");
static_assert(std::is_same<decltype(e.replace_filename(replace)), void>::value,
"operation must return void");
}
{
assert(e.path() == p);
e.replace_filename(replace);
assert(e.path() == expect);
}
}
int main() {
test_copy_assign_operator();
test_move_assign_operator();
test_path_assign_method();
test_replace_filename_method();
}

View File

@ -0,0 +1,132 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// directory_entry& operator=(directory_entry const&) = default;
// directory_entry& operator=(directory_entry&&) noexcept = default;
// void assign(path const&);
// void replace_filename(path const&);
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
TEST_SUITE(directory_entry_mods_suite)
TEST_CASE(test_path_assign_method) {
using namespace fs;
const path p("foo/bar/baz");
const path p2("abc");
directory_entry e(p);
{
static_assert(std::is_same<decltype(e.assign(p)), void>::value,
"return type should be void");
static_assert(noexcept(e.assign(p)) == false,
"operation must not be noexcept");
}
{
TEST_CHECK(e.path() == p);
e.assign(p2);
TEST_CHECK(e.path() == p2 && e.path() != p);
e.assign(p);
TEST_CHECK(e.path() == p && e.path() != p2);
}
}
TEST_CASE(test_path_assign_ec_method) {
using namespace fs;
const path p("foo/bar/baz");
const path p2("abc");
{
std::error_code ec;
directory_entry e(p);
static_assert(std::is_same<decltype(e.assign(p, ec)), void>::value,
"return type should be void");
static_assert(noexcept(e.assign(p, ec)) == false,
"operation must not be noexcept");
}
{
directory_entry ent(p);
std::error_code ec = GetTestEC();
ent.assign(p2, ec);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
TEST_CHECK(ent.path() == p2);
}
}
TEST_CASE(test_assign_calls_refresh) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path sym = env.create_symlink("dir/file", "sym");
{
directory_entry ent;
ent.assign(file);
// removing the file demonstrates that the values where cached previously.
LIBCPP_ONLY(remove(file));
TEST_CHECK(ent.is_regular_file());
}
env.create_file("dir/file", 101);
{
directory_entry ent;
ent.assign(sym);
LIBCPP_ONLY(remove(file));
LIBCPP_ONLY(remove(sym));
TEST_CHECK(ent.is_symlink());
TEST_CHECK(ent.is_regular_file());
}
}
TEST_CASE(test_assign_propagates_error) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
const path file_out_of_dir = env.create_file("file1");
const path sym_in_dir = env.create_symlink("file1", "dir/sym1");
permissions(dir, perms::none);
{
directory_entry ent;
std::error_code ec = GetTestEC();
ent.assign(file, ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
}
{
directory_entry ent;
std::error_code ec = GetTestEC();
ent.assign(sym_in_dir, ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
}
{
directory_entry ent;
std::error_code ec = GetTestEC();
ent.assign(sym_out_of_dir, ec);
TEST_CHECK(!ec);
}
}
TEST_SUITE_END()

View File

@ -0,0 +1,339 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// directory_entry& operator=(directory_entry const&) = default;
// directory_entry& operator=(directory_entry&&) noexcept = default;
// void assign(path const&);
// void replace_filename(path const&);
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
TEST_SUITE(directory_entry_mods_suite)
TEST_CASE(test_refresh_method) {
using namespace fs;
{
directory_entry e;
static_assert(noexcept(e.refresh()) == false,
"operation cannot be noexcept");
static_assert(std::is_same<decltype(e.refresh()), void>::value,
"operation must return void");
}
{
directory_entry e;
e.refresh();
TEST_CHECK(!e.exists());
}
}
TEST_CASE(test_refresh_ec_method) {
using namespace fs;
{
directory_entry e;
std::error_code ec;
static_assert(noexcept(e.refresh(ec)), "operation should be noexcept");
static_assert(std::is_same<decltype(e.refresh(ec)), void>::value,
"operation must return void");
}
{
directory_entry e;
std::error_code ec = GetTestEC();
e.refresh(ec);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
}
}
TEST_CASE(refresh_on_file_dne) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const perms old_perms = status(dir).permissions();
// test file doesn't exist
{
directory_entry ent(file);
remove(file);
TEST_CHECK(ent.exists());
ent.refresh();
permissions(dir, perms::none);
TEST_CHECK(!ent.exists());
}
permissions(dir, old_perms);
env.create_file("dir/file", 101);
{
directory_entry ent(file);
remove(file);
TEST_CHECK(ent.exists());
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
permissions(dir, perms::none);
TEST_CHECK(!ent.exists());
}
}
void remove_if_exists(const fs::path& p) {
std::error_code ec;
remove(p, ec);
}
TEST_CASE(refresh_on_bad_symlink) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path sym = env.create_symlink("dir/file", "sym");
const perms old_perms = status(dir).permissions();
// test file doesn't exist
{
directory_entry ent(sym);
LIBCPP_ONLY(remove(file));
TEST_CHECK(ent.is_symlink());
TEST_CHECK(ent.is_regular_file());
TEST_CHECK(ent.exists());
remove_if_exists(file);
ent.refresh();
LIBCPP_ONLY(permissions(dir, perms::none));
TEST_CHECK(ent.is_symlink());
TEST_CHECK(!ent.is_regular_file());
TEST_CHECK(!ent.exists());
}
permissions(dir, old_perms);
env.create_file("dir/file", 101);
{
directory_entry ent(sym);
LIBCPP_ONLY(remove(file));
TEST_CHECK(ent.is_symlink());
TEST_CHECK(ent.is_regular_file());
TEST_CHECK(ent.exists());
remove_if_exists(file);
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(!ec); // we don't report bad symlinks as an error.
LIBCPP_ONLY(permissions(dir, perms::none));
TEST_CHECK(!ent.exists());
}
}
TEST_CASE(refresh_cannot_resolve) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_out_of_dir = env.create_file("file1", 99);
const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
const path sym_in_dir = env.create_symlink("file1", "dir/sym1");
perms old_perms = status(dir).permissions();
{
directory_entry ent(file);
permissions(dir, perms::none);
TEST_CHECK(ent.is_regular_file());
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
TEST_CHECK(ent.path() == file);
ExceptionChecker Checker(file, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
}
permissions(dir, old_perms);
{
directory_entry ent(sym_in_dir);
permissions(dir, perms::none);
TEST_CHECK(ent.is_symlink());
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
TEST_CHECK(ent.path() == sym_in_dir);
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
}
permissions(dir, old_perms);
{
directory_entry ent(sym_out_of_dir);
permissions(dir, perms::none);
TEST_CHECK(ent.is_symlink());
// Failure to resolve the linked entity due to permissions is not
// reported as an error.
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(!ec);
TEST_CHECK(ent.is_symlink());
ec = GetTestEC();
TEST_CHECK(ent.exists(ec) == false);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
TEST_CHECK(ent.path() == sym_out_of_dir);
}
permissions(dir, old_perms);
{
directory_entry ent_file(file);
directory_entry ent_sym(sym_in_dir);
directory_entry ent_sym2(sym_out_of_dir);
permissions(dir, perms::none);
((void)ent_file);
((void)ent_sym);
TEST_CHECK_THROW(filesystem_error, ent_file.refresh());
TEST_CHECK_THROW(filesystem_error, ent_sym.refresh());
TEST_CHECK_NO_THROW(ent_sym2);
}
}
TEST_CASE(refresh_doesnt_throw_on_dne_but_reports_it) {
using namespace fs;
scoped_test_env env;
const path file = env.create_file("file1", 42);
const path sym = env.create_symlink("file1", "sym");
{
directory_entry ent(file);
TEST_CHECK(ent.file_size() == 42);
remove(file);
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
TEST_CHECK_NO_THROW(ent.refresh());
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
// doesn't throw!
TEST_CHECK_THROW(filesystem_error, ent.file_size());
}
env.create_file("file1", 99);
{
directory_entry ent(sym);
TEST_CHECK(ent.is_symlink());
TEST_CHECK(ent.is_regular_file());
TEST_CHECK(ent.file_size() == 99);
remove(file);
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(!ec);
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
TEST_CHECK_THROW(filesystem_error, ent.file_size());
}
}
TEST_CASE(access_cache_after_refresh_fails) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_out_of_dir = env.create_file("file1", 101);
const path sym = env.create_symlink("dir/file", "sym");
const path sym_in_dir = env.create_symlink("dir/file", "dir/sym2");
const perms old_perms = status(dir).permissions();
#define CHECK_ACCESS(func, expect) \
ec = GetTestEC(); \
TEST_CHECK(ent.func(ec) == expect); \
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied))
// test file doesn't exist
{
directory_entry ent(file);
TEST_CHECK(!ent.is_symlink());
TEST_CHECK(ent.is_regular_file());
TEST_CHECK(ent.exists());
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
CHECK_ACCESS(exists, false);
CHECK_ACCESS(is_symlink, false);
CHECK_ACCESS(last_write_time, file_time_type::min());
CHECK_ACCESS(hard_link_count, uintmax_t(-1));
}
permissions(dir, old_perms);
{
directory_entry ent(sym_in_dir);
TEST_CHECK(ent.is_symlink());
TEST_CHECK(ent.is_regular_file());
TEST_CHECK(ent.exists());
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
CHECK_ACCESS(exists, false);
CHECK_ACCESS(is_symlink, false);
CHECK_ACCESS(last_write_time, file_time_type::min());
CHECK_ACCESS(hard_link_count, uintmax_t(-1));
}
permissions(dir, old_perms);
{
directory_entry ent(sym);
TEST_CHECK(ent.is_symlink());
TEST_CHECK(ent.is_regular_file());
TEST_CHECK(ent.exists());
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.refresh(ec);
TEST_CHECK(!ec);
TEST_CHECK(ent.is_symlink());
CHECK_ACCESS(exists, false);
CHECK_ACCESS(is_regular_file, false);
CHECK_ACCESS(last_write_time, file_time_type::min());
CHECK_ACCESS(hard_link_count, uintmax_t(-1));
}
#undef CHECK_ACCESS
}
TEST_SUITE_END()

View File

@ -0,0 +1,169 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// directory_entry& operator=(directory_entry const&) = default;
// directory_entry& operator=(directory_entry&&) noexcept = default;
// void assign(path const&);
// void replace_filename(path const&);
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
TEST_SUITE(directory_entry_mods_suite)
TEST_CASE(test_replace_filename_method) {
using namespace fs;
{
directory_entry e;
path replace;
static_assert(noexcept(e.replace_filename(replace)) == false,
"operation cannot be noexcept");
static_assert(
std::is_same<decltype(e.replace_filename(replace)), void>::value,
"operation must return void");
}
{
const path p("/path/to/foo.exe");
const path replace("bar.out");
const path expect("/path/to/bar.out");
directory_entry e(p);
TEST_CHECK(e.path() == p);
e.replace_filename(replace);
TEST_CHECK(e.path() == expect);
}
}
TEST_CASE(test_replace_filename_ec_method) {
using namespace fs;
{
directory_entry e;
path replace;
std::error_code ec;
static_assert(noexcept(e.replace_filename(replace, ec)) == false,
"operation cannot be noexcept");
static_assert(
std::is_same<decltype(e.replace_filename(replace, ec)), void>::value,
"operation must return void");
}
{
const path p("/path/to/foo.exe");
const path replace("bar.out");
const path expect("/path/to/bar.out");
directory_entry e(p);
TEST_CHECK(e.path() == p);
std::error_code ec = GetTestEC();
e.replace_filename(replace, ec);
TEST_CHECK(e.path() == expect);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
}
{
const path p = StaticEnv::EmptyFile;
const path expect = StaticEnv::NonEmptyFile;
const path replace = StaticEnv::NonEmptyFile.filename();
TEST_REQUIRE(expect.parent_path() == p.parent_path());
directory_entry e(p);
TEST_CHECK(e.path() == p);
std::error_code ec = GetTestEC();
e.replace_filename(replace, ec);
TEST_CHECK(e.path() == expect);
TEST_CHECK(!ec);
}
}
TEST_CASE(test_replace_filename_calls_refresh) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_two = env.create_file("dir/file_two", 101);
const path sym = env.create_symlink("dir/file", "sym");
const path sym_two = env.create_symlink("dir/file_two", "sym_two");
{
directory_entry ent(file);
ent.replace_filename(file_two.filename());
TEST_REQUIRE(ent.path() == file_two);
// removing the file demonstrates that the values where cached previously.
LIBCPP_ONLY(remove(file_two));
TEST_CHECK(ent.file_size() == 101);
}
env.create_file("dir/file_two", 99);
{
directory_entry ent(sym);
ent.replace_filename(sym_two.filename());
TEST_REQUIRE(ent.path() == sym_two);
LIBCPP_ONLY(remove(file_two));
LIBCPP_ONLY(remove(sym_two));
TEST_CHECK(ent.is_symlink());
TEST_CHECK(ent.is_regular_file());
TEST_CHECK(ent.file_size() == 99);
}
}
TEST_CASE(test_replace_filename_propagates_error) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_two = env.create_file("dir/file_two", 99);
const path file_out_of_dir = env.create_file("file_three", 101);
const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
const path sym_out_of_dir_two = env.create_symlink("dir/file", "sym_two");
const path sym_in_dir = env.create_symlink("file_two", "dir/sym_three");
const path sym_in_dir_two = env.create_symlink("file_two", "dir/sym_four");
const perms old_perms = status(dir).permissions();
{
directory_entry ent(file);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.replace_filename(file_two.filename(), ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
}
permissions(dir, old_perms);
{
directory_entry ent(sym_in_dir);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.replace_filename(sym_in_dir_two.filename(), ec);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
}
permissions(dir, old_perms);
{
directory_entry ent(sym_out_of_dir);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.replace_filename(sym_out_of_dir_two.filename(), ec);
TEST_CHECK(!ec);
TEST_CHECK(ent.is_symlink());
ec = GetTestEC();
TEST_CHECK(!ent.exists(ec));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
}
}
TEST_SUITE_END()

View File

@ -0,0 +1,238 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// uintmax_t file_size() const;
// uintmax_t file_size(error_code const&) const noexcept;
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "filesystem_test_helper.hpp"
#include "rapid-cxx-test.hpp"
#include <iostream>
TEST_SUITE(directory_entry_obs_testsuite)
TEST_CASE(signatures) {
using namespace fs;
{
const fs::directory_entry e;
std::error_code ec;
static_assert(std::is_same<decltype(e.file_size()), uintmax_t>::value, "");
static_assert(std::is_same<decltype(e.file_size(ec)), uintmax_t>::value,
"");
static_assert(noexcept(e.file_size()) == false, "");
static_assert(noexcept(e.file_size(ec)) == true, "");
}
}
TEST_CASE(basic) {
using namespace fs;
scoped_test_env env;
const path file = env.create_file("file", 42);
const path dir = env.create_dir("dir");
const path sym = env.create_symlink("file", "sym");
{
directory_entry ent(file);
uintmax_t expect = file_size(ent);
TEST_CHECK(expect == 42);
// Remove the file to show that the results were already in the cache.
LIBCPP_ONLY(remove(file));
std::error_code ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == expect);
TEST_CHECK(!ec);
}
env.create_file("file", 99);
{
directory_entry ent(sym);
uintmax_t expect = file_size(ent);
TEST_CHECK(expect == 99);
LIBCPP_ONLY(remove(ent));
std::error_code ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == 99);
TEST_CHECK(!ec);
}
}
TEST_CASE(not_regular_file) {
using namespace fs;
scoped_test_env env;
struct {
const path p;
std::errc expected_err;
} TestCases[] = {
{env.create_dir("dir"), std::errc::is_a_directory},
{env.create_fifo("fifo"), std::errc::not_supported},
{env.create_symlink("dir", "sym"), std::errc::is_a_directory}};
for (auto const& TC : TestCases) {
const path& p = TC.p;
directory_entry ent(p);
TEST_CHECK(ent.path() == p);
std::error_code ec = GetTestEC(0);
std::error_code other_ec = GetTestEC(1);
uintmax_t expect = file_size(p, other_ec);
uintmax_t got = ent.file_size(ec);
TEST_CHECK(got == expect);
TEST_CHECK(got == uintmax_t(-1));
TEST_CHECK(ec == other_ec);
TEST_CHECK(ErrorIs(ec, TC.expected_err));
ExceptionChecker Checker(p, TC.expected_err);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
}
}
TEST_CASE(error_reporting) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_out_of_dir = env.create_file("file2", 101);
const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
const path sym_in_dir = env.create_symlink("file2", "dir/sym2");
const perms old_perms = status(dir).permissions();
// test a file which doesn't exist
{
directory_entry ent;
std::error_code ec = GetTestEC();
ent.assign(StaticEnv::DNE, ec);
TEST_REQUIRE(ent.path() == StaticEnv::DNE);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::DNE,
std::errc::no_such_file_or_directory);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
}
// test a dead symlink
{
directory_entry ent;
std::error_code ec = GetTestEC();
uintmax_t expect_bad = file_size(StaticEnv::BadSymlink, ec);
TEST_CHECK(expect_bad == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ec = GetTestEC();
ent.assign(StaticEnv::BadSymlink, ec);
TEST_REQUIRE(ent.path() == StaticEnv::BadSymlink);
TEST_CHECK(!ec);
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == expect_bad);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::BadSymlink,
std::errc::no_such_file_or_directory);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
}
// test a file w/o appropriate permissions.
{
directory_entry ent;
uintmax_t expect_good = file_size(file);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(file, ec);
TEST_REQUIRE(ent.path() == file);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(file, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.file_size());
}
permissions(dir, old_perms);
// test a symlink w/o appropriate permissions.
{
directory_entry ent;
uintmax_t expect_good = file_size(sym_in_dir);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(sym_in_dir, ec);
TEST_REQUIRE(ent.path() == sym_in_dir);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.file_size());
}
permissions(dir, old_perms);
// test a symlink to a file w/o appropriate permissions
{
directory_entry ent;
uintmax_t expect_good = file_size(sym_out_of_dir);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(sym_out_of_dir, ec);
TEST_REQUIRE(ent.path() == sym_out_of_dir);
TEST_CHECK(!ec);
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.file_size(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.file_size());
}
}
TEST_SUITE_END()

View File

@ -0,0 +1,258 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// file_status status() const;
// file_status status(error_code const&) const noexcept;
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "filesystem_test_helper.hpp"
#include "rapid-cxx-test.hpp"
TEST_SUITE(directory_entry_obs_testsuite)
TEST_CASE(file_dne) {
using namespace fs;
directory_entry p("dne");
}
TEST_CASE(signatures) {
using namespace fs;
const directory_entry e;
std::error_code ec;
#define TEST_FUNC(name) \
static_assert(std::is_same<decltype(e.name()), bool>::value, \
"wrong return type"); \
static_assert(noexcept(e.name()) == false, "should not be noexcept"); \
static_assert(std::is_same<decltype(e.name(ec)), bool>::value, \
"wrong return type"); \
static_assert(noexcept(e.name(ec)) == true, "should be noexcept")
TEST_FUNC(exists);
TEST_FUNC(is_block_file);
TEST_FUNC(is_character_file);
TEST_FUNC(is_directory);
TEST_FUNC(is_fifo);
TEST_FUNC(is_other);
TEST_FUNC(is_regular_file);
TEST_FUNC(is_socket);
TEST_FUNC(is_symlink);
#undef TEST_FUNC
}
TEST_CASE(test_without_ec) {
using namespace fs;
using fs::directory_entry;
using fs::file_status;
using fs::path;
scoped_test_env env;
path f = env.create_file("foo", 42);
path d = env.create_dir("dir");
path fifo = env.create_fifo("fifo");
path hl = env.create_hardlink("foo", "hl");
for (auto p : {hl, f, d, fifo}) {
directory_entry e(p);
file_status st = status(p);
file_status sym_st = symlink_status(p);
fs::remove(p);
TEST_REQUIRE(e.exists());
TEST_REQUIRE(!exists(p));
TEST_CHECK(e.exists() == exists(st));
TEST_CHECK(e.is_block_file() == is_block_file(st));
TEST_CHECK(e.is_character_file() == is_character_file(st));
TEST_CHECK(e.is_directory() == is_directory(st));
TEST_CHECK(e.is_fifo() == is_fifo(st));
TEST_CHECK(e.is_other() == is_other(st));
TEST_CHECK(e.is_regular_file() == is_regular_file(st));
TEST_CHECK(e.is_socket() == is_socket(st));
TEST_CHECK(e.is_symlink() == is_symlink(sym_st));
}
}
TEST_CASE(test_with_ec) {
using namespace fs;
using fs::directory_entry;
using fs::file_status;
using fs::path;
scoped_test_env env;
path f = env.create_file("foo", 42);
path d = env.create_dir("dir");
path fifo = env.create_fifo("fifo");
path hl = env.create_hardlink("foo", "hl");
for (auto p : {hl, f, d, fifo}) {
directory_entry e(p);
std::error_code status_ec = GetTestEC();
std::error_code sym_status_ec = GetTestEC(1);
file_status st = status(p, status_ec);
file_status sym_st = symlink_status(p, sym_status_ec);
fs::remove(p);
std::error_code ec = GetTestEC(2);
auto CheckEC = [&](std::error_code const& other_ec) {
bool res = ec == other_ec;
ec = GetTestEC(2);
return res;
};
TEST_REQUIRE(e.exists(ec));
TEST_CHECK(CheckEC(status_ec));
TEST_REQUIRE(!exists(p));
TEST_CHECK(e.exists(ec) == exists(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_block_file(ec) == is_block_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_character_file(ec) == is_character_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_directory(ec) == is_directory(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_fifo(ec) == is_fifo(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_other(ec) == is_other(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_regular_file(ec) == is_regular_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_socket(ec) == is_socket(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_symlink(ec) == is_symlink(sym_st));
TEST_CHECK(CheckEC(sym_status_ec));
}
}
TEST_CASE(test_with_ec_dne) {
using namespace fs;
using fs::directory_entry;
using fs::file_status;
using fs::path;
for (auto p : {StaticEnv::DNE, StaticEnv::BadSymlink}) {
directory_entry e(p);
std::error_code status_ec = GetTestEC();
std::error_code sym_status_ec = GetTestEC(1);
file_status st = status(p, status_ec);
file_status sym_st = symlink_status(p, sym_status_ec);
std::error_code ec = GetTestEC(2);
auto CheckEC = [&](std::error_code const& other_ec) {
bool res = ec == other_ec;
ec = GetTestEC(2);
return res;
};
TEST_CHECK(e.exists(ec) == exists(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_block_file(ec) == is_block_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_character_file(ec) == is_character_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_directory(ec) == is_directory(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_fifo(ec) == is_fifo(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_other(ec) == is_other(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_regular_file(ec) == is_regular_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_socket(ec) == is_socket(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_symlink(ec) == is_symlink(sym_st));
TEST_CHECK(CheckEC(sym_status_ec));
}
}
TEST_CASE(test_with_ec_cannot_resolve) {
using namespace fs;
using fs::directory_entry;
using fs::file_status;
using fs::path;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_out_of_dir = env.create_file("file2", 99);
const path sym = env.create_symlink("file2", "dir/sym");
perms old_perms = fs::status(dir).permissions();
for (auto p : {file, sym}) {
permissions(dir, old_perms);
directory_entry e(p);
permissions(dir, perms::none);
std::error_code dummy_ec;
e.refresh(dummy_ec);
TEST_REQUIRE(dummy_ec);
std::error_code status_ec = GetTestEC();
std::error_code sym_status_ec = GetTestEC(1);
file_status st = status(p, status_ec);
file_status sym_st = symlink_status(p, sym_status_ec);
std::error_code ec = GetTestEC(2);
auto CheckEC = [&](std::error_code const& other_ec) {
bool res = ec == other_ec;
ec = GetTestEC(2);
return res;
};
TEST_CHECK(e.exists(ec) == exists(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_block_file(ec) == is_block_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_character_file(ec) == is_character_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_directory(ec) == is_directory(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_fifo(ec) == is_fifo(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_other(ec) == is_other(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_regular_file(ec) == is_regular_file(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_socket(ec) == is_socket(st));
TEST_CHECK(CheckEC(status_ec));
TEST_CHECK(e.is_symlink(ec) == is_symlink(sym_st));
TEST_CHECK(CheckEC(sym_status_ec));
}
}
TEST_SUITE_END()

View File

@ -0,0 +1,237 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// uintmax_t hard_link_count() const;
// uintmax_t hard_link_count(error_code const&) const noexcept;
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "filesystem_test_helper.hpp"
#include "rapid-cxx-test.hpp"
TEST_SUITE(directory_entry_obs_testsuite)
TEST_CASE(signatures) {
using namespace fs;
{
const fs::directory_entry e;
std::error_code ec;
static_assert(std::is_same<decltype(e.hard_link_count()), uintmax_t>::value, "");
static_assert(std::is_same<decltype(e.hard_link_count(ec)), uintmax_t>::value,
"");
static_assert(noexcept(e.hard_link_count()) == false, "");
static_assert(noexcept(e.hard_link_count(ec)) == true, "");
}
}
TEST_CASE(basic) {
using namespace fs;
scoped_test_env env;
const path file = env.create_file("file", 42);
const path dir = env.create_dir("dir");
const path sym = env.create_symlink("file", "sym");
{
directory_entry ent(file);
uintmax_t expect = hard_link_count(ent);
// Remove the file to show that the results were already in the cache.
LIBCPP_ONLY(remove(file));
std::error_code ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == expect);
TEST_CHECK(!ec);
}
{
directory_entry ent(dir);
uintmax_t expect = hard_link_count(ent);
LIBCPP_ONLY(remove(dir));
std::error_code ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == expect);
TEST_CHECK(!ec);
}
env.create_file("file", 99);
env.create_hardlink("file", "hl");
{
directory_entry ent(sym);
std::error_code ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == 2);
TEST_CHECK(!ec);
}
}
TEST_CASE(not_regular_file) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path dir2 = env.create_dir("dir/dir2");
const path fifo = env.create_fifo("dir/fifo");
const path sym_to_fifo = env.create_symlink("dir/fifo", "dir/sym");
const perms old_perms = status(dir).permissions();
for (auto p : {dir2, fifo, sym_to_fifo}) {
permissions(dir, old_perms);
std::error_code dummy_ec = GetTestEC();
directory_entry ent(p, dummy_ec);
TEST_CHECK(!dummy_ec);
uintmax_t expect = hard_link_count(p);
LIBCPP_ONLY(permissions(dir, perms::none));
std::error_code ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == expect);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.hard_link_count());
}
}
TEST_CASE(error_reporting) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_out_of_dir = env.create_file("file2", 101);
const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
const path sym_in_dir = env.create_symlink("file2", "dir/sym2");
const perms old_perms = status(dir).permissions();
// test a file which doesn't exist
{
directory_entry ent;
std::error_code ec = GetTestEC();
ent.assign(StaticEnv::DNE, ec);
TEST_CHECK(ec);
TEST_REQUIRE(ent.path() == StaticEnv::DNE);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::DNE,
std::errc::no_such_file_or_directory);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
}
// test a dead symlink
{
directory_entry ent;
std::error_code ec = GetTestEC();
uintmax_t expect_bad = hard_link_count(StaticEnv::BadSymlink, ec);
TEST_CHECK(expect_bad == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ec = GetTestEC();
ent.assign(StaticEnv::BadSymlink, ec);
TEST_REQUIRE(ent.path() == StaticEnv::BadSymlink);
TEST_CHECK(!ec);
ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == expect_bad);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::BadSymlink,
std::errc::no_such_file_or_directory);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
}
// test a file w/o appropriate permissions.
{
directory_entry ent;
uintmax_t expect_good = hard_link_count(file);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(file, ec);
TEST_REQUIRE(ent.path() == file);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(file, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.hard_link_count());
}
permissions(dir, old_perms);
// test a symlink w/o appropriate permissions.
{
directory_entry ent;
uintmax_t expect_good = hard_link_count(sym_in_dir);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(sym_in_dir, ec);
TEST_REQUIRE(ent.path() == sym_in_dir);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.hard_link_count());
}
permissions(dir, old_perms);
// test a symlink to a file w/o appropriate permissions
{
directory_entry ent;
uintmax_t expect_good = hard_link_count(sym_out_of_dir);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(sym_out_of_dir, ec);
TEST_REQUIRE(ent.path() == sym_out_of_dir);
TEST_CHECK(!ec);
ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.hard_link_count(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.hard_link_count());
}
}
TEST_SUITE_END()

View File

@ -0,0 +1,210 @@
//===----------------------------------------------------------------------===//
//
// 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 directory_entry
// file_time_type last_write_time() const;
// file_time_type last_write_time(error_code const&) const noexcept;
#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include "filesystem_test_helper.hpp"
#include "rapid-cxx-test.hpp"
TEST_SUITE(directory_entry_obs_testsuite)
TEST_CASE(signatures) {
using namespace fs;
{
const fs::directory_entry e;
std::error_code ec;
static_assert(std::is_same<decltype(e.last_write_time()), file_time_type>::value,
"");
static_assert(std::is_same<decltype(e.last_write_time(ec)), file_time_type>::value,
"");
static_assert(noexcept(e.last_write_time()) == false, "");
static_assert(noexcept(e.last_write_time(ec)) == true, "");
}
}
TEST_CASE(basic) {
using namespace fs;
scoped_test_env env;
const path file = env.create_file("file", 42);
const path dir = env.create_dir("dir");
const path sym = env.create_symlink("file", "sym");
{
directory_entry ent(file);
file_time_type expect = last_write_time(ent);
// Remove the file to show that the results were already in the cache.
LIBCPP_ONLY(remove(file));
std::error_code ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == expect);
TEST_CHECK(!ec);
}
{
directory_entry ent(dir);
file_time_type expect = last_write_time(ent);
LIBCPP_ONLY(remove(dir));
std::error_code ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == expect);
TEST_CHECK(!ec);
}
env.create_file("file", 99);
{
directory_entry ent(sym);
file_time_type expect = last_write_time(sym);
std::error_code ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == expect);
TEST_CHECK(!ec);
}
}
TEST_CASE(error_reporting) {
using namespace fs;
scoped_test_env env;
const path dir = env.create_dir("dir");
const path file = env.create_file("dir/file", 42);
const path file_out_of_dir = env.create_file("file2", 101);
const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
const path sym_in_dir = env.create_symlink("file2", "dir/sym2");
const perms old_perms = status(dir).permissions();
// test a file which doesn't exist
{
directory_entry ent;
std::error_code ec = GetTestEC();
ent.assign(StaticEnv::DNE, ec);
TEST_REQUIRE(ent.path() == StaticEnv::DNE);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::DNE,
std::errc::no_such_file_or_directory);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
}
// test a dead symlink
{
directory_entry ent;
std::error_code ec = GetTestEC();
file_time_type expect_bad = last_write_time(StaticEnv::BadSymlink, ec);
TEST_CHECK(expect_bad == file_time_type::min());
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ec = GetTestEC();
ent.assign(StaticEnv::BadSymlink, ec);
TEST_REQUIRE(ent.path() == StaticEnv::BadSymlink);
TEST_CHECK(!ec);
ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == expect_bad);
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::BadSymlink,
std::errc::no_such_file_or_directory);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
}
// test a file w/o appropriate permissions.
{
directory_entry ent;
file_time_type expect_good = last_write_time(file);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(file, ec);
TEST_REQUIRE(ent.path() == file);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(file, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.last_write_time());
}
permissions(dir, old_perms);
// test a symlink w/o appropriate permissions.
{
directory_entry ent;
file_time_type expect_good = last_write_time(sym_in_dir);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(sym_in_dir, ec);
TEST_REQUIRE(ent.path() == sym_in_dir);
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.last_write_time());
}
permissions(dir, old_perms);
// test a symlink to a file w/o appropriate permissions
{
directory_entry ent;
file_time_type expect_good = last_write_time(sym_out_of_dir);
permissions(dir, perms::none);
std::error_code ec = GetTestEC();
ent.assign(sym_out_of_dir, ec);
TEST_REQUIRE(ent.path() == sym_out_of_dir);
TEST_CHECK(!ec);
ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
permissions(dir, old_perms);
ec = GetTestEC();
TEST_CHECK(ent.last_write_time(ec) == expect_good);
TEST_CHECK(!ec);
TEST_CHECK_NO_THROW(ent.last_write_time());
}
}
TEST_SUITE_END()

View File

@ -21,31 +21,38 @@
#include <cassert>
#include "filesystem_test_helper.hpp"
#include "rapid-cxx-test.hpp"
int main()
{
TEST_SUITE(directory_entry_status_testsuite)
TEST_CASE(test_basic) {
using namespace fs;
{
const directory_entry e("foo");
const fs::directory_entry e("foo");
std::error_code ec;
static_assert(std::is_same<decltype(e.status()), file_status>::value, "");
static_assert(std::is_same<decltype(e.status(ec)), file_status>::value, "");
static_assert(std::is_same<decltype(e.status()), fs::file_status>::value, "");
static_assert(std::is_same<decltype(e.status(ec)), fs::file_status>::value, "");
static_assert(noexcept(e.status()) == false, "");
static_assert(noexcept(e.status(ec)) == true, "");
}
auto TestFn = [](path const& p) {
path TestCases[] = {StaticEnv::File, StaticEnv::Dir, StaticEnv::SymlinkToFile,
StaticEnv::DNE};
for (const auto& p : TestCases) {
const directory_entry e(p);
std::error_code pec, eec;
std::error_code pec = GetTestEC(), eec = GetTestEC(1);
file_status ps = fs::status(p, pec);
file_status es = e.status(eec);
assert(ps.type() == es.type());
assert(ps.permissions() == es.permissions());
assert(pec == eec);
};
{
TestFn(StaticEnv::File);
TestFn(StaticEnv::Dir);
TestFn(StaticEnv::SymlinkToFile);
TestFn(StaticEnv::DNE);
TEST_CHECK(ps.type() == es.type());
TEST_CHECK(ps.permissions() == es.permissions());
TEST_CHECK(pec == eec);
}
for (const auto& p : TestCases) {
const directory_entry e(p);
file_status ps = fs::status(p);
file_status es = e.status();
TEST_CHECK(ps.type() == es.type());
TEST_CHECK(ps.permissions() == es.permissions());
}
}
TEST_SUITE_END()

View File

@ -21,8 +21,11 @@
#include <cassert>
#include "filesystem_test_helper.hpp"
#include "rapid-cxx-test.hpp"
int main() {
TEST_SUITE(directory_entry_obs_suite)
TEST_CASE(test_signature) {
using namespace fs;
{
const directory_entry e("foo");
@ -32,19 +35,24 @@ int main() {
static_assert(noexcept(e.symlink_status()) == false, "");
static_assert(noexcept(e.symlink_status(ec)) == true, "");
}
auto TestFn = [](path const& p) {
path TestCases[] = {StaticEnv::File, StaticEnv::Dir, StaticEnv::SymlinkToFile,
StaticEnv::DNE};
for (const auto& p : TestCases) {
const directory_entry e(p);
std::error_code pec, eec;
std::error_code pec = GetTestEC(), eec = GetTestEC(1);
file_status ps = fs::symlink_status(p, pec);
file_status es = e.symlink_status(eec);
assert(ps.type() == es.type());
assert(ps.permissions() == es.permissions());
assert(pec == eec);
};
{
TestFn(StaticEnv::File);
TestFn(StaticEnv::Dir);
TestFn(StaticEnv::SymlinkToFile);
TestFn(StaticEnv::DNE);
TEST_CHECK(ps.type() == es.type());
TEST_CHECK(ps.permissions() == es.permissions());
TEST_CHECK(pec == eec);
}
for (const auto& p : TestCases) {
const directory_entry e(p);
file_status ps = fs::symlink_status(p);
file_status es = e.symlink_status();
TEST_CHECK(ps.type() == es.type());
TEST_CHECK(ps.permissions() == es.permissions());
}
}
TEST_SUITE_END()

View File

@ -62,17 +62,22 @@ TEST_CASE(symlink_test_case)
TEST_CASE(file_size_error_cases)
{
const path testCases[] = {
StaticEnv::Dir,
StaticEnv::SymlinkToDir,
StaticEnv::BadSymlink,
StaticEnv::DNE
};
struct {
path p;
std::errc expected_err;
} TestCases[] = {
{StaticEnv::Dir, std::errc::is_a_directory},
{StaticEnv::SymlinkToDir, std::errc::is_a_directory},
{StaticEnv::BadSymlink, std::errc::no_such_file_or_directory},
{StaticEnv::DNE, std::errc::no_such_file_or_directory}};
const uintmax_t expect = static_cast<uintmax_t>(-1);
for (auto& TC : testCases) {
std::error_code ec;
TEST_CHECK(file_size(TC, ec) == expect);
TEST_CHECK(ec);
for (auto& TC : TestCases) {
std::error_code ec = GetTestEC();
TEST_CHECK(file_size(TC.p, ec) == expect);
TEST_CHECK(ErrorIs(ec, TC.expected_err));
ExceptionChecker Checker(TC.p, TC.expected_err);
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, file_size(TC.p));
}
}

View File

@ -8,6 +8,9 @@
#include <fstream>
#include <random>
#include <chrono>
#include <vector>
#include "rapid-cxx-test.hpp"
// static test helpers
@ -381,8 +384,39 @@ bool checkCollectionsEqualBackwards(
// We often need to test that the error_code was cleared if no error occurs
// this function returns an error_code which is set to an error that will
// never be returned by the filesystem functions.
inline std::error_code GetTestEC() {
return std::make_error_code(std::errc::address_family_not_supported);
inline std::error_code GetTestEC(unsigned Idx = 0) {
using std::errc;
auto GetErrc = [&]() {
switch (Idx) {
case 0:
return errc::address_family_not_supported;
case 1:
return errc::address_not_available;
case 2:
return errc::address_in_use;
case 3:
return errc::argument_list_too_long;
default:
assert(false && "Idx out of range");
std::abort();
}
};
return std::make_error_code(GetErrc());
}
inline bool ErrorIsImp(const std::error_code& ec,
std::vector<std::errc> const& errors) {
for (auto errc : errors) {
if (ec == std::make_error_code(errc))
return true;
}
return false;
}
template <class... ErrcT>
inline bool ErrorIs(const std::error_code& ec, std::errc First, ErrcT... Rest) {
std::vector<std::errc> errors = {First, Rest...};
return ErrorIsImp(ec, errors);
}
// Provide our own Sleep routine since std::this_thread::sleep_for is not
@ -403,4 +437,26 @@ inline bool PathEq(fs::path const& LHS, fs::path const& RHS) {
return LHS.native() == RHS.native();
}
struct ExceptionChecker {
std::vector<std::errc> expected_err_list;
fs::path expected_path1;
fs::path expected_path2;
template <class... ErrcT>
explicit ExceptionChecker(fs::path p, std::errc first_err, ErrcT... rest_err)
: expected_err_list({first_err, rest_err...}), expected_path1(p) {}
template <class... ErrcT>
explicit ExceptionChecker(fs::path p1, fs::path p2, std::errc first_err,
ErrcT... rest_err)
: expected_err_list({first_err, rest_err...}), expected_path1(p1),
expected_path2(p2) {}
void operator()(fs::filesystem_error const& Err) const {
TEST_CHECK(ErrorIsImp(Err.code(), expected_err_list));
TEST_CHECK(Err.path1() == expected_path1);
TEST_CHECK(Err.path2() == expected_path2);
}
};
#endif /* FILESYSTEM_TEST_HELPER_HPP */

View File

@ -221,6 +221,24 @@ namespace Name \
} while (false)
#
#define TEST_CHECK_THROW_RESULT(Except, Checker, ...) \
do { \
TEST_SET_CHECKPOINT(); \
::rapid_cxx_test::test_outcome m_f(::rapid_cxx_test::failure_type::none, \
__FILE__, TEST_FUNC_NAME(), __LINE__, \
"TEST_CHECK_THROW_RESULT(" #Except \
"," #Checker "," #__VA_ARGS__ ")", \
""); \
try { \
(static_cast<void>(__VA_ARGS__)); \
m_f.type = ::rapid_cxx_test::failure_type::check; \
} catch (Except const& Caught) { \
Checker(Caught); \
} \
::rapid_cxx_test::get_reporter().report(m_f); \
} while (false)
#
#else // TEST_HAS_NO_EXCEPTIONS
# define TEST_CHECK_NO_THROW(...) \
@ -236,6 +254,7 @@ namespace Name \
#
#define TEST_CHECK_THROW(Except, ...) ((void)0)
#define TEST_CHECK_THROW_RESULT(Except, Checker, ...) ((void)0)
#endif // TEST_HAS_NO_EXCEPTIONS

View File

@ -148,7 +148,7 @@
<tr><td><a href="https://wg21.link/P0156R2">P0156R2</a></td><td>LWG</td><td>Variadic Lock guard(rev 5)</td><td>Kona</td><td>Complete</td><td>5.0</td></tr>
<tr><td><a href="https://wg21.link/P0270R3">P0270R3</a></td><td>CWG</td><td>Removing C dependencies from signal handler wording</td><td>Kona</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P0298R3">P0298R3</a></td><td>CWG</td><td>A byte type definition</td><td>Kona</td><td>Complete</td><td>5.0</td></tr>
<tr><td><a href="https://wg21.link/P0317R1">P0317R1</a></td><td>LWG</td><td>Directory Entry Caching for Filesystem</td><td>Kona</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P0317R1">P0317R1</a></td><td>LWG</td><td>Directory Entry Caching for Filesystem</td><td>Kona</td><td>Complete</td><td>7.0</td></tr>
<tr><td><a href="https://wg21.link/P0430R2">P0430R2</a></td><td>LWG</td><td>File system library on non-POSIX-like operating systems</td><td>Kona</td><td>Complete</td><td>7.0</td></tr>
<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 &lt;numeric&gt; Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr>