Files
android_external_libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
Eric Fiselier a4c272d82f Implement LWG 3013 - some filesystem members should not be noexcept.
LWG 3013 points out that the constructors and increment members
of the directory iterators need to allocate, and therefore cannot
be marked noexcept.

It also points out that `is_empty` and `copy` likely need to allocate
as well, and as such can also not be noexcept.

This patch speculatively implements the resolution removing noexcept,
because libc++ does indeed have the possibility of throwing on allocation
failure.

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@316941 91177308-0d34-0410-b5e6-96231b3b80d8
2017-10-30 18:59:59 +00:00

487 lines
15 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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 recursive_directory_iterator
// recursive_directory_iterator& operator++();
// recursive_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(recursive_directory_iterator_increment_tests)
TEST_CASE(test_increment_signatures)
{
using D = recursive_directory_iterator;
recursive_directory_iterator d; ((void)d);
std::error_code ec; ((void)ec);
ASSERT_SAME_TYPE(decltype(++d), recursive_directory_iterator&);
ASSERT_NOT_NOEXCEPT(++d);
ASSERT_SAME_TYPE(decltype(d.increment(ec)), recursive_directory_iterator&);
ASSERT_NOT_NOEXCEPT(d.increment(ec));
}
TEST_CASE(test_prefix_increment)
{
const path testDir = StaticEnv::Dir;
const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
std::end( StaticEnv::RecDirIterationList));
const recursive_directory_iterator endIt{};
std::error_code ec;
recursive_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);
recursive_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::RecDirIterationList),
std::end( StaticEnv::RecDirIterationList));
const recursive_directory_iterator endIt{};
std::error_code ec;
recursive_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::RecDirIterationList),
std::end( StaticEnv::RecDirIterationList));
const recursive_directory_iterator endIt{};
std::error_code ec;
recursive_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);
recursive_directory_iterator& it_ref = it.increment(ec);
TEST_REQUIRE(!ec);
TEST_CHECK(&it_ref == &it);
}
TEST_CHECK(it == endIt);
}
TEST_CASE(test_follow_symlinks)
{
const path testDir = StaticEnv::Dir;
auto const& IterList = StaticEnv::RecDirFollowSymlinksIterationList;
const std::set<path> dir_contents(std::begin(IterList), std::end(IterList));
const recursive_directory_iterator endIt{};
std::error_code ec;
recursive_directory_iterator it(testDir,
directory_options::follow_directory_symlink, 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);
recursive_directory_iterator& it_ref = it.increment(ec);
TEST_REQUIRE(!ec);
TEST_CHECK(&it_ref == &it);
}
TEST_CHECK(it == endIt);
}
TEST_CASE(access_denied_on_recursion_test_case)
{
using namespace std::experimental::filesystem;
scoped_test_env env;
const path testFiles[] = {
env.create_dir("dir1"),
env.create_dir("dir1/dir2"),
env.create_file("dir1/dir2/file1"),
env.create_file("dir1/file2")
};
const path startDir = testFiles[0];
const path permDeniedDir = testFiles[1];
const path otherFile = testFiles[3];
auto SkipEPerm = directory_options::skip_permission_denied;
// Change the permissions so we can no longer iterate
permissions(permDeniedDir, perms::none);
const recursive_directory_iterator endIt;
// Test that recursion resulting in a "EACCESS" error is not ignored
// by default.
{
std::error_code ec = GetTestEC();
recursive_directory_iterator it(startDir, ec);
TEST_REQUIRE(ec != GetTestEC());
TEST_REQUIRE(!ec);
while (it != endIt && it->path() != permDeniedDir)
++it;
TEST_REQUIRE(it != endIt);
TEST_REQUIRE(*it == permDeniedDir);
it.increment(ec);
TEST_CHECK(ec);
TEST_CHECK(it == endIt);
}
// Same as above but test operator++().
{
std::error_code ec = GetTestEC();
recursive_directory_iterator it(startDir, ec);
TEST_REQUIRE(!ec);
while (it != endIt && it->path() != permDeniedDir)
++it;
TEST_REQUIRE(it != endIt);
TEST_REQUIRE(*it == permDeniedDir);
TEST_REQUIRE_THROW(filesystem_error, ++it);
}
// Test that recursion resulting in a "EACCESS" error is ignored when the
// correct options are given to the constructor.
{
std::error_code ec = GetTestEC();
recursive_directory_iterator it(startDir, SkipEPerm, ec);
TEST_REQUIRE(!ec);
TEST_REQUIRE(it != endIt);
bool seenOtherFile = false;
if (*it == otherFile) {
++it;
seenOtherFile = true;
TEST_REQUIRE (it != endIt);
}
TEST_REQUIRE(*it == permDeniedDir);
ec = GetTestEC();
it.increment(ec);
TEST_REQUIRE(!ec);
if (seenOtherFile) {
TEST_CHECK(it == endIt);
} else {
TEST_CHECK(it != endIt);
TEST_CHECK(*it == otherFile);
}
}
// Test that construction resulting in a "EACCESS" error is not ignored
// by default.
{
std::error_code ec;
recursive_directory_iterator it(permDeniedDir, ec);
TEST_REQUIRE(ec);
TEST_REQUIRE(it == endIt);
}
// Same as above but testing the throwing constructors
{
TEST_REQUIRE_THROW(filesystem_error,
recursive_directory_iterator(permDeniedDir));
}
// Test that construction resulting in a "EACCESS" error constructs the
// end iterator when the correct options are given.
{
std::error_code ec = GetTestEC();
recursive_directory_iterator it(permDeniedDir, SkipEPerm, ec);
TEST_REQUIRE(!ec);
TEST_REQUIRE(it == endIt);
}
}
// See llvm.org/PR35078
TEST_CASE(test_PR35078)
{
using namespace std::experimental::filesystem;
scoped_test_env env;
const path testFiles[] = {
env.create_dir("dir1"),
env.create_dir("dir1/dir2"),
env.create_dir("dir1/dir2/dir3"),
env.create_file("dir1/file1"),
env.create_file("dir1/dir2/dir3/file2")
};
const path startDir = testFiles[0];
const path permDeniedDir = testFiles[1];
const path nestedDir = testFiles[2];
const path nestedFile = testFiles[3];
// Change the permissions so we can no longer iterate
permissions(permDeniedDir,
perms::remove_perms|perms::group_exec
|perms::owner_exec|perms::others_exec);
const std::error_code eacess_ec =
std::make_error_code(std::errc::permission_denied);
std::error_code ec = GetTestEC();
const recursive_directory_iterator endIt;
auto SetupState = [&](bool AllowEAccess, bool& SeenFile3) {
SeenFile3 = false;
auto Opts = AllowEAccess ? directory_options::skip_permission_denied
: directory_options::none;
recursive_directory_iterator it(startDir, Opts, ec);
while (!ec && it != endIt && *it != nestedDir) {
if (*it == nestedFile)
SeenFile3 = true;
it.increment(ec);
}
return it;
};
{
bool SeenNestedFile = false;
recursive_directory_iterator it = SetupState(false, SeenNestedFile);
TEST_REQUIRE(it != endIt);
TEST_REQUIRE(*it == nestedDir);
ec = GetTestEC();
it.increment(ec);
TEST_CHECK(ec);
TEST_CHECK(ec == eacess_ec);
TEST_CHECK(it == endIt);
}
{
bool SeenNestedFile = false;
recursive_directory_iterator it = SetupState(true, SeenNestedFile);
TEST_REQUIRE(it != endIt);
TEST_REQUIRE(*it == nestedDir);
ec = GetTestEC();
it.increment(ec);
TEST_CHECK(!ec);
if (SeenNestedFile) {
TEST_CHECK(it == endIt);
} else {
TEST_REQUIRE(it != endIt);
TEST_CHECK(*it == nestedFile);
}
}
}
// See llvm.org/PR35078
TEST_CASE(test_PR35078_with_symlink)
{
using namespace std::experimental::filesystem;
scoped_test_env env;
const path testFiles[] = {
env.create_dir("dir1"),
env.create_file("dir1/file1"),
env.create_dir("sym_dir"),
env.create_dir("sym_dir/nested_sym_dir"),
env.create_symlink("sym_dir/nested_sym_dir", "dir1/dir2"),
env.create_dir("sym_dir/dir1"),
env.create_dir("sym_dir/dir1/dir2"),
};
// const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]);
const path startDir = testFiles[0];
const path nestedFile = testFiles[1];
const path permDeniedDir = testFiles[2];
const path symDir = testFiles[4];
// Change the permissions so we can no longer iterate
permissions(permDeniedDir,
perms::remove_perms|perms::group_exec
|perms::owner_exec|perms::others_exec);
const std::error_code eacess_ec =
std::make_error_code(std::errc::permission_denied);
std::error_code ec = GetTestEC();
const recursive_directory_iterator endIt;
auto SetupState = [&](bool AllowEAccess, bool FollowSym, bool& SeenFile3) {
SeenFile3 = false;
auto Opts = AllowEAccess ? directory_options::skip_permission_denied
: directory_options::none;
if (FollowSym)
Opts |= directory_options::follow_directory_symlink;
recursive_directory_iterator it(startDir, Opts, ec);
while (!ec && it != endIt && *it != symDir) {
std::cout << *it << std::endl;
if (*it == nestedFile)
SeenFile3 = true;
it.increment(ec);
}
return it;
};
struct {
bool SkipPermDenied;
bool FollowSymlinks;
bool ExpectSuccess;
} TestCases[] = {
// Passing cases
{false, false, true}, {true, true, true}, {true, false, true},
// Failing cases
{false, true, false}
};
for (auto TC : TestCases) {
bool SeenNestedFile = false;
recursive_directory_iterator it = SetupState(TC.SkipPermDenied,
TC.FollowSymlinks,
SeenNestedFile);
TEST_REQUIRE(!ec);
TEST_REQUIRE(it != endIt);
TEST_REQUIRE(*it == symDir);
ec = GetTestEC();
it.increment(ec);
if (TC.ExpectSuccess) {
TEST_CHECK(!ec);
if (SeenNestedFile) {
TEST_CHECK(it == endIt);
} else {
TEST_REQUIRE(it != endIt);
TEST_CHECK(*it == nestedFile);
}
} else {
TEST_CHECK(ec);
TEST_CHECK(ec == eacess_ec);
TEST_CHECK(it == endIt);
}
}
}
// See llvm.org/PR35078
TEST_CASE(test_PR35078_with_symlink_file)
{
using namespace std::experimental::filesystem;
scoped_test_env env;
const path testFiles[] = {
env.create_dir("dir1"),
env.create_dir("dir1/dir2"),
env.create_file("dir1/file2"),
env.create_dir("sym_dir"),
env.create_dir("sym_dir/sdir1"),
env.create_file("sym_dir/sdir1/sfile1"),
env.create_symlink("sym_dir/sdir1/sfile1", "dir1/dir2/file1")
};
const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]);
const path startDir = testFiles[0];
const path nestedDir = testFiles[1];
const path nestedFile = testFiles[2];
const path permDeniedDir = testFiles[3];
const path symFile = testFiles[TestFilesSize - 1];
// Change the permissions so we can no longer iterate
permissions(permDeniedDir,
perms::remove_perms|perms::group_exec
|perms::owner_exec|perms::others_exec);
const std::error_code eacess_ec =
std::make_error_code(std::errc::permission_denied);
std::error_code ec = GetTestEC();
const recursive_directory_iterator EndIt;
auto SetupState = [&](bool AllowEAccess, bool FollowSym, bool& SeenNestedFile) {
SeenNestedFile = false;
auto Opts = AllowEAccess ? directory_options::skip_permission_denied
: directory_options::none;
if (FollowSym)
Opts |= directory_options::follow_directory_symlink;
recursive_directory_iterator it(startDir, Opts, ec);
while (!ec && it != EndIt && *it != nestedDir) {
if (*it == nestedFile)
SeenNestedFile = true;
it.increment(ec);
}
return it;
};
struct {
bool SkipPermDenied;
bool FollowSymlinks;
bool ExpectSuccess;
} TestCases[] = {
// Passing cases
{false, false, true}, {true, true, true}, {true, false, true},
// Failing cases
{false, true, false}
};
for (auto TC : TestCases){
bool SeenNestedFile = false;
recursive_directory_iterator it = SetupState(TC.SkipPermDenied,
TC.FollowSymlinks,
SeenNestedFile);
TEST_REQUIRE(!ec);
TEST_REQUIRE(it != EndIt);
TEST_REQUIRE(*it == nestedDir);
ec = GetTestEC();
it.increment(ec);
TEST_REQUIRE(it != EndIt);
TEST_CHECK(!ec);
TEST_CHECK(*it == symFile);
ec = GetTestEC();
it.increment(ec);
if (TC.ExpectSuccess) {
if (!SeenNestedFile) {
TEST_CHECK(!ec);
TEST_REQUIRE(it != EndIt);
TEST_CHECK(*it == nestedFile);
ec = GetTestEC();
it.increment(ec);
}
TEST_CHECK(!ec);
TEST_CHECK(it == EndIt);
} else {
TEST_CHECK(ec);
TEST_CHECK(ec == eacess_ec);
TEST_CHECK(it == EndIt);
}
}
}
TEST_SUITE_END()