}
+class directory_iterator;
+class recursive_directory_iterator;
+class __dir_stream;
+
class directory_entry
{
typedef _VSTD_FS::path _Path;
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() {}
_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_;
}
}
_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
bool operator>=(directory_entry const& __rhs) const _NOEXCEPT {
return __p_ >= __rhs.__p_;
}
+
private:
- _Path __p_;
-};
+ 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();
+ }
+ };
-class directory_iterator;
-class recursive_directory_iterator;
-class __dir_stream;
+ _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 __dir_element_proxy {
public:
#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();
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;
__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 =
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());
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;
}
}
}
//
//===----------------------------------------------------------------------===////
-#ifndef FILESYSTEM_TIME_HELPER_H
-#define FILESYSTEM_TIME_HELPER_H
+#ifndef FILESYSTEM_COMMON_H
+#define FILESYSTEM_COMMON_H
#include "experimental/__config"
#include "chrono"
#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;
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>
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);
}
};
-} // 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
#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
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
-
- 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;
-}
-
-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);
+ 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);
+
+ 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, 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
__create_symlink(real_path, new_symlink, ec);
}
-
bool __create_directories(const path& p, std::error_code *ec)
{
std::error_code m_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();
_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)
{
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
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)
return result.lexically_normal();
}
-
-
///////////////////////////////////////////////////////////////////////////////
// path definitions
///////////////////////////////////////////////////////////////////////////////
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
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
#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 };
+++ /dev/null
-//===----------------------------------------------------------------------===//
-//
-// 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();
-}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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());
+ }
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
+++ /dev/null
-//===----------------------------------------------------------------------===//
-//
-// 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();
-}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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()
#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()
#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");
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()
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));
}
}
#include <fstream>
#include <random>
#include <chrono>
+#include <vector>
+
+#include "rapid-cxx-test.hpp"
// static test helpers
// 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
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 */
} 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(...) \
#
#define TEST_CHECK_THROW(Except, ...) ((void)0)
+#define TEST_CHECK_THROW_RESULT(Except, Checker, ...) ((void)0)
#endif // TEST_HAS_NO_EXCEPTIONS
<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 <numeric> Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr>