diff --git a/.travis.yml b/.travis.yml index 08783c04..d5e8c9c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,8 @@ addons: - python-xcbgen - xcb-proto - xutils-dev + - libgtest0 + - libgtest-dev env: global: diff --git a/CMakeLists.txt b/CMakeLists.txt index c23f6e2f..b6725b7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,9 @@ add_subdirectory(lib) add_subdirectory(man) add_subdirectory(src bin) +# We need to enable testing in the root folder so that 'ctest' and 'make test' +# can be run in the build directory +enable_testing() if(BUILD_TESTS) add_subdirectory(tests) else() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ce47a897..c8018f0f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,26 +1,62 @@ -# -# Based on https://github.com/modern-cpp-examples/match3/blob/master/test/CMakeLists.txt -# -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -include common/test.hpp") +# Compile and link with coverage +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fprofile-arcs -ftest-coverage") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") link_libraries(${libs}) include_directories(${dirs}) include_directories(${PROJECT_SOURCE_DIR}/src) include_directories(${CMAKE_CURRENT_LIST_DIR}) -function(unit_test file) +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTEST REQUIRED gtest) + +function(unit_test file tests) + set(multi_value_args SOURCES) + + cmake_parse_arguments("BIN" "" "" "${multi_value_args}" ${ARGN}) + + # Prefix all sources needed by the tests with ../src/ so that the calls to the + # unit_test function become cleaner + SET(sources "") + FOREACH(f ${BIN_SOURCES}) + LIST(APPEND sources "../src/${f}") + ENDFOREACH(f) + string(REPLACE "/" "_" testname ${file}) - add_executable(unit_test.${testname} unit_tests/${file}.cpp) - add_test(unit_test.${testname} unit_test.${testname}) + set(name "unit_test.${testname}") + add_executable(${name} unit_tests/${file}.cpp ${sources}) + + # Link against googletest + target_link_libraries(${name} ${GTEST_LDFLAGS}) + target_compile_options(${name} PUBLIC ${GTEST_CFLAGS}) + + add_test(NAME ${name} COMMAND ${name}) + + # Add test to list of unit tests + list(APPEND ${tests} "${name}") + set(${tests} ${${tests}} PARENT_SCOPE) endfunction() -unit_test(utils/color) -unit_test(utils/math) -unit_test(utils/memory) -unit_test(utils/string) -unit_test(utils/file) -unit_test(components/command_line) +unit_test(utils/color unit_tests) +unit_test(utils/math unit_tests) +unit_test(utils/memory unit_tests) +unit_test(utils/string unit_tests + SOURCES + utils/string.cpp) +unit_test(utils/file unit_tests + SOURCES + utils/command.cpp + utils/file.cpp + utils/env.cpp + utils/process.cpp + utils/io.cpp + utils/string.cpp + utils/concurrency.cpp + components/logger.cpp) +unit_test(components/command_line unit_tests + SOURCES + components/command_line.cpp + utils/string.cpp) -# XXX: Requires mocked xcb connection -#unit_test("x11/connection") -#unit_test("x11/winspec") +# Compile all unit tests with 'make all_unit_tests' +add_custom_target("all_unit_tests" DEPENDS ${unit_tests}) diff --git a/tests/common/test.hpp b/tests/common/test.hpp index fa662b03..5ee1b5e5 100644 --- a/tests/common/test.hpp +++ b/tests/common/test.hpp @@ -9,6 +9,7 @@ #include #include +#include #define expect(...) \ (void)((__VA_ARGS__) || (expect_fail__(#__VA_ARGS__, __FILE__, __LINE__), 0)) diff --git a/tests/unit_tests/components/command_line.cpp b/tests/unit_tests/components/command_line.cpp index d93f470f..79873a70 100644 --- a/tests/unit_tests/components/command_line.cpp +++ b/tests/unit_tests/components/command_line.cpp @@ -1,160 +1,113 @@ -#include "components/command_line.cpp" -#include "utils/string.cpp" +#include "common/test.hpp" +#include "components/command_line.hpp" +#include "utils/string.hpp" -int main() { - using namespace polybar; +using namespace polybar; - const auto get_opts = []() -> const command_line::options { - // clang-format off - return command_line::options{ - command_line::option{"-f", "--flag", "Flag description"}, - command_line::option{"-o", "--option", "Option description", "OPTION", {"foo", "bar", "baz"}}, +class CommandLine : public ::testing::Test { + protected: + virtual void SetUp() { + set_cli(); + } + + virtual void set_cli() { + cli = command_line::parser::make("cmd", get_opts()); + } + + command_line::options get_opts() { + // clang-format off + return command_line::options { + command_line::option{"-f", "--flag", "Flag description"}, + command_line::option{"-o", "--option", "Option description", "OPTION", {"foo", "bar", "baz"}}, + }; + // clang-format on }; - // clang-format on - }; - "has_short"_test = [&] { - auto cli = command_line::parser::make("cmd", get_opts()); - cli->process_input(string_util::split("-f", ' ')); - expect(cli->has("flag")); - expect(!cli->has("option")); + command_line::parser::make_type cli; +}; - cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("-f -o foo", ' ')); - expect(cli->has("flag")); - expect(cli->has("option")); - cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("-o baz", ' ')); - expect(!cli->has("flag")); - expect(cli->has("option")); - }; +TEST_F(CommandLine, hasShort) { + cli->process_input(string_util::split("-f", ' ')); + EXPECT_TRUE(cli->has("flag")); + EXPECT_FALSE(cli->has("option")); - "has_long"_test = [&] { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("--flag", ' ')); - expect(cli->has("flag")); - expect(!cli->has("option")); + set_cli(); + cli->process_input(string_util::split("-f -o foo", ' ')); + EXPECT_TRUE(cli->has("flag")); + EXPECT_TRUE(cli->has("option")); - cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("--flag --option=foo", ' ')); - expect(cli->has("flag")); - expect(cli->has("option")); - - cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("--option=foo --flag", ' ')); - expect(cli->has("flag")); - expect(cli->has("option")); - - cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("--option=baz", ' ')); - expect(!cli->has("flag")); - expect(cli->has("option")); - }; - - "compare"_test = [&] { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("-o baz", ' ')); - expect(cli->compare("option", "baz")); - - cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("--option=foo", ' ')); - expect(cli->compare("option", "foo")); - }; - - "get"_test = [&] { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("--option=baz", ' ')); - expect("baz" == cli->get("option")); - - cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(string_util::split("--option=foo", ' ')); - expect("foo" == cli->get("option")); - }; - - "missing_value"_test = [&] { - auto input1 = string_util::split("--option", ' '); - auto input2 = string_util::split("-o", ' '); - auto input3 = string_util::split("--option baz", ' '); - - bool exception_thrown = false; - try { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(input1); - } catch (const command_line::value_error&) { - exception_thrown = true; - } catch (...) { - } - expect(exception_thrown); - - exception_thrown = false; // reset - try { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(input2); - } catch (const command_line::value_error&) { - exception_thrown = true; - } catch (...) { - } - expect(exception_thrown); - - exception_thrown = false; // reset - try { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(input3); - } catch (const command_line::value_error&) { - exception_thrown = true; - } catch (...) { - } - expect(exception_thrown); - }; - - "invalid_value"_test = [&] { - auto input1 = string_util::split("--option=invalid", ' '); - auto input2 = string_util::split("-o invalid_value", ' '); - - bool exception_thrown = false; - try { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(input1); - } catch (const command_line::value_error&) { - exception_thrown = true; - } catch (...) { - } - expect(exception_thrown); - - exception_thrown = false; // reset - try { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(input2); - } catch (const command_line::value_error&) { - exception_thrown = true; - } catch (...) { - } - expect(exception_thrown); - }; - - "unrecognized"_test = [&] { - auto input1 = string_util::split("-x", ' '); - auto input2 = string_util::split("--unrecognized", ' '); - - bool exception_thrown = false; - try { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(input1); - } catch (const command_line::argument_error&) { - exception_thrown = true; - } catch (...) { - } - expect(exception_thrown); - - exception_thrown = false; // reset - try { - auto cli = command_line::parser::make("cmd", get_opts());; - cli->process_input(input2); - } catch (const command_line::argument_error&) { - exception_thrown = true; - } catch (...) { - } - expect(exception_thrown); - }; + set_cli(); + cli->process_input(string_util::split("-o baz", ' ')); + EXPECT_FALSE(cli->has("flag")); + EXPECT_TRUE(cli->has("option")); +} + +TEST_F(CommandLine, hasLong) { + cli->process_input(string_util::split("--flag", ' ')); + EXPECT_TRUE(cli->has("flag")); + EXPECT_FALSE(cli->has("option")); + + set_cli(); + cli->process_input(string_util::split("--flag --option=foo", ' ')); + EXPECT_TRUE(cli->has("flag")); + EXPECT_TRUE(cli->has("option")); + + set_cli(); + cli->process_input(string_util::split("--option=foo --flag", ' ')); + EXPECT_TRUE(cli->has("flag")); + EXPECT_TRUE(cli->has("option")); + + set_cli(); + cli->process_input(string_util::split("--option=baz", ' ')); + EXPECT_FALSE(cli->has("flag")); + EXPECT_TRUE(cli->has("option")); +} + +TEST_F(CommandLine, compare) { + cli->process_input(string_util::split("-o baz", ' ')); + EXPECT_TRUE(cli->compare("option", "baz")); + + set_cli(); + cli->process_input(string_util::split("--option=foo", ' ')); + EXPECT_TRUE(cli->compare("option", "foo")); +} + +TEST_F(CommandLine, get) { + cli->process_input(string_util::split("--option=baz", ' ')); + EXPECT_EQ("baz", cli->get("option")); + + set_cli(); + cli->process_input(string_util::split("--option=foo", ' ')); + EXPECT_EQ("foo", cli->get("option")); +} + +TEST_F(CommandLine, missingValue) { + auto input1 = string_util::split("--option", ' '); + auto input2 = string_util::split("-o", ' '); + auto input3 = string_util::split("--option baz", ' '); + + EXPECT_THROW(cli->process_input(input1), command_line::value_error); + set_cli(); + EXPECT_THROW(cli->process_input(input2), command_line::value_error); + set_cli(); + EXPECT_THROW(cli->process_input(input3), command_line::value_error); +} + +TEST_F(CommandLine, invalidValue) { + auto input1 = string_util::split("--option=invalid", ' '); + auto input2 = string_util::split("-o invalid_value", ' '); + + EXPECT_THROW(cli->process_input(input1), command_line::value_error); + set_cli(); + EXPECT_THROW(cli->process_input(input2), command_line::value_error); +} + +TEST_F(CommandLine, unrecognized) { + auto input1 = string_util::split("-x", ' '); + auto input2 = string_util::split("--unrecognized", ' '); + + EXPECT_THROW(cli->process_input(input1), command_line::argument_error); + set_cli(); + EXPECT_THROW(cli->process_input(input2), command_line::argument_error); } diff --git a/tests/unit_tests/utils/file.cpp b/tests/unit_tests/utils/file.cpp index 4aacd80d..47e915ef 100644 --- a/tests/unit_tests/utils/file.cpp +++ b/tests/unit_tests/utils/file.cpp @@ -1,14 +1,9 @@ #include #include -#include "components/logger.cpp" -#include "utils/command.cpp" -#include "utils/concurrency.cpp" -#include "utils/env.cpp" -#include "utils/file.cpp" -#include "utils/io.cpp" -#include "utils/process.cpp" -#include "utils/string.cpp" +#include "common/test.hpp" +#include "utils/command.hpp" +#include "utils/file.hpp" int main() { using namespace polybar; diff --git a/tests/unit_tests/utils/math.cpp b/tests/unit_tests/utils/math.cpp index f338a98d..faceb18c 100644 --- a/tests/unit_tests/utils/math.cpp +++ b/tests/unit_tests/utils/math.cpp @@ -1,3 +1,4 @@ +#include "common/test.hpp" #include "utils/math.hpp" int main() { diff --git a/tests/unit_tests/utils/memory.cpp b/tests/unit_tests/utils/memory.cpp index 92dc43ba..5c50f8b2 100644 --- a/tests/unit_tests/utils/memory.cpp +++ b/tests/unit_tests/utils/memory.cpp @@ -1,24 +1,23 @@ +#include "common/test.hpp" #include "utils/memory.hpp" +using namespace polybar; + struct mytype { int x, y, z; }; -int main() { - using namespace polybar; - - "make_malloc_ptr"_test = [] { - auto ptr = memory_util::make_malloc_ptr(); - expect(sizeof(mytype*) == sizeof(ptr.get())); - ptr.reset(); - expect(ptr.get() == nullptr); - }; - - "countof"_test = [] { - mytype A[3]{{}, {}, {}}; - mytype B[8]{{}, {}, {}, {}, {}, {}, {}, {}}; - - expect(memory_util::countof(A) == size_t{3}); - expect(memory_util::countof(B) == size_t{8}); - }; +TEST(Memory, makeMallocPtr) { + auto ptr = memory_util::make_malloc_ptr(); + EXPECT_EQ(sizeof(mytype*), sizeof(ptr.get())); + ptr.reset(); + EXPECT_EQ(nullptr, ptr.get()); +} + +TEST(Memory, countof) { + mytype A[3]{{}, {}, {}}; + mytype B[8]{{}, {}, {}, {}, {}, {}, {}, {}}; + + EXPECT_EQ(memory_util::countof(A), size_t{3}); + EXPECT_EQ(memory_util::countof(B), size_t{8}); } diff --git a/tests/unit_tests/utils/scope.cpp b/tests/unit_tests/utils/scope.cpp index 839db83c..5324e0d8 100644 --- a/tests/unit_tests/utils/scope.cpp +++ b/tests/unit_tests/utils/scope.cpp @@ -1,3 +1,4 @@ +#include "common/test.hpp" #include "utils/scope.hpp" int main() { diff --git a/tests/unit_tests/utils/string.cpp b/tests/unit_tests/utils/string.cpp index 93e109f5..e52e52dd 100644 --- a/tests/unit_tests/utils/string.cpp +++ b/tests/unit_tests/utils/string.cpp @@ -1,6 +1,7 @@ #include -#include "utils/string.cpp" +#include "common/test.hpp" +#include "utils/string.hpp" int main() { using namespace polybar; diff --git a/tests/unit_tests/x11/connection.cpp b/tests/unit_tests/x11/connection.cpp deleted file mode 100644 index 4bce78a6..00000000 --- a/tests/unit_tests/x11/connection.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "utils/string.cpp" -#include "x11/atoms.cpp" -#include "x11/connection.cpp" -#include "x11/xutils.cpp" -#include "x11/xlib.cpp" - -int main() { - using namespace polybar; - - "id"_test = [] { - connection& conn{configure_connection().create()}; - expect(conn.id(static_cast(0x12345678)) == "0x12345678"); - }; -} diff --git a/tests/unit_tests/x11/winspec.cpp b/tests/unit_tests/x11/winspec.cpp deleted file mode 100644 index d853e828..00000000 --- a/tests/unit_tests/x11/winspec.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "utils/string.cpp" -#include "x11/atoms.cpp" -#include "x11/connection.cpp" -#include "x11/winspec.hpp" -#include "x11/xutils.cpp" -#include "x11/xlib.cpp" - -int main() { - using namespace polybar; - - "cw_create"_test = [] { - connection& conn{configure_connection().create()}; - auto id = conn.generate_id(); - - // clang-format off - auto win = winspec(conn, id) - << cw_size(100, 200) - << cw_pos(10, -20) - << cw_border(9) - << cw_class(XCB_WINDOW_CLASS_INPUT_ONLY) - << cw_parent(0x000110a) - ; - // clang-format on - - expect(static_cast(win) == id); - - xcb_rectangle_t rect{static_cast(win)}; - expect(rect.width == 100); - expect(rect.height == 200); - expect(rect.x == 10); - expect(rect.y == -20); - }; -}