From 10b3c9e5a8ce46cfbc3c80042fcaf6079f6a8a34 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Tue, 24 Mar 2020 19:22:40 +0300 Subject: add some unit tests --- .appveyor.yml | 7 +--- .clang-format | 10 +++-- .travis/Dockerfile | 3 +- src/symbol.cpp | 4 +- test/CMakeLists.txt | 12 +++--- test/unit_tests/CMakeLists.txt | 10 +++++ test/unit_tests/dbghelp.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++ test/unit_tests/error.cpp | 16 +++++++ test/unit_tests/main.cpp | 2 + test/unit_tests/utils.hpp | 37 ++++++++++++++++ 10 files changed, 179 insertions(+), 17 deletions(-) create mode 100644 test/unit_tests/CMakeLists.txt create mode 100644 test/unit_tests/dbghelp.cpp create mode 100644 test/unit_tests/error.cpp create mode 100644 test/unit_tests/main.cpp create mode 100644 test/unit_tests/utils.hpp diff --git a/.appveyor.yml b/.appveyor.yml index f07fecf..15b26de 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -36,11 +36,8 @@ after_build: - appveyor.exe PushArtifact "%APPVEYOR_PROJECT_NAME%-%PLATFORM%-%CONFIGURATION%.zip" test_script: - - '"%install_dir%\bin\enum_symbols" --pdb "%install_dir%\bin\symbols.pdb"' - - '"%install_dir%\bin\enum_symbols" --pdb "%install_dir%\bin\symbols.pdb" --mask "foobar!foobar_ns*"' - - '"%install_dir%\bin\enum_symbols" --pdb "%install_dir%\bin\symbols.pdb" --functions --mask "foobar_ns*"' - - - '"%install_dir%\bin\call_stack"' + - '"%install_dir%\bin\test\unit_tests" --log_level=all' + - '"%install_dir%\bin\test\call_stack"' for: # Only build Release builds on master to speed things up: diff --git a/.clang-format b/.clang-format index 79d4ce0..ea7323d 100644 --- a/.clang-format +++ b/.clang-format @@ -12,12 +12,14 @@ SpacesBeforeTrailingComments: 1 IncludeCategories: - Regex: '^".*' Priority: 1 - - Regex: '^$' + - Regex: '^$' + - Regex: '^$' Priority: 4 - - Regex: '.*' + - Regex: '^<.*\.h>$' Priority: 5 + - Regex: '.*' + Priority: 6 ... diff --git a/.travis/Dockerfile b/.travis/Dockerfile index 9ca711c..d17d54f 100644 --- a/.travis/Dockerfile +++ b/.travis/Dockerfile @@ -31,7 +31,8 @@ RUN ./cmake/boost/build/ci/travis.py \ -- \ --user-config="$TRAVIS_BUILD_DIR/cmake/boost/toolchains/mingw-w64-$platform.jam" \ --with-filesystem \ - --with-program_options + --with-program_options \ + --with-test COPY [".", "$TRAVIS_BUILD_DIR"] diff --git a/src/symbol.cpp b/src/symbol.cpp index 3b0f5d9..d51cc83 100644 --- a/src/symbol.cpp +++ b/src/symbol.cpp @@ -57,7 +57,9 @@ SymbolInfo::SymbolInfo(const Impl& impl) : SymbolInfo{} { } std::string SymbolInfo::get_name() const { - return boost::nowide::narrow(std::wstring{get_impl().Name, get_impl().NameLen}); + // SymFromAddrW, contrary to SymFromAddrA, seems to include the terminating + // null character in NameLen. + return boost::nowide::narrow(get_impl().Name); } LineInfo::LineInfo(const Impl& impl) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ac3e8b3..c4406c8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,16 +2,16 @@ add_executable(call_stack call_stack.cpp) target_link_libraries(call_stack PRIVATE pdb_repo) target_link_libraries(call_stack PRIVATE Boost::nowide) -install(TARGETS call_stack RUNTIME DESTINATION bin) +install(TARGETS call_stack RUNTIME DESTINATION bin/test) if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - install(FILES "$" DESTINATION bin OPTIONAL) + install(FILES "$" DESTINATION bin/test OPTIONAL) endif() -set(CC_STATIC_RUNTIME OFF) # Reduce the number of symbols in this test utility - add_executable(symbols symbols.cpp) -install(TARGETS symbols RUNTIME DESTINATION bin) +install(TARGETS symbols RUNTIME DESTINATION bin/test) if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - install(FILES "$" DESTINATION bin OPTIONAL) + install(FILES "$" DESTINATION bin/test OPTIONAL) endif() + +add_subdirectory(unit_tests) diff --git a/test/unit_tests/CMakeLists.txt b/test/unit_tests/CMakeLists.txt new file mode 100644 index 0000000..e7362e1 --- /dev/null +++ b/test/unit_tests/CMakeLists.txt @@ -0,0 +1,10 @@ +find_package(Boost REQUIRED COMPONENTS filesystem unit_test_framework) + +add_executable(unit_tests main.cpp dbghelp.cpp error.cpp) +target_link_libraries(unit_tests PRIVATE pdb_repo) +target_link_libraries(unit_tests PRIVATE Boost::disable_autolinking Boost::filesystem Boost::nowide Boost::unit_test_framework) + +install(TARGETS unit_tests RUNTIME DESTINATION bin/test) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + install(FILES "$" DESTINATION bin/test OPTIONAL) +endif() diff --git a/test/unit_tests/dbghelp.cpp b/test/unit_tests/dbghelp.cpp new file mode 100644 index 0000000..86cf1ff --- /dev/null +++ b/test/unit_tests/dbghelp.cpp @@ -0,0 +1,95 @@ +#include "utils.hpp" + +#include + +#include +#include + +#include +#include +#include +#include + +namespace { + +class DbgHelp { +public: + DbgHelp() : dbghelp{pdb::DbgHelp::post_mortem()} { BOOST_TEST_MESSAGE("Initializing DbgHelp"); } + + ~DbgHelp() { BOOST_TEST_MESSAGE("Cleaning up DbgHelp"); } + + const pdb::DbgHelp dbghelp; + +private: + DbgHelp(const DbgHelp&) = delete; + DbgHelp& operator=(const DbgHelp&) = delete; +}; + +template +using Set = std::unordered_set; + +template +Set join(Set&& xs, Set&& ys) { + xs.insert(std::make_move_iterator(ys.begin()), std::make_move_iterator(ys.end())); + return std::move(xs); +} + +class DbgHelpWithSymbols : public DbgHelp { +public: + DbgHelpWithSymbols() { load_symbols_pdb(); } + + static const std::string& get_namespace() { + static const std::string name{"foobar_ns"}; + return name; + } + + typedef std::unordered_set SymbolList; + + static SymbolList expected_functions() { return make_qualified({"foo", "bar", "baz"}); } + + static SymbolList expected_variables() { return make_qualified({"exit_code"}); } + + static SymbolList expected_symbols() { + return join(expected_functions(), expected_variables()); + } + +private: + static SymbolList make_qualified(SymbolList&& plain) { + SymbolList qualified; + for (auto&& name : plain) { + qualified.emplace(get_namespace() + "::" + std::move(name)); + } + return qualified; + } + + void load_symbols_pdb() { + const auto pdb_path = get_symbols_pdb_path().string(); + BOOST_TEST_MESSAGE("Loading PDB: " << pdb_path); + dbghelp.load_pdb(pdb_path); + } + + static boost::filesystem::path get_symbols_pdb_path() { + return Paths::get().exe_dir / "symbols.pdb"; + } +}; + +} // namespace + +BOOST_AUTO_TEST_SUITE(dbghelp_tests) +BOOST_FIXTURE_TEST_SUITE(enum_symbols_tests, DbgHelpWithSymbols) + +BOOST_AUTO_TEST_CASE(basic) { + // Symbols can be enumerated, and all the expected symbols are there. + auto all_symbols = expected_symbols(); + const auto callback = [&all_symbols](const pdb::SymbolInfo& symbol) { + const auto name = symbol.get_name(); + all_symbols.erase(name); + }; + dbghelp.enum_symbols(callback); + for (const auto& missing : all_symbols) { + BOOST_TEST(false, "Symbol wasn't enumerated: " << missing); + } +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/unit_tests/error.cpp b/test/unit_tests/error.cpp new file mode 100644 index 0000000..10eb8b4 --- /dev/null +++ b/test/unit_tests/error.cpp @@ -0,0 +1,16 @@ +#include + +#include + +#include + +#include + +BOOST_AUTO_TEST_SUITE(error_tests) + +BOOST_AUTO_TEST_CASE(file_not_found) { + const std::string actual{pdb::error::windows(ERROR_FILE_NOT_FOUND).what()}; + BOOST_TEST(actual == "The system cannot find the file specified."); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/unit_tests/main.cpp b/test/unit_tests/main.cpp new file mode 100644 index 0000000..937bea3 --- /dev/null +++ b/test/unit_tests/main.cpp @@ -0,0 +1,2 @@ +#define BOOST_TEST_MODULE pdb_repo tests +#include diff --git a/test/unit_tests/utils.hpp b/test/unit_tests/utils.hpp new file mode 100644 index 0000000..561ca4d --- /dev/null +++ b/test/unit_tests/utils.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +using path = boost::filesystem::path; + +class FixFilesystem { +public: + FixFilesystem() { boost::nowide::nowide_filesystem(); } +}; + +class Paths { +public: + typedef boost::filesystem::path path; + + static Paths& get() { + static FixFilesystem fix_filesystem; + static Paths instance; + return instance; + } + + Paths() : exe_path{get_executable_path()}, exe_dir{exe_path.parent_path()} {} + + const path exe_path; + const path exe_dir; + +private: + static path get_executable_path() { + const auto argv0 = boost::unit_test::framework::master_test_suite().argv[0]; + return boost::filesystem::system_complete(argv0); + } + + Paths(const Paths&) = delete; + Paths& operator=(const Paths&) = delete; +}; -- cgit v1.2.3