diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-07-23 02:00:52 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-07-23 02:00:52 +0000 |
commit | 9158bfd32ebd457476bc367707fcf391ca0520f1 (patch) | |
tree | 5669f4d5e87703dfcf8ab10a6e1eeeba05ecab0d /libcxx/test | |
parent | aa87753097b1302d268a4f27ee1251f065379f56 (diff) | |
download | bcm5719-llvm-9158bfd32ebd457476bc367707fcf391ca0520f1.tar.gz bcm5719-llvm-9158bfd32ebd457476bc367707fcf391ca0520f1.zip |
Implement filesystem_error::what() and improve reporting.
This patch implements the `what()` for filesystem errors. The message
includes the 'what_arg', any paths that were specified, and the
error code message.
Additionally this patch refactors how errors are created, making it easier
to report them correctly.
llvm-svn: 337664
Diffstat (limited to 'libcxx/test')
10 files changed, 184 insertions, 44 deletions
diff --git a/libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp b/libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp index 30b44b877c5..2305f54f5cb 100644 --- a/libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp +++ b/libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp @@ -80,7 +80,8 @@ TEST_CASE(last_write_time_not_representable_error) { TEST_CHECK(last_write_time(file, ec) == file_time_type::min()); TEST_CHECK(ErrorIs(ec, expected_err)); - ExceptionChecker CheckExcept(file, expected_err); + ExceptionChecker CheckExcept(file, expected_err, + "directory_entry::last_write_time"); TEST_CHECK_THROW_RESULT(fs::filesystem_error, CheckExcept, ent.last_write_time()); diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp index 40d2cef1da1..74c73f030c9 100644 --- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp @@ -168,7 +168,8 @@ TEST_CASE(refresh_cannot_resolve) { TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); TEST_CHECK(ent.path() == file); - ExceptionChecker Checker(file, std::errc::permission_denied); + ExceptionChecker Checker(file, std::errc::permission_denied, + "directory_entry::refresh"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh()); } permissions(dir, old_perms); @@ -182,7 +183,8 @@ TEST_CASE(refresh_cannot_resolve) { TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); TEST_CHECK(ent.path() == sym_in_dir); - ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied); + ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied, + "directory_entry::refresh"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh()); } permissions(dir, old_perms); diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp index d5050edc71f..a38b10c00fc 100644 --- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp @@ -102,7 +102,7 @@ TEST_CASE(not_regular_file) { TEST_CHECK(ec == other_ec); TEST_CHECK(ErrorIs(ec, TC.expected_err)); - ExceptionChecker Checker(p, TC.expected_err); + ExceptionChecker Checker(p, TC.expected_err, "directory_entry::file_size"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); } } @@ -134,7 +134,8 @@ TEST_CASE(error_reporting) { TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); ExceptionChecker Checker(StaticEnv::DNE, - std::errc::no_such_file_or_directory); + std::errc::no_such_file_or_directory, + "directory_entry::file_size"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); } // test a dead symlink @@ -156,7 +157,8 @@ TEST_CASE(error_reporting) { TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); ExceptionChecker Checker(StaticEnv::BadSymlink, - std::errc::no_such_file_or_directory); + std::errc::no_such_file_or_directory, + "directory_entry::file_size"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); } // test a file w/o appropriate permissions. @@ -174,7 +176,7 @@ TEST_CASE(error_reporting) { TEST_CHECK(ent.file_size(ec) == uintmax_t(-1)); TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); - ExceptionChecker Checker(file, std::errc::permission_denied); + ExceptionChecker Checker(file, std::errc::permission_denied, "file_size"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); permissions(dir, old_perms); @@ -199,7 +201,8 @@ TEST_CASE(error_reporting) { 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); + ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied, + "file_size"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); permissions(dir, old_perms); @@ -224,7 +227,8 @@ TEST_CASE(error_reporting) { 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); + ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied, + "file_size"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); permissions(dir, old_perms); diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp index 0ec801bb4bf..d11457d20ae 100644 --- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp @@ -133,7 +133,8 @@ TEST_CASE(error_reporting) { TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); ExceptionChecker Checker(StaticEnv::DNE, - std::errc::no_such_file_or_directory); + std::errc::no_such_file_or_directory, + "directory_entry::hard_link_count"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); } // test a dead symlink @@ -155,7 +156,8 @@ TEST_CASE(error_reporting) { TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); ExceptionChecker Checker(StaticEnv::BadSymlink, - std::errc::no_such_file_or_directory); + std::errc::no_such_file_or_directory, + "directory_entry::hard_link_count"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); } // test a file w/o appropriate permissions. @@ -173,7 +175,8 @@ TEST_CASE(error_reporting) { 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); + ExceptionChecker Checker(file, std::errc::permission_denied, + "hard_link_count"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); permissions(dir, old_perms); @@ -198,7 +201,8 @@ TEST_CASE(error_reporting) { 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); + ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied, + "hard_link_count"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); permissions(dir, old_perms); @@ -223,7 +227,8 @@ TEST_CASE(error_reporting) { 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); + ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied, + "hard_link_count"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); permissions(dir, old_perms); diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp index 250dc6f5a11..3d519728c50 100644 --- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp @@ -106,7 +106,8 @@ TEST_CASE(error_reporting) { TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); ExceptionChecker Checker(StaticEnv::DNE, - std::errc::no_such_file_or_directory); + std::errc::no_such_file_or_directory, + "directory_entry::last_write_time"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); } // test a dead symlink @@ -128,7 +129,8 @@ TEST_CASE(error_reporting) { TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); ExceptionChecker Checker(StaticEnv::BadSymlink, - std::errc::no_such_file_or_directory); + std::errc::no_such_file_or_directory, + "directory_entry::last_write_time"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); } // test a file w/o appropriate permissions. @@ -146,7 +148,8 @@ TEST_CASE(error_reporting) { 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); + ExceptionChecker Checker(file, std::errc::permission_denied, + "last_write_time"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); permissions(dir, old_perms); @@ -171,7 +174,8 @@ TEST_CASE(error_reporting) { 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); + ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied, + "last_write_time"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); permissions(dir, old_perms); @@ -196,7 +200,8 @@ TEST_CASE(error_reporting) { 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); + ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied, + "last_write_time"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); permissions(dir, old_perms); diff --git a/libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp index 1699a414af1..830f4a9436d 100644 --- a/libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp @@ -25,6 +25,8 @@ #include "rapid-cxx-test.hpp" #include "filesystem_test_helper.hpp" +#include <iostream> + using namespace fs; TEST_SUITE(recursive_directory_iterator_increment_tests) @@ -290,18 +292,15 @@ TEST_CASE(test_PR35078) } { bool SeenNestedFile = false; - recursive_directory_iterator it = SetupState(true, SeenNestedFile); + recursive_directory_iterator it = SetupState(false, 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); - } + + ExceptionChecker Checker(std::errc::permission_denied, + "recursive_directory_iterator::operator++()", + format_string("attempting recursion into \"%s\"", + nestedDir.native())); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ++it); } } diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp index 3c2543ab942..5f023743a43 100644 --- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp @@ -64,7 +64,7 @@ TEST_CASE(test_error_reporting) { TEST_CHECK(fs::copy_file(file, file, copy_options::overwrite_existing, ec) == false); TEST_CHECK(ErrorIs(ec, std::errc::file_exists)); - ExceptionChecker Checker(file, file, std::errc::file_exists); + ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing)); } @@ -72,7 +72,7 @@ TEST_CASE(test_error_reporting) { std::error_code ec; TEST_CHECK(fs::copy_file(file, file2, ec) == false); TEST_CHECK(ErrorIs(ec, std::errc::file_exists)); - ExceptionChecker Checker(file, file, std::errc::file_exists); + ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing)); } diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp index e2b2513ecdb..c79012d9e13 100644 --- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp @@ -69,14 +69,15 @@ TEST_CASE(file_size_error_cases) {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}}; + {StaticEnv::DNE, std::errc::no_such_file_or_directory}, + {"", std::errc::no_such_file_or_directory}}; const uintmax_t expect = static_cast<uintmax_t>(-1); 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); + ExceptionChecker Checker(TC.p, TC.expected_err, "file_size"); TEST_CHECK_THROW_RESULT(filesystem_error, Checker, file_size(TC.p)); } } diff --git a/libcxx/test/support/filesystem_test_helper.hpp b/libcxx/test/support/filesystem_test_helper.hpp index de06868c014..f027928700b 100644 --- a/libcxx/test/support/filesystem_test_helper.hpp +++ b/libcxx/test/support/filesystem_test_helper.hpp @@ -9,8 +9,11 @@ #include <random> #include <chrono> #include <vector> +#include <regex> +#include "test_macros.h" #include "rapid-cxx-test.hpp" +#include "format_string.hpp" // static test helpers @@ -442,25 +445,77 @@ inline bool PathEq(fs::path const& LHS, fs::path const& RHS) { } struct ExceptionChecker { - std::vector<std::errc> expected_err_list; + std::errc expected_err; fs::path expected_path1; fs::path expected_path2; + unsigned num_paths; + const char* func_name; + std::string opt_message; + + explicit ExceptionChecker(std::errc first_err, const char* func_name, + std::string opt_msg = {}) + : expected_err{first_err}, num_paths(0), func_name(func_name), + opt_message(opt_msg) {} + explicit ExceptionChecker(fs::path p, std::errc first_err, + const char* func_name, std::string opt_msg = {}) + : expected_err(first_err), expected_path1(p), num_paths(1), + func_name(func_name), opt_message(opt_msg) {} - 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) {} + const char* func_name, std::string opt_msg = {}) + : expected_err(first_err), expected_path1(p1), expected_path2(p2), + num_paths(2), func_name(func_name), opt_message(opt_msg) {} - void operator()(fs::filesystem_error const& Err) const { - TEST_CHECK(ErrorIsImp(Err.code(), expected_err_list)); + void operator()(fs::filesystem_error const& Err) { + TEST_CHECK(ErrorIsImp(Err.code(), {expected_err})); TEST_CHECK(Err.path1() == expected_path1); TEST_CHECK(Err.path2() == expected_path2); + LIBCPP_ONLY(check_libcxx_string(Err)); + } + + void check_libcxx_string(fs::filesystem_error const& Err) { + std::string message = std::make_error_code(expected_err).message(); + + std::string additional_msg = ""; + if (!opt_message.empty()) { + additional_msg = opt_message + ": "; + } + auto transform_path = [](const fs::path& p) { + if (p.native().empty()) + return "\"\""; + return p.c_str(); + }; + std::string format = [&]() -> std::string { + switch (num_paths) { + case 0: + return format_string("filesystem error: in %s: %s%s", func_name, + additional_msg, message); + case 1: + return format_string("filesystem error: in %s: %s%s [%s]", func_name, + additional_msg, message, + transform_path(expected_path1)); + case 2: + return format_string("filesystem error: in %s: %s%s [%s] [%s]", + func_name, additional_msg, message, + transform_path(expected_path1), + transform_path(expected_path2)); + default: + TEST_CHECK(false && "unexpected case"); + return ""; + } + }(); + TEST_CHECK(format == Err.what()); + if (format != Err.what()) { + fprintf(stderr, + "filesystem_error::what() does not match expected output:\n"); + fprintf(stderr, " expected: \"%s\"\n", format.c_str()); + fprintf(stderr, " actual: \"%s\"\n\n", Err.what()); + } } + + ExceptionChecker(ExceptionChecker const&) = delete; + ExceptionChecker& operator=(ExceptionChecker const&) = delete; + }; #endif /* FILESYSTEM_TEST_HELPER_HPP */ diff --git a/libcxx/test/support/format_string.hpp b/libcxx/test/support/format_string.hpp new file mode 100644 index 00000000000..ea3116facb5 --- /dev/null +++ b/libcxx/test/support/format_string.hpp @@ -0,0 +1,68 @@ +#ifndef TEST_SUPPORT_FORMAT_STRING_HPP +#define TEST_SUPPORT_FORMAT_STRING_HPP + +#include <cstdio> +#include <string> +#include <memory> +#include <array> + +namespace format_string_detail { +inline std::string format_string_imp(const char* msg, ...) { + // we might need a second shot at this, so pre-emptivly make a copy + struct GuardVAList { + va_list& target; + bool active = true; + void clear() { + if (active) + va_end(target); + active = false; + } + ~GuardVAList() { + if (active) + va_end(target); + } + }; + va_list args; + va_start(args, msg); + GuardVAList args_guard = {args}; + + va_list args_cp; + va_copy(args_cp, args); + GuardVAList args_copy_guard = {args_cp}; + + std::array<char, 256> local_buff; + std::size_t size = local_buff.size(); + auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp); + + args_copy_guard.clear(); + + // handle empty expansion + if (ret == 0) + return std::string{}; + if (static_cast<std::size_t>(ret) < size) + return std::string(local_buff.data()); + + // we did not provide a long enough buffer on our first attempt. + // add 1 to size to account for null-byte in size cast to prevent overflow + size = static_cast<std::size_t>(ret) + 1; + auto buff_ptr = std::unique_ptr<char[]>(new char[size]); + ret = ::vsnprintf(buff_ptr.get(), size, msg, args); + return std::string(buff_ptr.get()); +} + +const char* unwrap(std::string& s) { return s.c_str(); } +template <class Arg> +Arg const& unwrap(Arg& a) { + static_assert(!std::is_class<Arg>::value, "cannot pass class here"); + return a; +} + +} // namespace format_string_detail + +template <class... Args> +std::string format_string(const char* fmt, Args const&... args) { + return format_string_detail::format_string_imp( + fmt, format_string_detail::unwrap(const_cast<Args&>(args))...); +} + +#endif // TEST_SUPPORT_FORMAT_STRING_HPP |