| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 lexically_normal() const; | ||
|
|
||
| #include "filesystem_include.hpp" | ||
| #include <type_traits> | ||
| #include <vector> | ||
| #include <iostream> | ||
| #include <cassert> | ||
|
|
||
| #include "test_macros.h" | ||
| #include "test_iterators.h" | ||
| #include "count_new.hpp" | ||
| #include "filesystem_test_helper.hpp" | ||
|
|
||
|
|
||
| int main() { | ||
| // clang-format off | ||
| struct { | ||
| std::string input; | ||
| std::string expect; | ||
| } TestCases[] = { | ||
| {"", ""}, | ||
| {"/a/b/c", "/a/b/c"}, | ||
| {"/a/b//c", "/a/b/c"}, | ||
| {"foo/./bar/..", "foo/"}, | ||
| {"foo/.///bar/../", "foo/"}, | ||
| {"/a/b/", "/a/b/"}, | ||
| {"a/b", "a/b"}, | ||
| {"a/b/.", "a/b/"}, | ||
| {"a/b/./", "a/b/"}, | ||
| {"a/..", "."}, | ||
| {".", "."}, | ||
| {"./", "."}, | ||
| {"./.", "."}, | ||
| {"./..", ".."}, | ||
| {"..", ".."}, | ||
| {"../..", "../.."}, | ||
| {"/../", "/"}, | ||
| {"/../..", "/"}, | ||
| {"/../../", "/"}, | ||
| {"..", ".."}, | ||
| {"../", ".."}, | ||
| {"/a/b/c/../", "/a/b/"}, | ||
| {"/a/b/./", "/a/b/"}, | ||
| {"/a/b/c/../d", "/a/b/d"}, | ||
| {"/a/b/c/../d/", "/a/b/d/"}, | ||
| {"//a/", "/a/"}, | ||
| {"//a/b/", "/a/b/"}, | ||
| {"//a/b/.", "/a/b/"}, | ||
| {"//a/..", "/"}, | ||
| ///===---------------------------------------------------------------===// | ||
| /// Tests specifically for the clauses under [fs.path.generic]p6 | ||
| ///===---------------------------------------------------------------===// | ||
| // p1: If the path is empty, stop. | ||
| {"", ""}, | ||
| // p2: Replace each slash character in the root-name with a preferred | ||
| // separator. | ||
| {"NO_ROOT_NAME_ON_LINUX", "NO_ROOT_NAME_ON_LINUX"}, | ||
| // p3: Replace each directory-separator with a preferred-separator. | ||
| // [ Note: The generic pathname grammar ([fs.path.generic]) defines | ||
| // directory-separator as one or more slashes and preferred-separators. | ||
| // — end note ] | ||
| {"/", "/"}, | ||
| {"//", "/"}, | ||
| {"///", "/"}, | ||
| {"a/b", "a/b"}, | ||
| {"a//b", "a/b"}, | ||
| {"a///b", "a/b"}, | ||
| {"a/b/", "a/b/"}, | ||
| {"a/b//", "a/b/"}, | ||
| {"a/b///", "a/b/"}, | ||
| {"///a////b//////", "/a/b/"}, | ||
| // p4: Remove each dot filename and any immediately following directory | ||
| // separators | ||
| {"foo/.", "foo/"}, | ||
| {"foo/./bar/.", "foo/bar/"}, | ||
| {"./foo/././bar/./", "foo/bar/"}, | ||
| {".///foo//.////./bar/.///", "foo/bar/"}, | ||
| // p5: As long as any appear, remove a non-dot-dot filename immediately | ||
| // followed by a directory-separator and a dot-dot filename, along with | ||
| // any immediately following directory separator. | ||
| {"foo/..", "."}, | ||
| {"foo/../", "."}, | ||
| {"foo/bar/..", "foo/"}, | ||
| {"foo/bar/../", "foo/"}, | ||
| {"foo/bar/../..", "."}, | ||
| {"foo/bar/../../", "."}, | ||
| {"foo/bar/baz/../..", "foo/"}, | ||
| {"foo/bar/baz/../../", "foo/"}, | ||
| {"foo/bar/./..", "foo/"}, | ||
| {"foo/bar/./../", "foo/"}, | ||
| // p6: If there is a root-directory, remove all dot-dot filenames and any | ||
| // directory-separators immediately following them. [ Note: These dot-dot | ||
| // filenames attempt to refer to nonexistent parent directories. — end note ] | ||
| {"/..", "/"}, | ||
| {"/../", "/"}, | ||
| {"/foo/../..", "/"}, | ||
| {"/../foo", "/foo"}, | ||
| {"/../foo/../..", "/"}, | ||
| // p7: If the last filename is dot-dot, remove any trailing | ||
| // directory-separator. | ||
| {"../", ".."}, | ||
| {"../../", "../.."}, | ||
| {"foo/../bar/../..///", ".."}, | ||
| {"foo/../bar/..//..///../", "../.."}, | ||
| // p8: If the path is empty, add a dot | ||
| {".", "."}, | ||
| {"./", "."}, | ||
| {"foo/..", "."} | ||
| }; | ||
| // clang-format on | ||
| int ID = 0; | ||
| bool Failed = false; | ||
| for (auto& TC : TestCases) { | ||
| ++ID; | ||
| fs::path p(TC.input); | ||
| const fs::path output = p.lexically_normal(); | ||
| if (!PathEq(output, TC.expect)) { | ||
| Failed = true; | ||
| std::cerr << "TEST CASE #" << ID << " FAILED: \n"; | ||
| std::cerr << " Input: '" << TC.input << "'\n"; | ||
| std::cerr << " Expected: '" << TC.expect << "'\n"; | ||
| std::cerr << " Output: '" << output.native() << "'"; | ||
| std::cerr << std::endl; | ||
| } | ||
| } | ||
| return Failed; | ||
| } |
| 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 path | ||
|
|
||
| // path lexically_relative(const path& p) const; | ||
| // path lexically_proximate(const path& p) const; | ||
|
|
||
| #include "filesystem_include.hpp" | ||
| #include <type_traits> | ||
| #include <vector> | ||
| #include <iostream> | ||
| #include <cassert> | ||
|
|
||
| #include "test_macros.h" | ||
| #include "test_iterators.h" | ||
| #include "count_new.hpp" | ||
| #include "filesystem_test_helper.hpp" | ||
|
|
||
|
|
||
| int main() { | ||
| // clang-format off | ||
| struct { | ||
| std::string input; | ||
| std::string base; | ||
| std::string expect; | ||
| } TestCases[] = { | ||
| {"", "", "."}, | ||
| {"/", "a", ""}, | ||
| {"a", "/", ""}, | ||
| {"//net", "a", ""}, | ||
| {"a", "//net", ""}, | ||
| {"//net/", "//net", ""}, | ||
| {"//net", "//net/", ".."}, | ||
| {"//base", "a", ""}, | ||
| {"a", "a", "."}, | ||
| {"a/b", "a/b", "."}, | ||
| {"a/b/c/", "a/b/c/", "."}, | ||
| {"//net", "//net", "."}, | ||
| {"//net/", "//net/", "."}, | ||
| {"//net/a/b", "//net/a/b", "."}, | ||
| {"/a/d", "/a/b/c", "../../d"}, | ||
| {"/a/b/c", "/a/d", "../b/c"}, | ||
| {"a/b/c", "a", "b/c"}, | ||
| {"a/b/c", "a/b/c/x/y", "../.."}, | ||
| {"a/b/c", "a/b/c", "."}, | ||
| {"a/b", "c/d", "../../a/b"} | ||
| }; | ||
| // clang-format on | ||
| int ID = 0; | ||
| bool Failed = false; | ||
| for (auto& TC : TestCases) { | ||
| ++ID; | ||
| const fs::path p(TC.input); | ||
| const fs::path output = p.lexically_relative(TC.base); | ||
| auto ReportErr = [&](const char* Testing, fs::path const& Output, | ||
| fs::path const& Expected) { | ||
| Failed = true; | ||
| std::cerr << "TEST CASE #" << ID << " FAILED: \n"; | ||
| std::cerr << " Testing: " << Testing << "\n"; | ||
| std::cerr << " Input: '" << TC.input << "'\n"; | ||
| std::cerr << " Base: '" << TC.base << "'\n"; | ||
| std::cerr << " Expected: '" << Expected << "'\n"; | ||
| std::cerr << " Output: '" << Output.native() << "'"; | ||
| std::cerr << std::endl; | ||
| }; | ||
| if (!PathEq(output, TC.expect)) | ||
| ReportErr("path::lexically_relative", output, TC.expect); | ||
| const fs::path proximate_output = p.lexically_proximate(TC.base); | ||
| // [path.gen] lexically_proximate | ||
| // Returns: If the value of lexically_relative(base) is not an empty path, | ||
| // return it.Otherwise return *this. | ||
| const fs::path proximate_expected = output.native().empty() ? p | ||
| : output; | ||
| if (!PathEq(proximate_expected, proximate_output)) | ||
| ReportErr("path::lexically_proximate", proximate_output, proximate_expected); | ||
| } | ||
| return Failed; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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> | ||
|
|
||
| // enum class perm_options; | ||
|
|
||
| #include "filesystem_include.hpp" | ||
| #include <type_traits> | ||
| #include <cassert> | ||
| #include <sys/stat.h> | ||
|
|
||
| #include "test_macros.h" | ||
| #include "check_bitmask_types.hpp" | ||
|
|
||
|
|
||
| constexpr fs::perm_options ME(int val) { | ||
| return static_cast<fs::perm_options>(val); | ||
| } | ||
|
|
||
| int main() { | ||
| typedef fs::perm_options E; | ||
| static_assert(std::is_enum<E>::value, ""); | ||
|
|
||
| // Check that E is a scoped enum by checking for conversions. | ||
| typedef std::underlying_type<E>::type UT; | ||
| static_assert(!std::is_convertible<E, UT>::value, ""); | ||
|
|
||
| static_assert(std::is_same<UT, unsigned char >::value, ""); // Implementation detail | ||
|
|
||
| typedef check_bitmask_type<E, E::replace, E::nofollow> BitmaskTester; | ||
| assert(BitmaskTester::check()); | ||
|
|
||
| static_assert( | ||
| E::replace == ME(1) && | ||
| E::add == ME(2) && | ||
| E::remove == ME(4) && | ||
| E::nofollow == ME(8), | ||
| "Expected enumeration values do not match"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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> | ||
|
|
||
| // path proximate(const path& p, error_code &ec) | ||
| // path proximate(const path& p, const path& base = current_path()) | ||
| // path proximate(const path& p, const path& base, error_code& ec); | ||
|
|
||
| #include "filesystem_include.hpp" | ||
| #include <type_traits> | ||
| #include <vector> | ||
| #include <iostream> | ||
| #include <cassert> | ||
|
|
||
| #include "test_macros.h" | ||
| #include "test_iterators.h" | ||
| #include "count_new.hpp" | ||
| #include "rapid-cxx-test.hpp" | ||
| #include "filesystem_test_helper.hpp" | ||
|
|
||
|
|
||
| static int count_path_elems(const fs::path& p) { | ||
| int count = 0; | ||
| for (auto& elem : p) { | ||
| if (elem != "/" && elem != "") | ||
| ++count; | ||
| } | ||
| return count; | ||
| } | ||
|
|
||
| TEST_SUITE(filesystem_proximate_path_test_suite) | ||
|
|
||
|
|
||
| TEST_CASE(signature_test) | ||
| { | ||
| using fs::path; | ||
| const path p; ((void)p); | ||
| std::error_code ec; ((void)ec); | ||
| ASSERT_NOT_NOEXCEPT(proximate(p)); | ||
| ASSERT_NOT_NOEXCEPT(proximate(p, p)); | ||
| ASSERT_NOT_NOEXCEPT(proximate(p, ec)); | ||
| ASSERT_NOT_NOEXCEPT(proximate(p, p, ec)); | ||
| } | ||
|
|
||
| TEST_CASE(basic_test) { | ||
| using fs::path; | ||
| const path cwd = fs::current_path(); | ||
| const path parent_cwd = cwd.parent_path(); | ||
| const path curdir = cwd.filename(); | ||
| TEST_REQUIRE(!cwd.native().empty()); | ||
| int cwd_depth = count_path_elems(cwd); | ||
| path dot_dot_to_root; | ||
| for (int i=0; i < cwd_depth; ++i) | ||
| dot_dot_to_root /= ".."; | ||
| path relative_cwd = cwd.native().substr(1); | ||
| // clang-format off | ||
| struct { | ||
| std::string input; | ||
| std::string base; | ||
| std::string expect; | ||
| } TestCases[] = { | ||
| {"", "", "."}, | ||
| {cwd, "a", ".."}, | ||
| {parent_cwd, "a", "../.."}, | ||
| {"a", cwd, "a"}, | ||
| {"a", parent_cwd, "fs.op.proximate/a"}, | ||
| {"/", "a", dot_dot_to_root / ".."}, | ||
| {"/", "a/b", dot_dot_to_root / "../.."}, | ||
| {"/", "a/b/", dot_dot_to_root / "../../.."}, | ||
| {"a", "/", relative_cwd / "a"}, | ||
| {"a/b", "/", relative_cwd / "a/b"}, | ||
| {"a", "/net", ".." / relative_cwd / "a"}, | ||
| {"//net/", "//net", "/net/"}, | ||
| {"//net", "//net/", ".."}, | ||
| {"//net", "//net", "."}, | ||
| {"//net/", "//net/", "."}, | ||
| {"//base", "a", dot_dot_to_root / "../base"}, | ||
| {"a", "a", "."}, | ||
| {"a/b", "a/b", "."}, | ||
| {"a/b/c/", "a/b/c/", "."}, | ||
| {"//net/a/b", "//net/a/b", "."}, | ||
| {"/a/d", "/a/b/c", "../../d"}, | ||
| {"/a/b/c", "/a/d", "../b/c"}, | ||
| {"a/b/c", "a", "b/c"}, | ||
| {"a/b/c", "a/b/c/x/y", "../.."}, | ||
| {"a/b/c", "a/b/c", "."}, | ||
| {"a/b", "c/d", "../../a/b"} | ||
| }; | ||
| // clang-format on | ||
| int ID = 0; | ||
| for (auto& TC : TestCases) { | ||
| ++ID; | ||
| std::error_code ec = GetTestEC(); | ||
| fs::path p(TC.input); | ||
| const fs::path output = fs::proximate(p, TC.base, ec); | ||
| TEST_CHECK(!ec); | ||
| TEST_CHECK(PathEq(output, TC.expect)); | ||
| if (!PathEq(output, TC.expect)) { | ||
| const path canon_input = fs::weakly_canonical(TC.input); | ||
| const path canon_base = fs::weakly_canonical(TC.base); | ||
| const path lexically_p = canon_input.lexically_proximate(canon_base); | ||
| std::cerr << "TEST CASE #" << ID << " FAILED: \n"; | ||
| std::cerr << " Input: '" << TC.input << "'\n"; | ||
| std::cerr << " Base: '" << TC.base << "'\n"; | ||
| std::cerr << " Expected: '" << TC.expect << "'\n"; | ||
| std::cerr << " Output: '" << output.native() << "'\n"; | ||
| std::cerr << " Lex Prox: '" << lexically_p.native() << "'\n"; | ||
| std::cerr << " Canon Input: " << canon_input << "\n"; | ||
| std::cerr << " Canon Base: " << canon_base << "\n"; | ||
|
|
||
| std::cerr << std::endl; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| TEST_SUITE_END() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is dual licensed under the MIT and the University of Illinois Open | ||
| // Source Licenses. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++98, c++03 | ||
|
|
||
| // <experimental/filesystem> | ||
|
|
||
| // path proximate(const path& p, error_code &ec) | ||
| // path proximate(const path& p, const path& base = current_path()) | ||
| // path proximate(const path& p, const path& base, error_code& ec); | ||
|
|
||
| #include "filesystem_include.hpp" | ||
| #include <type_traits> | ||
| #include <vector> | ||
| #include <iostream> | ||
| #include <cassert> | ||
|
|
||
| #include "test_macros.h" | ||
| #include "test_iterators.h" | ||
| #include "count_new.hpp" | ||
| #include "rapid-cxx-test.hpp" | ||
| #include "filesystem_test_helper.hpp" | ||
|
|
||
|
|
||
| TEST_SUITE(filesystem_proximate_path_test_suite) | ||
|
|
||
| TEST_CASE(test_signature) { | ||
|
|
||
| } | ||
| int main() { | ||
| // clang-format off | ||
| struct { | ||
| std::string input; | ||
| std::string expect; | ||
| } TestCases[] = { | ||
| {"", fs::current_path()}, | ||
| {".", fs::current_path()}, | ||
| {StaticEnv::File, StaticEnv::File}, | ||
| {StaticEnv::Dir, StaticEnv::Dir}, | ||
| {StaticEnv::SymlinkToDir, StaticEnv::Dir}, | ||
| {StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"}, | ||
| // FIXME? If the trailing separator occurs in a part of the path that exists, | ||
| // it is ommitted. Otherwise it is added to the end of the result. | ||
| {StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"}, | ||
| {StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"}, | ||
| {StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2}, | ||
| {StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""}, | ||
| {StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"}, | ||
| {StaticEnv::Dir / "../dir1", StaticEnv::Dir}, | ||
| {StaticEnv::Dir / "./.", StaticEnv::Dir}, | ||
| {StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"} | ||
| }; | ||
| // clang-format on | ||
| int ID = 0; | ||
| bool Failed = false; | ||
| for (auto& TC : TestCases) { | ||
| ++ID; | ||
| fs::path p(TC.input); | ||
| const fs::path output = fs::weakly_canonical(p); | ||
| if (output != TC.expect) { | ||
| Failed = true; | ||
| std::cerr << "TEST CASE #" << ID << " FAILED: \n"; | ||
| std::cerr << " Input: '" << TC.input << "'\n"; | ||
| std::cerr << " Expected: '" << TC.expect << "'\n"; | ||
| std::cerr << " Output: '" << output.native() << "'"; | ||
| std::cerr << std::endl; | ||
| } | ||
| } | ||
| return Failed; | ||
| } | ||
|
|
||
| TEST_SUITE_END() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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> | ||
|
|
||
| // path weakly_canonical(const path& p); | ||
| // path weakly_canonical(const path& p, error_code& ec); | ||
|
|
||
| #include "filesystem_include.hpp" | ||
| #include <type_traits> | ||
| #include <vector> | ||
| #include <iostream> | ||
| #include <cassert> | ||
|
|
||
| #include "test_macros.h" | ||
| #include "test_iterators.h" | ||
| #include "count_new.hpp" | ||
| #include "filesystem_test_helper.hpp" | ||
|
|
||
|
|
||
| int main() { | ||
| // clang-format off | ||
| struct { | ||
| std::string input; | ||
| std::string expect; | ||
| } TestCases[] = { | ||
| {"", fs::current_path()}, | ||
| {".", fs::current_path()}, | ||
| {"/", "/"}, | ||
| {"/foo", "/foo"}, | ||
| {"/.", "/"}, | ||
| {"/./", "/"}, | ||
| {"a/b", fs::current_path() / "a/b"}, | ||
| {"a", fs::current_path() / "a"}, | ||
| {"a/b/", fs::current_path() / "a/b/"}, | ||
| {StaticEnv::File, StaticEnv::File}, | ||
| {StaticEnv::Dir, StaticEnv::Dir}, | ||
| {StaticEnv::SymlinkToDir, StaticEnv::Dir}, | ||
| {StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"}, | ||
| // FIXME? If the trailing separator occurs in a part of the path that exists, | ||
| // it is ommitted. Otherwise it is added to the end of the result. | ||
| {StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"}, | ||
| {StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"}, | ||
| {StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2}, | ||
| {StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""}, | ||
| {StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"}, | ||
| {StaticEnv::Dir / "../dir1", StaticEnv::Dir}, | ||
| {StaticEnv::Dir / "./.", StaticEnv::Dir}, | ||
| {StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"} | ||
| }; | ||
| // clang-format on | ||
| int ID = 0; | ||
| bool Failed = false; | ||
| for (auto& TC : TestCases) { | ||
| ++ID; | ||
| fs::path p(TC.input); | ||
| const fs::path output = fs::weakly_canonical(p); | ||
| if (!PathEq(output, TC.expect)) { | ||
| Failed = true; | ||
| std::cerr << "TEST CASE #" << ID << " FAILED: \n"; | ||
| std::cerr << " Input: '" << TC.input << "'\n"; | ||
| std::cerr << " Expected: '" << TC.expect << "'\n"; | ||
| std::cerr << " Output: '" << output.native() << "'"; | ||
| std::cerr << std::endl; | ||
| } | ||
| } | ||
| return Failed; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,222 @@ | ||
| #ifndef TEST_SUPPORT_VERBOSE_ASSERT | ||
| #define TEST_SUPPORT_VERBOSE_ASSERT | ||
|
|
||
| #include <iostream> | ||
| #include <cstdio> | ||
| #include <sstream> | ||
| #include <string> | ||
| #include "test_macros.h" | ||
|
|
||
| namespace verbose_assert { | ||
|
|
||
| typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&); | ||
|
|
||
| template <class Stream, class Tp, | ||
| class = decltype(std::declval<Stream&>() << std::declval<Tp const&>())> | ||
| std::true_type IsStreamableImp(int); | ||
| template <class Stream, class Tp> std::false_type IsStreamableImp(long); | ||
|
|
||
| template <class Stream, class Tp> | ||
| struct IsStreamable : decltype(IsStreamableImp<Stream, Tp>(0)) {}; | ||
|
|
||
| template <class Tp, int ST = (IsStreamable<decltype(std::cerr), Tp>::value ? 1 | ||
| : (IsStreamable<decltype(std::wcerr), Tp>::value ? 2 : -1))> | ||
| struct SelectStream { | ||
| static_assert(ST == -1, "specialization required for ST != -1"); | ||
| static void Print(Tp const&) { std::clog << "Value Not Streamable!\n"; } | ||
| }; | ||
|
|
||
| template <class Tp> | ||
| struct SelectStream<Tp, 1> { | ||
| static void Print(Tp const& val) { std::cerr << val; } | ||
| }; | ||
|
|
||
| template <class Tp> | ||
| struct SelectStream<Tp, 2> { | ||
| static void Print(Tp const& val) { std::wcerr << val; } | ||
| }; | ||
|
|
||
| struct AssertData { | ||
| AssertData(const char* xcheck, const char* xfile, const char* xfunc, | ||
| unsigned long xline, bool xpassed = true) | ||
| : passed(xpassed), check(xcheck), file(xfile), func(xfunc), line(xline), | ||
| msg() {} | ||
|
|
||
| AssertData& SetFailed(std::string xmsg = std::string()) { | ||
| msg = xmsg; | ||
| passed = false; | ||
| return *this; | ||
| } | ||
|
|
||
| void PrintFailed() const { | ||
| std::fprintf(stderr, "%s:%lu %s: Assertion '%s' failed.\n", file, line, | ||
| func, check); | ||
| if (!msg.empty()) | ||
| std::fprintf(stderr, "%s\n", msg.data()); | ||
| } | ||
|
|
||
| bool passed; | ||
| const char* check; | ||
| const char* file; | ||
| const char* func; | ||
| unsigned long line; | ||
| std::string msg; | ||
| }; | ||
|
|
||
| // AssertHandler is the class constructed by failing CHECK macros. AssertHandler | ||
| // will log information about the failures and abort when it is destructed. | ||
| class AssertHandler { | ||
| public: | ||
| AssertHandler(AssertData const& Data) | ||
| : passed(Data.passed) { | ||
| if (!passed) | ||
| Data.PrintFailed(); | ||
| } | ||
|
|
||
| ~AssertHandler() TEST_NOEXCEPT_FALSE { | ||
| if (!passed) { | ||
| error_log << std::endl; | ||
| std::abort(); | ||
| } | ||
| } | ||
|
|
||
| class LogType { | ||
| friend class AssertHandler; | ||
|
|
||
| template <class Tp> | ||
| friend LogType& operator<<(LogType& log, Tp const& value) { | ||
| if (!log.is_disabled) { | ||
| SelectStream<Tp>::Print(value); | ||
| } | ||
| return log; | ||
| } | ||
|
|
||
| friend LogType& operator<<(LogType& log, EndLType* m) { | ||
| if (!log.is_disabled) { | ||
| SelectStream<EndLType*>::Print(m); | ||
| } | ||
| return log; | ||
| } | ||
|
|
||
| private: | ||
| LogType(bool disable) : is_disabled(disable) {} | ||
| bool is_disabled; | ||
|
|
||
| LogType(LogType const&); | ||
| LogType& operator=(LogType const&); | ||
| }; | ||
|
|
||
| LogType& GetLog() { | ||
| if (passed) | ||
| return null_log; | ||
| return error_log; | ||
| } | ||
|
|
||
| private: | ||
| static LogType null_log; | ||
| static LogType error_log; | ||
|
|
||
| AssertHandler& operator=(const AssertHandler&) = delete; | ||
| AssertHandler(const AssertHandler&) = delete; | ||
| AssertHandler() = delete; | ||
|
|
||
| private: | ||
| bool passed; | ||
| }; | ||
|
|
||
| AssertHandler::LogType AssertHandler::null_log(true); | ||
| AssertHandler::LogType AssertHandler::error_log(false); | ||
|
|
||
| template <class It1> | ||
| std::string PrintRange(const char* Name, It1 F, It1 E) { | ||
| std::stringstream ss; | ||
| ss << " " << Name << " = ["; | ||
| while (F != E) { | ||
| ss << *F; | ||
| ++F; | ||
| if (F != E) | ||
| ss << ", "; | ||
| } | ||
| ss << "]\n"; | ||
| return ss.str(); | ||
| } | ||
|
|
||
| template <class Tp, class Up> | ||
| std::string PrintMismatch(Tp const& LHS, Up const& RHS, int Elem) { | ||
| std::stringstream ss; | ||
| ss << " Element " << Elem << " mismatched: `" << LHS << "` != `" << RHS | ||
| << "`!\n"; | ||
| return ss.str(); | ||
| }; | ||
|
|
||
| struct EqualToComp { | ||
| template <class Tp, class Up> | ||
| bool operator()(Tp const& LHS, Up const& RHS) const { | ||
| return LHS == RHS; | ||
| } | ||
| }; | ||
|
|
||
| template <class It1, class It2, class Comp> | ||
| AssertData CheckCollectionsEqual(It1 F1, It1 E1, It2 F2, It2 E2, | ||
| AssertData Data, Comp C = EqualToComp()) { | ||
| const It1 F1Orig = F1; | ||
| const It2 F2Orig = F2; | ||
| bool Failed = false; | ||
| std::string ErrorMsg; | ||
| int Idx = 0; | ||
| while (F1 != E1 && F2 != E2) { | ||
| if (!(C(*F1, *F2))) { | ||
| ErrorMsg += PrintMismatch(*F1, *F2, Idx); | ||
| Failed = true; | ||
| break; | ||
| } | ||
| ++Idx; | ||
| ++F1; | ||
| ++F2; | ||
| } | ||
| if (!Failed && (F1 != E1 || F2 != E2)) { | ||
| ErrorMsg += " Ranges have different sizes!\n"; | ||
| Failed = true; | ||
| } | ||
| if (Failed) { | ||
| ErrorMsg += PrintRange("LHS", F1Orig, E1); | ||
| ErrorMsg += PrintRange("RHS", F2Orig, E2); | ||
| Data.SetFailed(ErrorMsg); | ||
| } | ||
| return Data; | ||
| } | ||
| } // namespace verbose_assert | ||
|
|
||
| #ifdef __GNUC__ | ||
| #define ASSERT_FN_NAME() __PRETTY_FUNCTION__ | ||
| #else | ||
| #define ASSERT_FN_NAME() __func__ | ||
| #endif | ||
|
|
||
| #define DISPLAY(...) " " #__VA_ARGS__ " = " << (__VA_ARGS__) << "\n" | ||
|
|
||
| #define ASSERT(...) \ | ||
| ::verbose_assert::AssertHandler(::verbose_assert::AssertData( \ | ||
| #__VA_ARGS__, __FILE__, ASSERT_FN_NAME(), __LINE__,(__VA_ARGS__))).GetLog() | ||
|
|
||
| #define ASSERT_EQ(LHS, RHS) \ | ||
| ASSERT(LHS == RHS) << DISPLAY(LHS) << DISPLAY(RHS) | ||
| #define ASSERT_NEQ(LHS, RHS) \ | ||
| ASSERT(LHS != RHS) << DISPLAY(LHS) << DISPLAY(RHS) | ||
| #define ASSERT_PRED(PRED, LHS, RHS) \ | ||
| ASSERT(PRED(LHS, RHS)) << DISPLAY(LHS) << DISPLAY(RHS) | ||
|
|
||
| #define ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, Comp) \ | ||
| (::verbose_assert::AssertHandler( \ | ||
| ::verbose_assert::CheckCollectionsEqual( \ | ||
| F1, E1, F2, E2, \ | ||
| ::verbose_assert::AssertData("CheckCollectionsEqual(" #F1 ", " #E1 \ | ||
| ", " #F2 ", " #E2 ")", \ | ||
| __FILE__, ASSERT_FN_NAME(), __LINE__), \ | ||
| Comp)) \ | ||
| .GetLog()) | ||
|
|
||
| #define ASSERT_COLLECTION_EQ(F1, E1, F2, E2) \ | ||
| ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, ::verbose_assert::EqualToComp()) | ||
|
|
||
| #endif |