2,050 changes: 2,050 additions & 0 deletions libcxx/include/experimental/filesystem

Large diffs are not rendered by default.

31 changes: 25 additions & 6 deletions libcxx/include/iomanip
Original file line number Diff line number Diff line change
Expand Up @@ -512,8 +512,6 @@ put_time(const tm* __tm, const _CharT* __fmt)
return __iom_t10<_CharT>(__tm, __fmt);
}

#if _LIBCPP_STD_VER > 11

template <class _CharT, class _Traits, class _ForwardIterator>
std::basic_ostream<_CharT, _Traits> &
__quoted_output ( basic_ostream<_CharT, _Traits> &__os,
Expand Down Expand Up @@ -631,22 +629,43 @@ quoted ( const _CharT *__s, _CharT __delim = _CharT('"'), _CharT __escape =_Char
return __quoted_output_proxy<_CharT, const _CharT *> ( __s, __end, __delim, __escape );
}


template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_INLINE_VISIBILITY
__quoted_output_proxy<_CharT, typename basic_string <_CharT, _Traits, _Allocator>::const_iterator>
quoted ( const basic_string <_CharT, _Traits, _Allocator> &__s, _CharT __delim = _CharT('"'), _CharT __escape=_CharT('\\'))
__quoted ( const basic_string <_CharT, _Traits, _Allocator> &__s, _CharT __delim = _CharT('"'), _CharT __escape=_CharT('\\'))
{
return __quoted_output_proxy<_CharT,
typename basic_string <_CharT, _Traits, _Allocator>::const_iterator>
return __quoted_output_proxy<_CharT,
typename basic_string <_CharT, _Traits, _Allocator>::const_iterator>
( __s.cbegin(), __s.cend (), __delim, __escape );
}

template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_INLINE_VISIBILITY
__quoted_proxy<_CharT, _Traits, _Allocator>
quoted ( basic_string <_CharT, _Traits, _Allocator> &__s, _CharT __delim = _CharT('"'), _CharT __escape=_CharT('\\'))
__quoted ( basic_string <_CharT, _Traits, _Allocator> &__s, _CharT __delim = _CharT('"'), _CharT __escape=_CharT('\\'))
{
return __quoted_proxy<_CharT, _Traits, _Allocator>( __s, __delim, __escape );
}


#if _LIBCPP_STD_VER > 11

template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_INLINE_VISIBILITY
__quoted_output_proxy<_CharT, typename basic_string <_CharT, _Traits, _Allocator>::const_iterator>
quoted ( const basic_string <_CharT, _Traits, _Allocator> &__s, _CharT __delim = _CharT('"'), _CharT __escape=_CharT('\\'))
{
return __quoted(__s, __delim, __escape);
}

template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_INLINE_VISIBILITY
__quoted_proxy<_CharT, _Traits, _Allocator>
quoted ( basic_string <_CharT, _Traits, _Allocator> &__s, _CharT __delim = _CharT('"'), _CharT __escape=_CharT('\\'))
{
return __quoted(__s, __delim, __escape);
}
#endif

_LIBCPP_END_NAMESPACE_STD
Expand Down
5 changes: 4 additions & 1 deletion libcxx/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ set_target_properties(cxx

if (LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY)
file(GLOB LIBCXX_EXPERIMENTAL_SOURCES ../src/experimental/*.cpp)
add_library(cxx_experimental STATIC ${LIBCXX_EXPERIMENTAL_SOURCES})
if (LIBCXX_ENABLE_FILESYSTEM)
file(GLOB LIBCXX_FILESYSTEM_SOURCES ../src/experimental/filesystem/*.cpp)
endif()
add_library(cxx_experimental STATIC ${LIBCXX_EXPERIMENTAL_SOURCES} ${LIBCXX_FILESYSTEM_SOURCES})
target_link_libraries(cxx_experimental cxx)

set(experimental_flags "${LIBCXX_COMPILE_FLAGS}")
Expand Down
256 changes: 256 additions & 0 deletions libcxx/src/experimental/filesystem/directory_iterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
#include "experimental/filesystem"
#include <dirent.h>
#include <errno.h>

_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM

namespace { namespace detail {

inline error_code capture_errno() {
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
return error_code{errno, std::generic_category()};
}

template <class ...Args>
inline bool capture_error_or_throw(std::error_code* user_ec,
const char* msg, Args&&... args)
{
std::error_code my_ec = capture_errno();
if (user_ec) {
*user_ec = my_ec;
return true;
}
__libcpp_throw(filesystem_error(msg, std::forward<Args>(args)..., my_ec));
return false;
}

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;
}
__libcpp_throw(filesystem_error(msg, std::forward<Args>(args)..., my_ec));
return false;
}

typedef path::string_type string_type;


inline string_type posix_readdir(DIR *dir_stream, error_code& ec) {
struct dirent* dir_entry_ptr = nullptr;
errno = 0; // zero errno in order to detect errors
if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
ec = capture_errno();
return {};
} else {
ec.clear();
return dir_entry_ptr->d_name;
}
}

}} // namespace detail

using detail::set_or_throw;

class __dir_stream {
public:
__dir_stream() = delete;
__dir_stream& operator=(const __dir_stream&) = delete;

__dir_stream(__dir_stream&& other) noexcept
: __stream_(other.__stream_), __root_(std::move(other.__root_)),
__entry_(std::move(other.__entry_))
{
other.__stream_ = nullptr;
}


__dir_stream(const path& root, directory_options opts, error_code& ec)
: __stream_(nullptr),
__root_(root)
{
if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
ec = detail::capture_errno();
const bool allow_eacess =
bool(opts & directory_options::skip_permission_denied);
if (allow_eacess && ec.value() == EACCES)
ec.clear();
return;
}
advance(ec);
}

~__dir_stream() noexcept
{ if (__stream_) close(); }

bool good() const noexcept { return __stream_ != nullptr; }

bool advance(error_code &ec) {
while (true) {
auto str = detail::posix_readdir(__stream_, ec);
if (str == "." || str == "..") {
continue;
} else if (ec || str.empty()) {
close();
return false;
} else {
__entry_.assign(__root_ / str);
return true;
}
}
}
private:
std::error_code close() noexcept {
std::error_code m_ec;
if (::closedir(__stream_) == -1)
m_ec = detail::capture_errno();
__stream_ = nullptr;
return m_ec;
}

DIR * __stream_{nullptr};
public:
path __root_;
directory_entry __entry_;
};

// directory_iterator

directory_iterator::directory_iterator(const path& p, error_code *ec,
directory_options opts)
{
std::error_code m_ec;
__imp_ = make_shared<__dir_stream>(p, opts, m_ec);
if (ec) *ec = m_ec;
if (!__imp_->good()) {
__imp_.reset();
if (m_ec)
set_or_throw(m_ec, ec,
"directory_iterator::directory_iterator(...)", p);
}
}

directory_iterator& directory_iterator::__increment(error_code *ec)
{
_LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
std::error_code m_ec;
if (!__imp_->advance(m_ec)) {
__imp_.reset();
if (m_ec)
set_or_throw(m_ec, ec, "directory_iterator::operator++()");
} else {
if (ec) ec->clear();
}
return *this;

}

directory_entry const& directory_iterator::__deref() const {
_LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
return __imp_->__entry_;
}

// recursive_directory_iterator

struct recursive_directory_iterator::__shared_imp {
stack<__dir_stream> __stack_;
directory_options __options_;
};

recursive_directory_iterator::recursive_directory_iterator(const path& p,
directory_options opt, error_code *ec)
: __imp_(nullptr), __rec_(true)
{
std::error_code m_ec;
__dir_stream new_s(p, opt, m_ec);
if (m_ec) set_or_throw(m_ec, ec, "recursive_directory_iterator", p);
if (m_ec || !new_s.good()) return;

__imp_ = _VSTD::make_shared<__shared_imp>();
__imp_->__options_ = opt;
__imp_->__stack_.push(_VSTD::move(new_s));
}

void recursive_directory_iterator::__pop(error_code* ec)
{
_LIBCPP_ASSERT(__imp_, "Popping the end iterator");
__imp_->__stack_.pop();
if (__imp_->__stack_.size() == 0) {
__imp_.reset();
if (ec) ec->clear();
} else {
__advance(ec);
}
}

directory_options recursive_directory_iterator::options() const {
return __imp_->__options_;
}

int recursive_directory_iterator::depth() const {
return __imp_->__stack_.size() - 1;
}

const directory_entry& recursive_directory_iterator::__deref() const {
return __imp_->__stack_.top().__entry_;
}

recursive_directory_iterator&
recursive_directory_iterator::__increment(error_code *ec)
{
if (recursion_pending()) {
if (__try_recursion(ec) || (ec && *ec))
return *this;
}
__rec_ = true;
__advance(ec);
return *this;
}

void recursive_directory_iterator::__advance(error_code* ec) {
const directory_iterator end_it;
auto& stack = __imp_->__stack_;
std::error_code m_ec;
while (stack.size() > 0) {
if (stack.top().advance(m_ec)) {
if (ec) ec->clear();
return;
}
if (m_ec) break;
stack.pop();
}
__imp_.reset();
if (m_ec)
set_or_throw(m_ec, ec, "recursive_directory_iterator::operator++()");
}

bool recursive_directory_iterator::__try_recursion(error_code *ec) {

bool rec_sym =
bool(options() & directory_options::follow_directory_symlink);
auto& curr_it = __imp_->__stack_.top();

if (is_directory(curr_it.__entry_.status()) &&
(!is_symlink(curr_it.__entry_.symlink_status()) || rec_sym))
{
std::error_code m_ec;
__dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
if (new_it.good()) {
__imp_->__stack_.push(_VSTD::move(new_it));
return true;
}
if (m_ec) {
__imp_.reset();
set_or_throw(m_ec, ec,
"recursive_directory_iterator::operator++()");
}
}
return false;
}


_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
754 changes: 754 additions & 0 deletions libcxx/src/experimental/filesystem/operations.cpp

Large diffs are not rendered by default.

392 changes: 392 additions & 0 deletions libcxx/src/experimental/filesystem/path.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,392 @@
//===--------------------- filesystem/path.cpp ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "experimental/filesystem"
#include "experimental/string_view"
#include "utility"

_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM

_LIBCPP_CONSTEXPR path::value_type path::preferred_separator;


namespace { namespace parser
{

using string_type = string_view;
using value_type = path::value_type;

using string_view_pair = pair<string_view, string_view>;

// status reporting
constexpr size_t npos = static_cast<size_t>(-1);

inline bool good(size_t pos) { return pos != npos; }

// lexical elements
constexpr value_type preferred_separator = path::preferred_separator;
constexpr value_type const * preferred_separator_str = "/";
constexpr value_type const * dot = ".";

// forward //
bool is_separator(string_type const &, size_t);
bool is_root_name(const string_type&, size_t);
bool is_root_directory(string_type const &, size_t);
bool is_trailing_separator(string_type const &, size_t);

size_t start_of(string_type const &, size_t);
size_t end_of(string_type const &, size_t);

size_t root_name_start(const string_type& s);
size_t root_name_end(const string_type&);

size_t root_directory_start(string_type const &);
size_t root_directory_end(string_type const &);

string_view_pair separate_filename(string_type const &);
string_view extract_raw(string_type const &, size_t);
string_view extract_preferred(string_type const &, size_t);

inline bool is_separator(const string_type& s, size_t pos) {
return (pos < s.size() && s[pos] == preferred_separator);
}

inline bool is_root_name(const string_type& s, size_t pos) {
return good(pos) && pos == 0 ? root_name_start(s) == pos : false;
}

inline bool is_root_directory(const string_type& s, size_t pos) {
return good(pos) ? root_directory_start(s) == pos : false;
}

inline bool is_trailing_separator(const string_type& s, size_t pos) {
return (pos < s.size() && is_separator(s, pos) &&
end_of(s, pos) == s.size()-1 &&
!is_root_directory(s, pos) && !is_root_name(s, pos));
}

size_t start_of(const string_type& s, size_t pos) {
if (pos >= s.size()) return npos;
bool in_sep = (s[pos] == preferred_separator);
while (pos - 1 < s.size() &&
(s[pos-1] == preferred_separator) == in_sep)
{ --pos; }
if (pos == 2 && !in_sep && s[0] == preferred_separator &&
s[1] == preferred_separator)
{ return 0; }
return pos;
}

size_t end_of(const string_type& s, size_t pos) {
if (pos >= s.size()) return npos;
// special case for root name
if (pos == 0 && is_root_name(s, pos)) return root_name_end(s);
bool in_sep = (s[pos] == preferred_separator);
while (pos + 1 < s.size() && (s[pos+1] == preferred_separator) == in_sep)
{ ++pos; }
return pos;
}

inline size_t root_name_start(const string_type& s) {
return good(root_name_end(s)) ? 0 : npos;
}

size_t root_name_end(const string_type& s) {
if (s.size() < 2 || s[0] != preferred_separator
|| s[1] != preferred_separator) {
return npos;
}
if (s.size() == 2) {
return 1;
}
size_t index = 2; // current position
if (s[index] == preferred_separator) {
return npos;
}
while (index + 1 < s.size() && s[index+1] != preferred_separator) {
++index;
}
return index;
}

size_t root_directory_start(const string_type& s) {
size_t e = root_name_end(s);
if (!good(e))
return is_separator(s, 0) ? 0 : npos;
return is_separator(s, e + 1) ? e + 1 : npos;
}

size_t root_directory_end(const string_type& s) {
size_t st = root_directory_start(s);
if (!good(st)) return npos;
size_t index = st;
while (index + 1 < s.size() && s[index + 1] == preferred_separator)
{ ++index; }
return index;
}

string_view_pair separate_filename(string_type const & s) {
if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
auto pos = s.find_last_of('.');
if (pos == string_type::npos) return string_view_pair{s, string_view{}};
return string_view_pair{s.substr(0, pos), s.substr(pos)};
}

inline string_view extract_raw(const string_type& s, size_t pos) {
size_t end_i = end_of(s, pos);
if (!good(end_i)) return string_view{};
return string_view(s).substr(pos, end_i - pos + 1);
}

string_view extract_preferred(const string_type& s, size_t pos) {
string_view raw = extract_raw(s, pos);
if (raw.empty())
return raw;
if (is_trailing_separator(s, pos))
return string_view{dot};
if (is_separator(s, pos) && !is_root_name(s, pos))
return string_view(preferred_separator_str);
return raw;
}

}} // namespace parser


////////////////////////////////////////////////////////////////////////////////
// path_view_iterator
////////////////////////////////////////////////////////////////////////////////
namespace {

struct path_view_iterator {
const string_view __s_;
size_t __pos_;

explicit path_view_iterator(string_view const& __s) : __s_(__s), __pos_(__s_.empty() ? parser::npos : 0) {}
explicit path_view_iterator(string_view const& __s, size_t __p) : __s_(__s), __pos_(__p) {}

string_view operator*() const {
return parser::extract_preferred(__s_, __pos_);
}

path_view_iterator& operator++() {
increment();
return *this;
}

path_view_iterator& operator--() {
decrement();
return *this;
}

void increment() {
if (__pos_ == parser::npos) return;
while (! set_position(parser::end_of(__s_, __pos_)+1))
;
return;
}

void decrement() {
if (__pos_ == 0) {
set_position(0);
}
else if (__pos_ == parser::npos) {
auto const str_size = __s_.size();
set_position(parser::start_of(
__s_, str_size != 0 ? str_size - 1 : str_size));
} else {
while (!set_position(parser::start_of(__s_, __pos_-1)))
;
}
}

bool set_position(size_t pos) {
if (pos >= __s_.size()) {
__pos_ = parser::npos;
} else {
__pos_ = pos;
}
return valid_iterator_position();
}

bool valid_iterator_position() const {
if (__pos_ == parser::npos) return true; // end position is valid
return (!parser::is_separator (__s_, __pos_) ||
parser::is_root_directory (__s_, __pos_) ||
parser::is_trailing_separator(__s_, __pos_) ||
parser::is_root_name (__s_, __pos_));
}

bool is_end() const { return __pos_ == parser::npos; }

inline bool operator==(path_view_iterator const& __p) {
return __pos_ == __p.__pos_;
}
};

path_view_iterator pbegin(path const& p) {
return path_view_iterator(p.native());
}

path_view_iterator pend(path const& p) {
path_view_iterator __p(p.native());
__p.__pos_ = parser::npos;
return __p;
}

} // end namespace
///////////////////////////////////////////////////////////////////////////////
// path definitions
///////////////////////////////////////////////////////////////////////////////

path & path::replace_extension(path const & replacement)
{
path p = extension();
if (not p.empty()) {
__pn_.erase(__pn_.size() - p.native().size());
}
if (!replacement.empty()) {
if (replacement.native()[0] != '.') {
__pn_ += ".";
}
__pn_.append(replacement.__pn_);
}
return *this;
}

///////////////////////////////////////////////////////////////////////////////
// path.decompose

string_view path::__root_name() const
{
return parser::is_root_name(__pn_, 0)
? parser::extract_preferred(__pn_, 0)
: string_view{};
}

string_view path::__root_directory() const
{
auto start_i = parser::root_directory_start(__pn_);
if(!parser::good(start_i)) {
return {};
}
return parser::extract_preferred(__pn_, start_i);
}

string_view path::__relative_path() const
{
if (empty()) {
return {__pn_};
}
auto end_i = parser::root_directory_end(__pn_);
if (not parser::good(end_i)) {
end_i = parser::root_name_end(__pn_);
}
if (not parser::good(end_i)) {
return {__pn_};
}
return string_view(__pn_).substr(end_i+1);
}

string_view path::__parent_path() const
{
if (empty() || pbegin(*this) == --pend(*this)) {
return {};
}
auto end_it = --(--pend(*this));
auto end_i = parser::end_of(__pn_, end_it.__pos_);
return string_view(__pn_).substr(0, end_i+1);
}

string_view path::__filename() const
{
return empty() ? string_view{} : *--pend(*this);
}

string_view path::__stem() const
{
return parser::separate_filename(__filename()).first;
}

string_view path::__extension() const
{
return parser::separate_filename(__filename()).second;
}

////////////////////////////////////////////////////////////////////////////
// path.comparisons
int path::__compare(const value_type* __s) const {
path_view_iterator thisIter(this->native());
path_view_iterator sIter(__s);
while (!thisIter.is_end() && !sIter.is_end()) {
int res = (*thisIter).compare(*sIter);
if (res != 0) return res;
++thisIter; ++sIter;
}
if (thisIter.is_end() && sIter.is_end())
return 0;
if (thisIter.is_end())
return -1;
return 1;
}

////////////////////////////////////////////////////////////////////////////
// path.nonmembers
size_t hash_value(const path& __p) _NOEXCEPT {
path_view_iterator thisIter(__p.native());
struct HashPairT {
size_t first;
size_t second;
};
HashPairT hp = {0, 0};
std::hash<string_view> hasher;
std::__scalar_hash<decltype(hp)> pair_hasher;
while (!thisIter.is_end()) {
hp.second = hasher(*thisIter);
hp.first = pair_hasher(hp);
++thisIter;
}
return hp.first;
}

////////////////////////////////////////////////////////////////////////////
// path.itr
path::iterator path::begin() const
{
path_view_iterator pit = pbegin(*this);
iterator it;
it.__path_ptr_ = this;
it.__pos_ = pit.__pos_;
it.__elem_.__assign_view(*pit);
return it;
}

path::iterator path::end() const
{
iterator it{};
it.__path_ptr_ = this;
it.__pos_ = parser::npos;
return it;
}

path::iterator& path::iterator::__increment() {
path_view_iterator it(__path_ptr_->native(), __pos_);
it.increment();
__pos_ = it.__pos_;
__elem_.__assign_view(*it);
return *this;
}

path::iterator& path::iterator::__decrement() {
path_view_iterator it(__path_ptr_->native(), __pos_);
it.decrement();
__pos_ = it.__pos_;
__elem_.__assign_view(*it);
return *this;
}

_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
1 change: 1 addition & 0 deletions libcxx/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(LIBCXX_LIT_VARIANT "libcxx" CACHE STRING

pythonize_bool(LIBCXX_ENABLE_EXCEPTIONS)
pythonize_bool(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY)
pythonize_bool(LIBCXX_ENABLE_FILESYSTEM)
pythonize_bool(LIBCXX_ENABLE_RTTI)
pythonize_bool(LIBCXX_ENABLE_SHARED)
pythonize_bool(LIBCXX_BUILD_32_BITS)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//===----------------------------------------------------------------------===//
//
// 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>

// template <class Tp> struct __is_pathable

// [path.req]
// In addition to the requirements (5), function template parameters named
// `Source` shall be one of:
// * basic_string<_ECharT, _Traits, _Alloc>
// * InputIterator with a value_type of _ECharT
// * A character array, which points to a NTCTS after array-to-pointer decay.


#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "test_macros.h"
#include "test_iterators.h"
#include "min_allocator.h"

namespace fs = std::experimental::filesystem;

using fs::__is_pathable;

template <class Tp>
struct Identity { typedef Tp type; };

template <class Source>
Identity<Source> CheckSourceType(Source const&);

template <class Tp>
using GetSourceType = typename decltype(CheckSourceType(std::declval<Tp>()))::type;

template <class Tp, class Exp,
class ExpQual = typename std::remove_const<Exp>::type>
using CheckPass = std::is_same<ExpQual, GetSourceType<Tp>>;

template <class Source>
using CheckPassSource = std::integral_constant<bool,
CheckPass<Source&, Source>::value &&
CheckPass<Source const&, Source>::value &&
CheckPass<Source&&, Source>::value &&
CheckPass<Source const&&, Source>::value
>;

template <class CharT>
struct MakeTestType {
using value_type = CharT;
using string_type = std::basic_string<CharT>;
using string_type2 = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>;
using cstr_type = CharT* const;
using const_cstr_type = const CharT*;
using array_type = CharT[25];
using const_array_type = const CharT[25];
using iter_type = input_iterator<CharT*>;
using bad_iter_type = input_iterator<signed char*>;

template <class TestT>
static void AssertPathable() {
static_assert(__is_pathable<TestT>::value, "");
static_assert(CheckPassSource<TestT>::value, "cannot pass as Source const&");
ASSERT_SAME_TYPE(CharT, typename __is_pathable<TestT>::__char_type);
}

template <class TestT>
static void AssertNotPathable() {
static_assert(!__is_pathable<TestT>::value, "");
}

static void Test() {
AssertPathable<string_type>();
AssertPathable<string_type2>();
AssertPathable<cstr_type>();
AssertPathable<const_cstr_type>();
AssertPathable<array_type>();
AssertPathable<const_array_type>();
AssertPathable<iter_type>();

AssertNotPathable<CharT>();
AssertNotPathable<bad_iter_type>();
AssertNotPathable<signed char*>();
}
};

int main() {
MakeTestType<char>::Test();
MakeTestType<wchar_t>::Test();
MakeTestType<char16_t>::Test();
MakeTestType<char32_t>::Test();
}
3 changes: 3 additions & 0 deletions libcxx/test/libcxx/experimental/filesystem/lit.local.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Disable all of the filesystem tests if the correct feature is not available.
if 'c++filesystem' not in config.available_features:
config.unsupported = True
22 changes: 22 additions & 0 deletions libcxx/test/libcxx/experimental/filesystem/version.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//===----------------------------------------------------------------------===//
//
// 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>

#include <experimental/filesystem>

#ifndef _LIBCPP_VERSION
#error _LIBCPP_VERSION not defined
#endif

int main()
{
}
30 changes: 30 additions & 0 deletions libcxx/test/libcxx/test/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def configure(self):
self.configure_execute_external()
self.configure_ccache()
self.configure_compile_flags()
self.configure_filesystem_compile_flags()
self.configure_link_flags()
self.configure_env()
self.configure_color_diagnostics()
Expand Down Expand Up @@ -427,6 +428,35 @@ def configure_compile_flags_abi_version(self):
self.config.available_features.add('libcpp-abi-unstable')
self.cxx.compile_flags += ['-D_LIBCPP_ABI_UNSTABLE']

def configure_filesystem_compile_flags(self):
enable_fs = self.get_lit_bool('enable_filesystem', default=False)
if not enable_fs:
return
enable_experimental = self.get_lit_bool('enable_experimental', default=False)
if not enable_experimental:
self.lit_config.fatal(
'filesystem is enabled but libc++experimental.a is not.')
self.config.available_features.add('c++filesystem')
static_env = os.path.join(self.libcxx_src_root, 'test', 'std',
'experimental', 'filesystem', 'Inputs', 'static_test_env')
assert os.path.isdir(static_env)
self.cxx.compile_flags += ['-DLIBCXX_FILESYSTEM_STATIC_TEST_ROOT="%s"' % static_env]

dynamic_env = os.path.join(self.libcxx_obj_root, 'test',
'filesystem', 'Output', 'dynamic_env')
if not os.path.isdir(dynamic_env):
os.makedirs(dynamic_env)
self.cxx.compile_flags += ['-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT="%s"' % dynamic_env]
self.env['LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT'] = ("%s" % dynamic_env)

dynamic_helper = os.path.join(self.libcxx_src_root, 'test', 'support',
'filesystem_dynamic_test_helper.py')
assert os.path.isfile(dynamic_helper)

self.cxx.compile_flags += ['-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER="%s %s"'
% (sys.executable, dynamic_helper)]


def configure_link_flags(self):
no_default_flags = self.get_lit_bool('no_default_flags', False)
if not no_default_flags:
Expand Down
1 change: 1 addition & 0 deletions libcxx/test/lit.site.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ config.libcxx_obj_root = "@LIBCXX_BINARY_DIR@"
config.cxx_library_root = "@LIBCXX_LIBRARY_DIR@"
config.enable_exceptions = "@LIBCXX_ENABLE_EXCEPTIONS@"
config.enable_experimental = "@LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY@"
config.enable_filesystem = "@LIBCXX_ENABLE_FILESYSTEM@"
config.enable_rtti = "@LIBCXX_ENABLE_RTTI@"
config.enable_shared = "@LIBCXX_ENABLE_SHARED@"
config.enable_32bit = "@LIBCXX_BUILD_32_BITS@"
Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//===----------------------------------------------------------------------===//
//
// 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 <experimental/filesystem>
#include <type_traits>
#include <cassert>

namespace fs = std::experimental::filesystem;

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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//===----------------------------------------------------------------------===//
//
// 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 <experimental/filesystem>
#include <type_traits>
#include <cassert>

namespace fs = std::experimental::filesystem;

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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// class directory_entry

// bool operator==(directory_entry const&) const noexcept;
// bool operator!=(directory_entry const&) const noexcept;
// bool operator< (directory_entry const&) const noexcept;
// bool operator<=(directory_entry const&) const noexcept;
// bool operator> (directory_entry const&) const noexcept;
// bool operator>=(directory_entry const&) const noexcept;


#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

namespace fs = std::experimental::filesystem;

#define CHECK_OP(Op) \
static_assert(std::is_same<decltype(ce. operator Op (ce)), bool>::value, ""); \
static_assert(noexcept(ce.operator Op (ce)), "Operation must be noexcept" )

void test_comparison_signatures() {
using namespace fs;
path const p("foo/bar/baz");
// Check that the operators are member functions with the correct signatures.
{
directory_entry const ce(p);
CHECK_OP(==);
CHECK_OP(!=);
CHECK_OP(< );
CHECK_OP(<=);
CHECK_OP(> );
CHECK_OP(>=);
}
}
#undef CHECK_OP

// The actual semantics of the comparisons are testing via paths operators.
void test_comparisons_simple() {
using namespace fs;
typedef std::pair<path, path> TestType;
TestType TestCases[] =
{
{"", ""},
{"", "a"},
{"a", "a"},
{"a", "b"},
{"foo/bar/baz", "foo/bar/baz/"}
};
auto TestFn = [](path const& LHS, const directory_entry& LHSE,
path const& RHS, const directory_entry& RHSE) {
assert((LHS == RHS) == (LHSE == RHSE));
assert((LHS != RHS) == (LHSE != RHSE));
assert((LHS < RHS) == (LHSE < RHSE));
assert((LHS <= RHS) == (LHSE <= RHSE));
assert((LHS > RHS) == (LHSE > RHSE));
assert((LHS >= RHS) == (LHSE >= RHSE));
};
for (auto const& TC : TestCases) {
const directory_entry L(TC.first);
const directory_entry R(TC.second);
TestFn(TC.first, L, TC.second, R);
TestFn(TC.second, R, TC.first, L);
}
}

int main() {
test_comparison_signatures();
test_comparisons_simple();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// class directory_entry

// const path& path() const noexcept;
// operator const path&() const noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

namespace fs = std::experimental::filesystem;

void test_path_method() {
using namespace fs;
const path p("foo/bar/baz.exe");
const path p2("abc");
{
directory_entry nce;
const directory_entry e("");
static_assert(std::is_same<decltype(e.path()), const path&>::value, "");
static_assert(std::is_same<decltype(nce.path()), const path&>::value, "");
static_assert(noexcept(e.path()) && noexcept(nce.path()), "");
}
{
directory_entry e(p);
path const& pref = e.path();
assert(pref == p);
assert(&pref == &e.path());
e.assign(p2);
assert(pref == p2);
assert(&pref == &e.path());
}
}

void test_path_conversion() {
using namespace fs;
const path p("foo/bar/baz.exe");
const path p2("abc");
{
directory_entry nce;
const directory_entry e("");
// Check conversions exist
static_assert(std::is_convertible<directory_entry&, path const&>::value, "");
static_assert(std::is_convertible<directory_entry const&, path const&>::value, "");
static_assert(std::is_convertible<directory_entry &&, path const&>::value, "");
static_assert(std::is_convertible<directory_entry const&&, path const&>::value, "");
// Not convertible to non-const
static_assert(!std::is_convertible<directory_entry&, path&>::value, "");
static_assert(!std::is_convertible<directory_entry const&, path&>::value, "");
static_assert(!std::is_convertible<directory_entry &&, path&>::value, "");
static_assert(!std::is_convertible<directory_entry const&&, path&>::value, "");
// conversions are noexcept
static_assert(noexcept(e.operator fs::path const&()) &&
noexcept(e.operator fs::path const&()), "");
}
// const
{
directory_entry const e(p);
path const& pref = e;
assert(&pref == &e.path());
}
// non-const
{
directory_entry e(p);
path const& pref = e;
assert(&pref == &e.path());

e.assign(p2);
assert(pref == p2);
assert(&pref == &e.path());
}
}

int main() {
test_path_method();
test_path_conversion();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// 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 <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "filesystem_test_helper.hpp"

int main()
{
using namespace fs;
{
const 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(noexcept(e.status()) == false, "");
static_assert(noexcept(e.status(ec)) == true, "");
}
auto TestFn = [](path const& p) {
const directory_entry e(p);
std::error_code pec, eec;
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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// 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 symlink_status() const;
// file_status symlink_status(error_code&) const noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "filesystem_test_helper.hpp"

int main() {
using namespace fs;
{
const directory_entry e("foo");
std::error_code ec;
static_assert(std::is_same<decltype(e.symlink_status()), file_status>::value, "");
static_assert(std::is_same<decltype(e.symlink_status(ec)), file_status>::value, "");
static_assert(noexcept(e.symlink_status()) == false, "");
static_assert(noexcept(e.symlink_status(ec)) == true, "");
}
auto TestFn = [](path const& p) {
const directory_entry e(p);
std::error_code pec, eec;
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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// directory_iterator(directory_iterator const&);

#include <experimental/filesystem>
#include <type_traits>
#include <set>
#include <cassert>

#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"

using namespace std::experimental::filesystem;

TEST_SUITE(directory_iterator_copy_construct_tests)

TEST_CASE(test_constructor_signature)
{
using D = directory_iterator;
static_assert(std::is_copy_constructible<D>::value, "");
}

TEST_CASE(test_copy_end_iterator)
{
const directory_iterator endIt;
directory_iterator it(endIt);
TEST_CHECK(it == endIt);
}

TEST_CASE(test_copy_valid_iterator)
{
const path testDir = StaticEnv::Dir;
const directory_iterator endIt{};

const directory_iterator it(testDir);
TEST_REQUIRE(it != endIt);
const path entry = *it;

const directory_iterator it2(it);
TEST_REQUIRE(it2 == it);
TEST_CHECK(*it2 == entry);
TEST_CHECK(*it == entry);
}

TEST_SUITE_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// directory_iterator& operator=(directory_iterator const&);

#include <experimental/filesystem>
#include <type_traits>
#include <set>
#include <cassert>

#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"

using namespace std::experimental::filesystem;

TEST_SUITE(directory_iterator_copy_assign_tests)

TEST_CASE(test_assignment_signature)
{
using D = directory_iterator;
static_assert(std::is_copy_assignable<D>::value, "");
}

TEST_CASE(test_copy_to_end_iterator)
{
const path testDir = StaticEnv::Dir;

const directory_iterator from(testDir);
TEST_REQUIRE(from != directory_iterator{});
const path entry = *from;

directory_iterator to{};
to = from;
TEST_REQUIRE(to == from);
TEST_CHECK(*to == entry);
TEST_CHECK(*from == entry);
}


TEST_CASE(test_copy_from_end_iterator)
{
const path testDir = StaticEnv::Dir;

const directory_iterator from{};

directory_iterator to(testDir);
TEST_REQUIRE(to != directory_iterator{});

to = from;
TEST_REQUIRE(to == from);
TEST_CHECK(to == directory_iterator{});
}

TEST_CASE(test_copy_valid_iterator)
{
const path testDir = StaticEnv::Dir;
const directory_iterator endIt{};

directory_iterator it_obj(testDir);
const directory_iterator& it = it_obj;
TEST_REQUIRE(it != endIt);
++it_obj;
TEST_REQUIRE(it != endIt);
const path entry = *it;

directory_iterator it2(testDir);
TEST_REQUIRE(it2 != it);
const path entry2 = *it2;
TEST_CHECK(entry2 != entry);

it2 = it;
TEST_REQUIRE(it2 == it);
TEST_CHECK(*it2 == entry);
}

TEST_CASE(test_returns_reference_to_self)
{
const directory_iterator it;
directory_iterator it2;
directory_iterator& ref = (it2 = it);
TEST_CHECK(&ref == &it2);
}


TEST_SUITE_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// explicit directory_iterator(const path& p);
// directory_iterator(const path& p, directory_options options);
// directory_iterator(const path& p, error_code& ec) noexcept;
// directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <set>
#include <cassert>

#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"

using namespace std::experimental::filesystem;

TEST_SUITE(directory_iterator_constructor_tests)

TEST_CASE(test_constructor_signatures)
{
using D = directory_iterator;

// explicit directory_iterator(path const&);
static_assert(!std::is_convertible<path, D>::value, "");
static_assert(std::is_constructible<D, path>::value, "");
static_assert(!std::is_nothrow_constructible<D, path>::value, "");

// directory_iterator(path const&, error_code&) noexcept
static_assert(std::is_nothrow_constructible<D, path, std::error_code&>::value, "");

// directory_iterator(path const&, directory_options);
static_assert(std::is_constructible<D, path, directory_options>::value, "");
static_assert(!std::is_nothrow_constructible<D, path, directory_options>::value, "");

// directory_iterator(path const&, directory_options, error_code&) noexcept
static_assert(std::is_nothrow_constructible<D, path, directory_options, std::error_code&>::value, "");
}

TEST_CASE(test_construction_from_bad_path)
{
std::error_code ec;
directory_options opts = directory_options::none;
const directory_iterator endIt;

const path testPaths[] = { StaticEnv::DNE, StaticEnv::BadSymlink };
for (path const& testPath : testPaths)
{
{
directory_iterator it(testPath, ec);
TEST_CHECK(ec);
TEST_CHECK(it == endIt);
}
{
directory_iterator it(testPath, opts, ec);
TEST_CHECK(ec);
TEST_CHECK(it == endIt);
}
{
TEST_CHECK_THROW(filesystem_error, directory_iterator(testPath));
TEST_CHECK_THROW(filesystem_error, directory_iterator(testPath, opts));
}
}
}

TEST_CASE(access_denied_test_case)
{
using namespace std::experimental::filesystem;
scoped_test_env env;
path const testDir = env.make_env_path("dir1");
path const testFile = testDir / "testFile";
env.create_dir(testDir);
env.create_file(testFile, 42);

// Test that we can iterator over the directory before changing the perms
directory_iterator it(testDir);
TEST_REQUIRE(it != directory_iterator{});

// Change the permissions so we can no longer iterate
permissions(testDir, perms::none);

// Check that the construction fails when skip_permissions_denied is
// not given.
{
std::error_code ec;
directory_iterator it(testDir, ec);
TEST_REQUIRE(ec);
TEST_CHECK(it == directory_iterator{});
}
// Check that construction does not report an error when
// 'skip_permissions_denied' is given.
{
std::error_code ec;
directory_iterator it(testDir, directory_options::skip_permission_denied, ec);
TEST_REQUIRE(!ec);
TEST_CHECK(it == directory_iterator{});
}
}


TEST_CASE(access_denied_to_file_test_case)
{
using namespace std::experimental::filesystem;
scoped_test_env env;
path const testFile = env.make_env_path("file1");
env.create_file(testFile, 42);

// Change the permissions so we can no longer iterate
permissions(testFile, perms::none);

// Check that the construction fails when skip_permissions_denied is
// not given.
{
std::error_code ec;
directory_iterator it(testFile, ec);
TEST_REQUIRE(ec);
TEST_CHECK(it == directory_iterator{});
}
// Check that construction still fails when 'skip_permissions_denied' is given
// because we tried to open a file and not a directory.
{
std::error_code ec;
directory_iterator it(testFile, directory_options::skip_permission_denied, ec);
TEST_REQUIRE(ec);
TEST_CHECK(it == directory_iterator{});
}
}

TEST_CASE(test_open_on_empty_directory_equals_end)
{
scoped_test_env env;
const path testDir = env.make_env_path("dir1");
env.create_dir(testDir);

const directory_iterator endIt;
{
std::error_code ec;
directory_iterator it(testDir, ec);
TEST_CHECK(!ec);
TEST_CHECK(it == endIt);
}
{
directory_iterator it(testDir);
TEST_CHECK(it == endIt);
}
}

TEST_CASE(test_open_on_directory_succeeds)
{
const path testDir = StaticEnv::Dir;
std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList),
std::end( StaticEnv::DirIterationList));
const directory_iterator endIt{};

{
std::error_code ec;
directory_iterator it(testDir, ec);
TEST_REQUIRE(!ec);
TEST_CHECK(it != endIt);
TEST_CHECK(dir_contents.count(*it));
}
{
directory_iterator it(testDir);
TEST_CHECK(it != endIt);
TEST_CHECK(dir_contents.count(*it));
}
}

TEST_CASE(test_open_on_file_fails)
{
const path testFile = StaticEnv::File;
const directory_iterator endIt{};
{
std::error_code ec;
directory_iterator it(testFile, ec);
TEST_REQUIRE(ec);
TEST_CHECK(it == endIt);
}
{
TEST_CHECK_THROW(filesystem_error, directory_iterator(testFile));
}
}

TEST_CASE(test_open_on_empty_string)
{
const path testPath = "";
const directory_iterator endIt{};

std::error_code ec;
directory_iterator it(testPath, ec);
TEST_CHECK(ec);
TEST_CHECK(it == endIt);
}

TEST_CASE(test_open_on_dot_dir)
{
const path testPath = ".";

std::error_code ec;
directory_iterator it(testPath, ec);
TEST_CHECK(!ec);
}

TEST_CASE(test_open_on_symlink)
{
const path symlinkToDir = StaticEnv::SymlinkToDir;
std::set<path> dir_contents;
for (path const& p : StaticEnv::DirIterationList) {
dir_contents.insert(p.filename());
}
const directory_iterator endIt{};

{
std::error_code ec;
directory_iterator it(symlinkToDir, ec);
TEST_REQUIRE(!ec);
TEST_CHECK(it != endIt);
path const& entry = *it;
TEST_CHECK(dir_contents.count(entry.filename()));
}
{
std::error_code ec;
directory_iterator it(symlinkToDir,
directory_options::follow_directory_symlink, ec);
TEST_REQUIRE(!ec);
TEST_CHECK(it != endIt);
path const& entry = *it;
TEST_CHECK(dir_contents.count(entry.filename()));
}
}

TEST_SUITE_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// directory_iterator::directory_iterator() noexcept


#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "test_macros.h"

namespace fs = std::experimental::filesystem;

int main() {
{
static_assert(std::is_nothrow_default_constructible<fs::directory_iterator>::value, "");
}
{
fs::directory_iterator d1;
const fs::directory_iterator d2;
assert(d1 == d2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// directory_iterator& operator++();
// directory_iterator& increment(error_code& ec) noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <set>
#include <cassert>

#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
#include <iostream>

using namespace std::experimental::filesystem;

TEST_SUITE(directory_iterator_increment_tests)

TEST_CASE(test_increment_signatures)
{
using D = directory_iterator;
directory_iterator d; ((void)d);
std::error_code ec; ((void)ec);

ASSERT_SAME_TYPE(decltype(++d), directory_iterator&);
ASSERT_NOT_NOEXCEPT(++d);

ASSERT_SAME_TYPE(decltype(d.increment(ec)), directory_iterator&);
ASSERT_NOEXCEPT(d.increment(ec));
}

TEST_CASE(test_prefix_increment)
{
const path testDir = StaticEnv::Dir;
const std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList),
std::end( StaticEnv::DirIterationList));
const directory_iterator endIt{};

std::error_code ec;
directory_iterator it(testDir, ec);
TEST_REQUIRE(!ec);

std::set<path> unseen_entries = dir_contents;
while (!unseen_entries.empty()) {
TEST_REQUIRE(it != endIt);
const path entry = *it;
TEST_REQUIRE(unseen_entries.erase(entry) == 1);
directory_iterator& it_ref = ++it;
TEST_CHECK(&it_ref == &it);
}

TEST_CHECK(it == endIt);
}

TEST_CASE(test_postfix_increment)
{
const path testDir = StaticEnv::Dir;
const std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList),
std::end( StaticEnv::DirIterationList));
const directory_iterator endIt{};

std::error_code ec;
directory_iterator it(testDir, ec);
TEST_REQUIRE(!ec);

std::set<path> unseen_entries = dir_contents;
while (!unseen_entries.empty()) {
TEST_REQUIRE(it != endIt);
const path entry = *it;
TEST_REQUIRE(unseen_entries.erase(entry) == 1);
const path entry2 = *it++;
TEST_CHECK(entry2 == entry);
}

TEST_CHECK(it == endIt);
}


TEST_CASE(test_increment_method)
{
const path testDir = StaticEnv::Dir;
const std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList),
std::end( StaticEnv::DirIterationList));
const directory_iterator endIt{};

std::error_code ec;
directory_iterator it(testDir, ec);
TEST_REQUIRE(!ec);

std::set<path> unseen_entries = dir_contents;
while (!unseen_entries.empty()) {
TEST_REQUIRE(it != endIt);
const path entry = *it;
TEST_REQUIRE(unseen_entries.erase(entry) == 1);
directory_iterator& it_ref = it.increment(ec);
TEST_REQUIRE(!ec);
TEST_CHECK(&it_ref == &it);
}

TEST_CHECK(it == endIt);
}

TEST_SUITE_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// directory_iterator(directory_iterator&&) noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <set>
#include <cassert>

#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"

using namespace std::experimental::filesystem;

TEST_SUITE(directory_iterator_move_construct_tests)

TEST_CASE(test_constructor_signature)
{
using D = directory_iterator;
static_assert(std::is_nothrow_move_constructible<D>::value, "");
}

TEST_CASE(test_move_end_iterator)
{
const directory_iterator endIt;
directory_iterator endIt2{};

directory_iterator it(std::move(endIt2));
TEST_CHECK(it == endIt);
TEST_CHECK(endIt2 == endIt);
}

TEST_CASE(test_move_valid_iterator)
{
const path testDir = StaticEnv::Dir;
const directory_iterator endIt{};

directory_iterator it(testDir);
TEST_REQUIRE(it != endIt);
const path entry = *it;

const directory_iterator it2(std::move(it));
TEST_CHECK(*it2 == entry);

TEST_CHECK(it == it2 || it == endIt);
}

TEST_SUITE_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// directory_iterator& operator=(directory_iterator const&);

#include <experimental/filesystem>
#include <type_traits>
#include <set>
#include <cassert>

#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"

using namespace std::experimental::filesystem;

TEST_SUITE(directory_iterator_move_assign_tests)

TEST_CASE(test_assignment_signature)
{
using D = directory_iterator;
static_assert(std::is_nothrow_move_assignable<D>::value, "");
}

TEST_CASE(test_move_to_end_iterator)
{
const path testDir = StaticEnv::Dir;

directory_iterator from(testDir);
TEST_REQUIRE(from != directory_iterator{});
const path entry = *from;

directory_iterator to{};
to = std::move(from);
TEST_REQUIRE(to != directory_iterator{});
TEST_CHECK(*to == entry);
}


TEST_CASE(test_move_from_end_iterator)
{
const path testDir = StaticEnv::Dir;

directory_iterator from{};

directory_iterator to(testDir);
TEST_REQUIRE(to != from);

to = std::move(from);
TEST_REQUIRE(to == directory_iterator{});
TEST_REQUIRE(from == directory_iterator{});
}

TEST_CASE(test_move_valid_iterator)
{
const path testDir = StaticEnv::Dir;
const directory_iterator endIt{};

directory_iterator it(testDir);
TEST_REQUIRE(it != endIt);
++it;
TEST_REQUIRE(it != endIt);
const path entry = *it;

directory_iterator it2(testDir);
TEST_REQUIRE(it2 != it);
const path entry2 = *it2;
TEST_CHECK(entry2 != entry);

it2 = std::move(it);
TEST_REQUIRE(it2 != directory_iterator{});
TEST_CHECK(*it2 == entry);
}

TEST_CASE(test_returns_reference_to_self)
{
directory_iterator it;
directory_iterator it2;
directory_iterator& ref = (it2 = it);
TEST_CHECK(&ref == &it2);
}


TEST_CASE(test_self_move)
{
// Create two non-equal iterators that have exactly the same state.
directory_iterator it(StaticEnv::Dir);
directory_iterator it2(StaticEnv::Dir);
++it; ++it2;
TEST_CHECK(it != it2);
TEST_CHECK(*it2 == *it);

it = std::move(it);
TEST_CHECK(*it2 == *it);
}


TEST_SUITE_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// directory_iterator begin(directory_iterator iter) noexcept;
// directory_iterator end(directory_iterator iter) noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <set>
#include <cassert>

#include "test_macros.h"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
#include <iostream>

using namespace std::experimental::filesystem;

TEST_SUITE(directory_iterator_begin_end_tests)

TEST_CASE(test_function_signatures)
{
using D = directory_iterator;
directory_iterator d; ((void)d);

ASSERT_SAME_TYPE(decltype(begin(d)), directory_iterator);
ASSERT_NOEXCEPT(begin(std::move(d)));

ASSERT_SAME_TYPE(decltype(end(d)), directory_iterator);
ASSERT_NOEXCEPT(end(std::move(d)));
}

TEST_CASE(test_ranged_for_loop)
{
const path testDir = StaticEnv::Dir;
std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList),
std::end( StaticEnv::DirIterationList));

std::error_code ec;
directory_iterator it(testDir, ec);
TEST_REQUIRE(!ec);

for (auto& elem : it) {
TEST_CHECK(dir_contents.erase(elem) == 1);
}
TEST_CHECK(dir_contents.empty());
}

TEST_SUITE_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// 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_iterator

// typedef ... value_type;
// typedef ... difference_type;
// typedef ... pointer;
// typedef ... reference;
// typedef ... iterator_category

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "test_macros.h"

namespace fs = std::experimental::filesystem;

int main() {
using namespace fs;
using D = directory_iterator;
ASSERT_SAME_TYPE(D::value_type, directory_entry);
ASSERT_SAME_TYPE(D::difference_type, std::ptrdiff_t);
ASSERT_SAME_TYPE(D::pointer, const directory_entry*);
ASSERT_SAME_TYPE(D::reference, const directory_entry&);
ASSERT_SAME_TYPE(D::iterator_category, std::input_iterator_tag);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//===----------------------------------------------------------------------===//
//
// 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 file_status

// explicit file_status() noexcept;
// explicit file_status(file_type, perms prms = perms::unknown) noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "test_convertible.hpp"

namespace fs = std::experimental::filesystem;

int main() {
using namespace fs;
// Default ctor
{
static_assert(std::is_nothrow_default_constructible<file_status>::value,
"The default constructor must be noexcept");
static_assert(!test_convertible<file_status>(),
"The default constructor must be explicit");
const file_status f;
assert(f.type() == file_type::none);
assert(f.permissions() == perms::unknown);
}

// Unary ctor
{
static_assert(std::is_nothrow_constructible<file_status, file_type>::value,
"This constructor must be noexcept");
static_assert(!test_convertible<file_status, file_type>(),
"This constructor must be explicit");

const file_status f(file_type::not_found);
assert(f.type() == file_type::not_found);
assert(f.permissions() == perms::unknown);
}
// Binary ctor
{
static_assert(std::is_nothrow_constructible<file_status, file_type, perms>::value,
"This constructor must be noexcept");
static_assert(!test_convertible<file_status, file_type, perms>(),
"This constructor must b explicit");
const file_status f(file_type::regular, perms::owner_read);
assert(f.type() == file_type::regular);
assert(f.permissions() == perms::owner_read);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// 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 file_status

// void type(file_type) noexcept;
// void permissions(perms) noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

namespace fs = std::experimental::filesystem;

int main() {
using namespace fs;

file_status st;

// type test
{
static_assert(noexcept(st.type(file_type::regular)),
"operation must be noexcept");
static_assert(std::is_same<decltype(st.type(file_type::regular)), void>::value,
"operation must return void");
assert(st.type() != file_type::regular);
st.type(file_type::regular);
assert(st.type() == file_type::regular);
}
// permissions test
{
static_assert(noexcept(st.permissions(perms::owner_read)),
"operation must be noexcept");
static_assert(std::is_same<decltype(st.permissions(perms::owner_read)), void>::value,
"operation must return void");
assert(st.permissions() != perms::owner_read);
st.permissions(perms::owner_read);
assert(st.permissions() == perms::owner_read);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===----------------------------------------------------------------------===//
//
// 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 file_status

// file_type type() const noexcept;
// perms permissions(p) const noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

namespace fs = std::experimental::filesystem;

int main() {
using namespace fs;

const file_status st(file_type::regular, perms::owner_read);

// type test
{
static_assert(noexcept(st.type()),
"operation must be noexcept");
static_assert(std::is_same<decltype(st.type()), file_type>::value,
"operation must return file_type");
assert(st.type() == file_type::regular);
}
// permissions test
{
static_assert(noexcept(st.permissions()),
"operation must be noexcept");
static_assert(std::is_same<decltype(st.permissions()), perms>::value,
"operation must return perms");
assert(st.permissions() == perms::owner_read);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//===----------------------------------------------------------------------===//
//
// 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 filesystem_error

// filesystem_error(const string& what_arg, error_code ec);
// filesystem_error(const string& what_arg, const path& p1, error_code ec);
// filesystem_error(const string& what_arg, const path& p1, const path& p2, error_code ec);
// const std::error_code& code() const;
// const char* what() const noexcept;
// const path& path1() const noexcept;
// const path& path2() const noexcept;

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "test_macros.h"

namespace fs = std::experimental::filesystem;

void test_constructors() {
using namespace fs;

// The string returned by "filesystem_error::what() must contain runtime_error::what()
const std::string what_arg = "Hello World";
const std::string what_contains = std::runtime_error(what_arg).what();
assert(what_contains.find(what_arg) != std::string::npos);
auto CheckWhat = [what_contains](filesystem_error const& e) {
std::string s = e.what();
assert(s.find(what_contains) != std::string::npos);
};

std::error_code ec = std::make_error_code(std::errc::file_exists);
const path p1("foo");
const path p2("bar");

// filesystem_error(const string& what_arg, error_code ec);
{
ASSERT_NOT_NOEXCEPT(filesystem_error(what_arg, ec));
filesystem_error e(what_arg, ec);
CheckWhat(e);
assert(e.code() == ec);
assert(e.path1().empty() && e.path2().empty());
}
// filesystem_error(const string& what_arg, const path&, error_code ec);
{
ASSERT_NOT_NOEXCEPT(filesystem_error(what_arg, p1, ec));
filesystem_error e(what_arg, p1, ec);
CheckWhat(e);
assert(e.code() == ec);
assert(e.path1() == p1);
assert(e.path2().empty());
}
// filesystem_error(const string& what_arg, const path&, const path&, error_code ec);
{
ASSERT_NOT_NOEXCEPT(filesystem_error(what_arg, p1, p2, ec));
filesystem_error e(what_arg, p1, p2, ec);
CheckWhat(e);
assert(e.code() == ec);
assert(e.path1() == p1);
assert(e.path2() == p2);
}
}

void test_signatures()
{
using namespace fs;
const path p;
std::error_code ec;
const filesystem_error e("lala", ec);
// const path& path1() const noexcept;
{
ASSERT_SAME_TYPE(path const&, decltype(e.path1()));
ASSERT_NOEXCEPT(e.path1());
}
// const path& path2() const noexcept
{
ASSERT_SAME_TYPE(path const&, decltype(e.path2()));
ASSERT_NOEXCEPT(e.path2());
}
// const char* what() const noexcept
{
ASSERT_SAME_TYPE(const char*, decltype(e.what()));
ASSERT_NOEXCEPT(e.what());
}
}

int main() {
static_assert(std::is_base_of<std::system_error, fs::filesystem_error>::value, "");
test_constructors();
test_signatures();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// class path

// template <class Source>
// path(const Source& source);
// template <class InputIterator>
// path(InputIterator first, InputIterator last);


#include <experimental/filesystem>
#include <iterator>
#include <type_traits>
#include <cassert>

#include "test_macros.h"
#include "filesystem_test_helper.hpp"

namespace fs = std::experimental::filesystem;


template <class It>
std::reverse_iterator<It> mkRev(It it) {
return std::reverse_iterator<It>(it);
}


void checkIteratorConcepts() {
using namespace fs;
using It = path::iterator;
using Traits = std::iterator_traits<It>;
ASSERT_SAME_TYPE(Traits::iterator_category, std::bidirectional_iterator_tag);
ASSERT_SAME_TYPE(Traits::value_type, path);
ASSERT_SAME_TYPE(Traits::pointer, path const*);
ASSERT_SAME_TYPE(Traits::reference, path const&);
{
It it;
ASSERT_SAME_TYPE(It&, decltype(++it));
ASSERT_SAME_TYPE(It, decltype(it++));
ASSERT_SAME_TYPE(It&, decltype(--it));
ASSERT_SAME_TYPE(It, decltype(it--));
ASSERT_SAME_TYPE(Traits::reference, decltype(*it));
ASSERT_SAME_TYPE(Traits::pointer, decltype(it.operator->()));
ASSERT_SAME_TYPE(std::string const&, decltype(it->native()));
ASSERT_SAME_TYPE(bool, decltype(it == it));
ASSERT_SAME_TYPE(bool, decltype(it != it));
}
{
path const p;
ASSERT_SAME_TYPE(It, decltype(p.begin()));
ASSERT_SAME_TYPE(It, decltype(p.end()));
assert(p.begin() == p.end());
}
}

void checkBeginEndBasic() {
using namespace fs;
using It = path::iterator;
{
path const p;
ASSERT_SAME_TYPE(It, decltype(p.begin()));
ASSERT_SAME_TYPE(It, decltype(p.end()));
assert(p.begin() == p.end());
}
{
path const p("foo");
It default_constructed;
default_constructed = p.begin();
assert(default_constructed == p.begin());
assert(default_constructed != p.end());
default_constructed = p.end();
assert(default_constructed == p.end());
assert(default_constructed != p.begin());
}
{
path p("//root_name//first_dir////second_dir");
const path expect[] = {"//root_name", "/", "first_dir", "second_dir"};
assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect)));
assert(checkCollectionsEqual(mkRev(p.end()), mkRev(p.begin()), mkRev(std::end(expect)), mkRev(std::begin(expect))));
}
{
path p("////foo/bar/baz///");
const path expect[] = {"/", "foo", "bar", "baz", "."};
assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect)));
assert(checkCollectionsEqual(mkRev(p.end()), mkRev(p.begin()), mkRev(std::end(expect)), mkRev(std::begin(expect))));
}
}

int main() {
using namespace fs;
checkIteratorConcepts();
checkBeginEndBasic(); // See path.decompose.pass.cpp for more tests.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// class path

// path& operator/=(path const&)
// template <class Source>
// path& operator/=(Source const&);
// template <class Source>
// path& append(Source const&);
// template <class InputIterator>
// path& append(InputIterator first, InputIterator last);


#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "test_macros.h"
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"

namespace fs = std::experimental::filesystem;

struct AppendOperatorTestcase {
MultiStringType lhs;
MultiStringType rhs;
MultiStringType expect;
};

#define S(Str) MKSTR(Str)
const AppendOperatorTestcase Cases[] =
{
{S(""), S(""), S("")}
, {S("p1"), S("p2"), S("p1/p2")}
, {S("p1/"), S("p2"), S("p1/p2")}
, {S("p1"), S("/p2"), S("p1/p2")}
, {S("p1/"), S("/p2"), S("p1//p2")}
, {S("p1"), S("\\p2"), S("p1/\\p2")}
, {S("p1\\"), S("p2"), S("p1\\/p2")}
, {S("p1\\"), S("\\p2"), S("p1\\/\\p2")}
, {S("p1"), S(""), S("p1")}
, {S(""), S("p2"), S("p2")}
};


const AppendOperatorTestcase LongLHSCases[] =
{
{S("p1"), S("p2"), S("p1/p2")}
, {S("p1/"), S("p2"), S("p1/p2")}
, {S("p1"), S("/p2"), S("p1/p2")}
};
#undef S


// The append operator may need to allocate a temporary buffer before a code_cvt
// conversion. Test if this allocation occurs by:
// 1. Create a path, `LHS`, and reserve enough space to append `RHS`.
// This prevents `LHS` from allocating during the actual appending.
// 2. Create a `Source` object `RHS`, which represents a "large" string.
// (The string must not trigger the SSO)
// 3. Append `RHS` to `LHS` and check for the expected allocation behavior.
template <class CharT>
void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
{
using namespace fs;
using Ptr = CharT const*;
using Str = std::basic_string<CharT>;
using InputIter = input_iterator<Ptr>;

const Ptr L = TC.lhs;
Str RShort = (Ptr)TC.rhs;
Str EShort = (Ptr)TC.expect;
assert(RShort.size() >= 2);
CharT c = RShort.back();
RShort.append(100, c);
EShort.append(100, c);
const Ptr R = RShort.data();
const Str& E = EShort;
std::size_t ReserveSize = E.size() + 3;
// basic_string
{
path LHS(L); PathReserve(LHS, ReserveSize);
Str RHS(R);
{
DisableAllocationGuard g;
LHS /= RHS;
}
assert(LHS == E);
}
// CharT*
{
path LHS(L); PathReserve(LHS, ReserveSize);
Ptr RHS(R);
{
DisableAllocationGuard g;
LHS /= RHS;
}
assert(LHS == E);
}
{
path LHS(L); PathReserve(LHS, ReserveSize);
Ptr RHS(R);
{
DisableAllocationGuard g;
LHS.append(RHS, StrEnd(RHS));
}
assert(LHS == E);
}
// input iterator - For non-native char types, appends needs to copy the
// iterator range into a contigious block of memory before it can perform the
// code_cvt conversions.
// For "char" no allocations will be performed because no conversion is
// required.
bool DisableAllocations = std::is_same<CharT, char>::value;
{
path LHS(L); PathReserve(LHS, ReserveSize);
InputIter RHS(R);
{
RequireAllocationGuard g; // requires 1 or more allocations occur by default
if (DisableAllocations) g.requireExactly(0);
LHS /= RHS;
}
assert(LHS == E);
}
{
path LHS(L); PathReserve(LHS, ReserveSize);
InputIter RHS(R);
InputIter REnd(StrEnd(R));
{
RequireAllocationGuard g;
if (DisableAllocations) g.requireExactly(0);
LHS.append(RHS, REnd);
}
assert(LHS == E);
}
}

template <class CharT>
void doAppendSourceTest(AppendOperatorTestcase const& TC)
{
using namespace fs;
using Ptr = CharT const*;
using Str = std::basic_string<CharT>;
using InputIter = input_iterator<Ptr>;
const Ptr L = TC.lhs;
const Ptr R = TC.rhs;
const Ptr E = TC.expect;
// basic_string
{
path LHS(L);
Str RHS(R);
path& Ref = (LHS /= RHS);
assert(LHS == E);
assert(&Ref == &LHS);
}
{
path LHS(L);
Str RHS(R);
path& Ref = LHS.append(RHS);
assert(LHS == E);
assert(&Ref == &LHS);
}
// Char*
{
path LHS(L);
Str RHS(R);
path& Ref = (LHS /= RHS);
assert(LHS == E);
assert(&Ref == &LHS);
}
{
path LHS(L);
Ptr RHS(R);
path& Ref = LHS.append(RHS);
assert(LHS == E);
assert(&Ref == &LHS);
}
{
path LHS(L);
Ptr RHS(R);
path& Ref = LHS.append(RHS, StrEnd(RHS));
assert(LHS == E);
assert(&Ref == &LHS);
}
// iterators
{
path LHS(L);
InputIter RHS(R);
path& Ref = (LHS /= RHS);
assert(LHS == E);
assert(&Ref == &LHS);
}
{
path LHS(L); InputIter RHS(R);
path& Ref = LHS.append(RHS);
assert(LHS == E);
assert(&Ref == &LHS);
}
{
path LHS(L);
InputIter RHS(R);
InputIter REnd(StrEnd(R));
path& Ref = LHS.append(RHS, REnd);
assert(LHS == E);
assert(&Ref == &LHS);
}
}

int main()
{
using namespace fs;
for (auto const & TC : Cases) {
{
path LHS((const char*)TC.lhs);
path RHS((const char*)TC.rhs);
path& Ref = (LHS /= RHS);
assert(LHS == (const char*)TC.expect);
assert(&Ref == &LHS);
}
doAppendSourceTest<char> (TC);
doAppendSourceTest<wchar_t> (TC);
doAppendSourceTest<char16_t>(TC);
doAppendSourceTest<char32_t>(TC);
}
for (auto const & TC : LongLHSCases) {
doAppendSourceAllocTest<char>(TC);
doAppendSourceAllocTest<wchar_t>(TC);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// class path

// path& operator=(path const&);

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "test_macros.h"

namespace fs = std::experimental::filesystem;

int main() {
using namespace fs;
static_assert(std::is_copy_assignable<path>::value, "");
static_assert(!std::is_nothrow_copy_assignable<path>::value, "should not be noexcept");
const std::string s("foo");
const path p(s);
path p2;
path& pref = (p2 = p);
assert(p.native() == s);
assert(p2.native() == s);
assert(&pref == &p2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// class path

// path& operator=(path&&) noexcept

#include <experimental/filesystem>
#include <type_traits>
#include <cassert>

#include "test_macros.h"
#include "count_new.hpp"

namespace fs = std::experimental::filesystem;

int main() {
using namespace fs;
static_assert(std::is_nothrow_move_assignable<path>::value, "");
assert(globalMemCounter.checkOutstandingNewEq(0));
const std::string s("we really really really really really really really "
"really really long string so that we allocate");
assert(globalMemCounter.checkOutstandingNewEq(1));
path p(s);
{
DisableAllocationGuard g;
path p2;
path& pref = (p2 = std::move(p));
assert(p2.native() == s);
assert(p.native() != s); // Testing moved from state
assert(&pref == &p2);
}
}
Loading