refactor(tests): Migrate to googletest

googletest (gtest) is more feature rich than the current implementation
which only provides expect() which is basically an assertion. It is also
quite intuitive to use, this can be seen in the rewrite of the
command_line test where EXPECT_THROW replaces a whole try-catch block.

I have also moved the source files the test depend on to be linked in
CMakeLists.txt instead of including them directly because include .cpp
files is bad.

The two x11 tests were removed because they were written two years ago
and a lot of the things they depend on, don't actually exist anymore in
polybar (I think we switched to xpp after those tests were written)

Tests are now compiled with the gcov lib which can be used to provide
test coverage in a second step
This commit is contained in:
patrick96 2018-04-07 22:16:55 +02:00 committed by Patrick Ziegler
parent 1d20df00e8
commit c865add821
12 changed files with 185 additions and 240 deletions

View File

@ -35,6 +35,8 @@ addons:
- python-xcbgen
- xcb-proto
- xutils-dev
- libgtest0
- libgtest-dev
env:
global:

View File

@ -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()

View File

@ -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})

View File

@ -9,6 +9,7 @@
#include <cstdio>
#include <cstdlib>
#include <gtest/gtest.h>
#define expect(...) \
(void)((__VA_ARGS__) || (expect_fail__(#__VA_ARGS__, __FILE__, __LINE__), 0))

View File

@ -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 {
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{
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
};
"has_short"_test = [&] {
auto cli = command_line::parser::make("cmd", get_opts());
command_line::parser::make_type cli;
};
TEST_F(CommandLine, hasShort) {
cli->process_input(string_util::split("-f", ' '));
expect(cli->has("flag"));
expect(!cli->has("option"));
EXPECT_TRUE(cli->has("flag"));
EXPECT_FALSE(cli->has("option"));
cli = command_line::parser::make("cmd", get_opts());;
set_cli();
cli->process_input(string_util::split("-f -o foo", ' '));
expect(cli->has("flag"));
expect(cli->has("option"));
EXPECT_TRUE(cli->has("flag"));
EXPECT_TRUE(cli->has("option"));
cli = command_line::parser::make("cmd", get_opts());;
set_cli();
cli->process_input(string_util::split("-o baz", ' '));
expect(!cli->has("flag"));
expect(cli->has("option"));
};
EXPECT_FALSE(cli->has("flag"));
EXPECT_TRUE(cli->has("option"));
}
"has_long"_test = [&] {
auto cli = command_line::parser::make("cmd", get_opts());;
TEST_F(CommandLine, hasLong) {
cli->process_input(string_util::split("--flag", ' '));
expect(cli->has("flag"));
expect(!cli->has("option"));
EXPECT_TRUE(cli->has("flag"));
EXPECT_FALSE(cli->has("option"));
cli = command_line::parser::make("cmd", get_opts());;
set_cli();
cli->process_input(string_util::split("--flag --option=foo", ' '));
expect(cli->has("flag"));
expect(cli->has("option"));
EXPECT_TRUE(cli->has("flag"));
EXPECT_TRUE(cli->has("option"));
cli = command_line::parser::make("cmd", get_opts());;
set_cli();
cli->process_input(string_util::split("--option=foo --flag", ' '));
expect(cli->has("flag"));
expect(cli->has("option"));
EXPECT_TRUE(cli->has("flag"));
EXPECT_TRUE(cli->has("option"));
cli = command_line::parser::make("cmd", get_opts());;
set_cli();
cli->process_input(string_util::split("--option=baz", ' '));
expect(!cli->has("flag"));
expect(cli->has("option"));
};
EXPECT_FALSE(cli->has("flag"));
EXPECT_TRUE(cli->has("option"));
}
"compare"_test = [&] {
auto cli = command_line::parser::make("cmd", get_opts());;
TEST_F(CommandLine, compare) {
cli->process_input(string_util::split("-o baz", ' '));
expect(cli->compare("option", "baz"));
EXPECT_TRUE(cli->compare("option", "baz"));
cli = command_line::parser::make("cmd", get_opts());;
set_cli();
cli->process_input(string_util::split("--option=foo", ' '));
expect(cli->compare("option", "foo"));
};
EXPECT_TRUE(cli->compare("option", "foo"));
}
"get"_test = [&] {
auto cli = command_line::parser::make("cmd", get_opts());;
TEST_F(CommandLine, get) {
cli->process_input(string_util::split("--option=baz", ' '));
expect("baz" == cli->get("option"));
EXPECT_EQ("baz", cli->get("option"));
cli = command_line::parser::make("cmd", get_opts());;
set_cli();
cli->process_input(string_util::split("--option=foo", ' '));
expect("foo" == cli->get("option"));
};
EXPECT_EQ("foo", cli->get("option"));
}
"missing_value"_test = [&] {
TEST_F(CommandLine, missingValue) {
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);
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);
}
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 = [&] {
TEST_F(CommandLine, invalidValue) {
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);
EXPECT_THROW(cli->process_input(input1), command_line::value_error);
set_cli();
EXPECT_THROW(cli->process_input(input2), command_line::value_error);
}
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 = [&] {
TEST_F(CommandLine, unrecognized) {
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);
};
EXPECT_THROW(cli->process_input(input1), command_line::argument_error);
set_cli();
EXPECT_THROW(cli->process_input(input2), command_line::argument_error);
}

View File

@ -1,14 +1,9 @@
#include <iomanip>
#include <iostream>
#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;

View File

@ -1,3 +1,4 @@
#include "common/test.hpp"
#include "utils/math.hpp"
int main() {

View File

@ -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 = [] {
TEST(Memory, makeMallocPtr) {
auto ptr = memory_util::make_malloc_ptr<mytype>();
expect(sizeof(mytype*) == sizeof(ptr.get()));
EXPECT_EQ(sizeof(mytype*), sizeof(ptr.get()));
ptr.reset();
expect(ptr.get() == nullptr);
};
EXPECT_EQ(nullptr, ptr.get());
}
"countof"_test = [] {
TEST(Memory, countof) {
mytype A[3]{{}, {}, {}};
mytype B[8]{{}, {}, {}, {}, {}, {}, {}, {}};
expect(memory_util::countof(A) == size_t{3});
expect(memory_util::countof(B) == size_t{8});
};
EXPECT_EQ(memory_util::countof(A), size_t{3});
EXPECT_EQ(memory_util::countof(B), size_t{8});
}

View File

@ -1,3 +1,4 @@
#include "common/test.hpp"
#include "utils/scope.hpp"
int main() {

View File

@ -1,6 +1,7 @@
#include <iomanip>
#include "utils/string.cpp"
#include "common/test.hpp"
#include "utils/string.hpp"
int main() {
using namespace polybar;

View File

@ -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<connection&>()};
expect(conn.id(static_cast<xcb_window_t>(0x12345678)) == "0x12345678");
};
}

View File

@ -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<connection&>()};
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<xcb_window_t>(win) == id);
xcb_rectangle_t rect{static_cast<xcb_rectangle_t>(win)};
expect(rect.width == 100);
expect(rect.height == 200);
expect(rect.x == 10);
expect(rect.y == -20);
};
}