diff --git a/test/GlibTestsFixture.hpp b/test/GlibTestsFixture.hpp index ffe6fd31..a21c8e07 100644 --- a/test/GlibTestsFixture.hpp +++ b/test/GlibTestsFixture.hpp @@ -7,6 +7,11 @@ class GlibTestsFixture : public sigc::trackable { public: GlibTestsFixture() : main_loop_{Glib::MainLoop::create()} {} + void setTimeout(int timeout) { + Glib::signal_timeout().connect_once([]() { throw std::runtime_error("Test timed out"); }, + timeout); + } + void run(std::function fn) { Glib::signal_idle().connect_once(fn); main_loop_->run(); diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp index b07e9cab..2c67317b 100644 --- a/test/SafeSignal.cpp +++ b/test/SafeSignal.cpp @@ -5,10 +5,15 @@ #include #include +#include #include "GlibTestsFixture.hpp" using namespace waybar; + +template +using remove_cvref_t = typename std::remove_cv::type>::type; + /** * Basic sanity test for SafeSignal: * check that type deduction works, events are delivered and the order is right @@ -25,7 +30,7 @@ TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal basic functionality", "[signal][t std::thread producer; // timeout the test in 500ms - Glib::signal_timeout().connect_once([]() { throw std::runtime_error("Test timed out"); }, 500); + setTimeout(500); test_signal.connect([&](auto val, auto str) { static_assert(std::is_same::value); @@ -57,6 +62,83 @@ TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal basic functionality", "[signal][t REQUIRE(count == NUM_EVENTS); } +template +struct TestObject { + T value; + unsigned copied = 0; + unsigned moved = 0; + + TestObject(const T& v) : value(v){}; + ~TestObject() = default; + + TestObject(const TestObject& other) + : value(other.value), copied(other.copied + 1), moved(other.moved) {} + + TestObject(TestObject&& other) noexcept + : value(std::move(other.value)), + copied(std::exchange(other.copied, 0)), + moved(std::exchange(other.moved, 0) + 1) {} + + TestObject& operator=(const TestObject& other) { + value = other.value; + copied = other.copied + 1; + moved = other.moved; + return *this; + } + + TestObject& operator=(TestObject&& other) noexcept { + value = std::move(other.value); + copied = std::exchange(other.copied, 0); + moved = std::exchange(other.moved, 0) + 1; + return *this; + } + + bool operator==(T other) const { return value == other; } + operator T() const { return value; } +}; + +/* + * Check the number of copies/moves performed on the object passed through SafeSignal + */ +TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal copy/move counter", "[signal][thread][util]") { + const int NUM_EVENTS = 3; + int count = 0; + + SafeSignal> test_signal; + + std::thread producer; + + // timeout the test in 500ms + setTimeout(500); + + test_signal.connect([&](auto& val) { + static_assert(std::is_same, remove_cvref_t>::value); + + /* explicit move in the producer thread */ + REQUIRE(val.moved <= 1); + /* copy within the SafeSignal queuing code */ + REQUIRE(val.copied <= 1); + + if (++count >= NUM_EVENTS) { + this->quit(); + }; + }); + + run([&]() { + test_signal.emit(1); + REQUIRE(count == 1); + producer = std::thread([&]() { + for (auto i = 2; i <= NUM_EVENTS; ++i) { + TestObject t{i}; + // check that signal.emit accepts moved objects + test_signal.emit(std::move(t)); + } + }); + }); + producer.join(); + REQUIRE(count == NUM_EVENTS); +} + int main(int argc, char* argv[]) { Glib::init(); return Catch::Session().run(argc, argv);