test(util): add tests for SafeSignal

Add a fixture for writing tests that require interaction with Glib event
loop and a very basic test for SafeSignal.
This commit is contained in:
Aleksei Bavshin 2020-12-28 17:28:03 -08:00
parent 79883dbce4
commit 3e2197a82a
No known key found for this signature in database
GPG Key ID: 4F071603387A382A
3 changed files with 96 additions and 0 deletions

19
test/GlibTestsFixture.hpp Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <glibmm/main.h>
/**
* Minimal Glib application to be used for tests that require Glib main loop
*/
class GlibTestsFixture : public sigc::trackable {
public:
GlibTestsFixture() : main_loop_{Glib::MainLoop::create()} {}
void run(std::function<void()> fn) {
Glib::signal_idle().connect_once(fn);
main_loop_->run();
}
void quit() { main_loop_->quit(); }
protected:
Glib::RefPtr<Glib::MainLoop> main_loop_;
};

63
test/SafeSignal.cpp Normal file
View File

@ -0,0 +1,63 @@
#define CATCH_CONFIG_RUNNER
#include "util/SafeSignal.hpp"
#include <glibmm.h>
#include <catch2/catch.hpp>
#include <thread>
#include "GlibTestsFixture.hpp"
using namespace waybar;
/**
* Basic sanity test for SafeSignal:
* check that type deduction works, events are delivered and the order is right
* Running this with -fsanitize=thread should not fail
*/
TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal basic functionality", "[signal][thread][util]") {
const int NUM_EVENTS = 100;
int count = 0;
int last_value = 0;
SafeSignal<int, std::string> test_signal;
const auto main_tid = std::this_thread::get_id();
std::thread producer;
// timeout the test in 500ms
Glib::signal_timeout().connect_once([]() { throw std::runtime_error("Test timed out"); }, 500);
test_signal.connect([&](auto val, auto str) {
static_assert(std::is_same<int, decltype(val)>::value);
static_assert(std::is_same<std::string, decltype(str)>::value);
// check that we're in the same thread as the main loop
REQUIRE(std::this_thread::get_id() == main_tid);
// check event order
REQUIRE(val == last_value + 1);
last_value = val;
if (++count >= NUM_EVENTS) {
this->quit();
};
});
run([&]() {
// check that events from the same thread are delivered and processed synchronously
test_signal.emit(1, "test");
REQUIRE(count == 1);
// start another thread and generate events
producer = std::thread([&]() {
for (auto i = 2; i <= NUM_EVENTS; ++i) {
test_signal.emit(i, "test");
}
});
});
producer.join();
REQUIRE(count == NUM_EVENTS);
}
int main(int argc, char* argv[]) {
Glib::init();
return Catch::Session().run(argc, argv);
}

View File

@ -2,6 +2,7 @@ test_inc = include_directories('../include')
test_dep = [ test_dep = [
catch2, catch2,
fmt, fmt,
gtkmm,
jsoncpp, jsoncpp,
spdlog, spdlog,
] ]
@ -14,8 +15,21 @@ config_test = executable(
include_directories: test_inc, include_directories: test_inc,
) )
safesignal_test = executable(
'safesignal_test',
'SafeSignal.cpp',
dependencies: test_dep,
include_directories: test_inc,
)
test( test(
'Configuration test', 'Configuration test',
config_test, config_test,
workdir: meson.source_root(), workdir: meson.source_root(),
) )
test(
'SafeSignal test',
safesignal_test,
workdir: meson.source_root(),
)