#include "test.h" #include #include using Dispatcher = CallbackDispatcher; using Event = Dispatcher::Event; TEST_PREFIX("callback dispatcher: ", "[callback dispatcher]"); TEST("empty") { Dispatcher d; d(1); REQUIRE(true); } TEST("synopsis") { CallbackDispatcher d1; string output; function hello = [&]() { output += "Hello,"; }; function world = [&]() { output += " World!"; }; d1.add(hello); d1.add(world); d1(); // both callback are called in the same order they were added assert(output == "Hello, World!"); d1.remove(world); d1(); // only hello remains assert(output == "Hello, World!Hello,"); using IntInt = CallbackDispatcher; IntInt d2; d2.add_event_listener([](IntInt::Event& event, int val) { return event.next(val).value_or(val) * 2; // do what you want with other listeners result }); d2.add_event_listener([](IntInt::Event& /*event*/, int val) { return val + 1; // or event skip calling others }); d2.add_event_listener([](IntInt::Event& /*event*/, int /*val*/) -> int { throw std::exception(); // never called }); optional result = d2(42); // 2 * (42 + 1) == 86 assert(result.value() == 86); } TEST("dispatcher void()") { CallbackDispatcher d; bool called = false; CallbackDispatcher::SimpleCallback f = [&](){called = true;}; d.add(f); d(); REQUIRE(called); } TEST("simplest dispatcher") { Dispatcher d; function (Dispatcher::Event&, int)> cb = [](Event& e, int a) -> int { return 1 + e.next(a).value_or(0); }; d.add_event_listener(cb); d.add_event_listener([](Event& e, int a) -> int { return a + e.next(a).value_or(0); }); REQUIRE(d(2).value_or(0) == 3); } TEST("remove callback") { Dispatcher d; d.add_event_listener([](Event& e, int a) -> int { return 1 + e.next(a).value_or(0); }); Dispatcher::Callback c = [](Event& e, int a) -> int { return a + e.next(a).value_or(0); }; d.add_event_listener(c); REQUIRE(d(2).value_or(0) == 3); d.remove(c); REQUIRE(d(2).value_or(0) == 1); } TEST("remove_all in process") { Dispatcher d; d.add_event_listener([&](Event& e, int a) -> int { d.remove_all(); return 1 + e.next(a).value_or(0); }); d.add_event_listener([](Event&, int) -> int { return 2; }); REQUIRE(d(2).value_or(0) == 1); } TEST("copy ellision") { Dispatcher d; Tracer::refresh(); { Dispatcher::Callback cb = Tracer(14); d.add_event_listener(cb); REQUIRE(d(2).value_or(0) == 16); d.remove(cb); REQUIRE(d(2).value_or(0) == 0); } REQUIRE(d(2).value_or(0) == 0); REQUIRE(Tracer::ctor_calls == 1); // 1 for temporary object Tracer(10); REQUIRE(Tracer::copy_calls == 0); REQUIRE(Tracer::move_calls == 1); // 1 construction from tmp object function f = Tracer(10); REQUIRE(Tracer::dtor_calls == 2); } TEST("callback without event") { Dispatcher d; bool called = false; Dispatcher::SimpleCallback s = [&](int) { called = true; }; d.add(s); REQUIRE(d(2).value_or(42) == 42); REQUIRE(called); } TEST("remove callback without event") { Dispatcher d; bool called = false; Dispatcher::SimpleCallback s = [&](int) { called = true; }; d.add(s); REQUIRE(d(2).value_or(42) == 42); REQUIRE(called); d.remove(s); called = false; REQUIRE(d(2).value_or(42) == 42); REQUIRE(!called); } TEST("remove callback with compatible type") { Dispatcher d; bool called = false; function s = [&](int) { called = true; }; d.add(s); REQUIRE(d(2).value_or(42) == 42); REQUIRE(called); d.remove(s); called = false; REQUIRE(d(2).value_or(42) == 42); REQUIRE(!called); } TEST("remove callback comparable functor") { Dispatcher d; static bool called; struct S { void operator()(int) { called = true; } bool operator ==(const S&) const { return true; } }; static_assert(panda::has_call_operator::value, "S shuld be callable, it can be wrong implementation of panda::has_call_operator or a compiler error"); S src; called = false; Dispatcher::SimpleCallback s = src; d.add(s); CHECK(d(2).value_or(42) == 42); CHECK(called); d.remove(s); called = false; CHECK(d(2).value_or(42) == 42); CHECK(!called); called = false; d.add(s); CHECK(d(2).value_or(42) == 42); CHECK(called); d.remove_object(S(src)); called = false; CHECK(d(2).value_or(42) == 42); CHECK(!called); } TEST("remove callback comparable full functor") { Dispatcher d; static bool called; struct S { int operator()(Dispatcher::Event& e, int a) { called = true; e.next(a); return a + 10; } bool operator ==(const S&) const { return true; } }; static_assert(panda::has_call_operator::value, "S shuld be callable, it can be wrong implementation of panda::has_call_operator or a compiler error"); S src; called = false; Dispatcher::Callback s = src; d.add_event_listener(s); CHECK(d(2).value_or(42) == 12); CHECK(called); called = false; SECTION("standatd remove") { d.remove(s); } SECTION("fast remove") { d.remove_object(S(src)); } CHECK(d(2).value_or(42) == 42); CHECK(!called); } TEST("remove simple callback self lambda") { Dispatcher d; static bool called; auto l = [&](panda::Ifunction& self, int) { d.remove(self); called = true; }; Dispatcher::SimpleCallback s = l; d.add(s); CHECK(d(2).value_or(42) == 42); CHECK(called); called = false; CHECK(d(2).value_or(42) == 42); CHECK(!called); } TEST("remove callback self lambda") { using panda::optional; Dispatcher d; static bool called; auto l = [&](panda::Ifunction, Dispatcher::Event&, int>& self, Dispatcher::Event&, int a) { d.remove(self); called = true; return a + 10; }; Dispatcher::Callback s = l; d.add_event_listener(s); CHECK(d(2).value_or(42) == 12); CHECK(called); called = false; CHECK(d(2).value_or(42) == 42); CHECK(!called); } TEST("dispatcher to function conversion") { Dispatcher d; d.add_event_listener([](Dispatcher::Event&, int a){return a*2;}); function(int)> f = d; REQUIRE(f(10).value_or(0) == 20); } TEST("dispatcher 2 string calls") { using Dispatcher = CallbackDispatcher; Dispatcher d; d.add([](string s) {CHECK(s == "value");}); d.add([](string s) {CHECK(s == "value");}); d(string("value")); string s = "value"; d(s); } TEST("normal(back) order") { using Dispatcher = CallbackDispatcher; Dispatcher d; std::vector res; d.add([&]{ res.push_back(1); }); d.add([&]{ res.push_back(2); }); d.add_event_listener([&](Dispatcher::Event& e){ res.push_back(3); e.next(); }); d(); REQUIRE(res == std::vector({1,2,3})); } TEST("front order") { using Dispatcher = CallbackDispatcher; Dispatcher d; std::vector res; d.prepend_event_listener([&](Dispatcher::Event& e){ res.push_back(1); e.next(); }); d.add([&]{ res.push_back(2); }, false); d.prepend([&]{ res.push_back(3); }); d(); REQUIRE(res == std::vector({3,2,1})); } TEST("const ref arg move") { Tracer::refresh(); CallbackDispatcher d; SECTION("many listenees") { Tracer::refresh(); d.add([](auto&&...){}); d.add([](auto&&...){}); d.add([](auto&&...){}); } SECTION("no listeners"){} d(Tracer()); CHECK(Tracer::ctor_total() == 1); } TEST("lambda self reference auto...") { CallbackDispatcher d1; CallbackDispatcher d2; auto l1 = [](auto...args) -> void { static_assert(sizeof...(args) == 1, "auto... resolved as without SELF"); }; auto l2 = [](auto&&...args) -> void { static_assert(sizeof...(args) == 1, "auto... resolved as without SELF"); }; auto l3 = [](auto&&...args) { REQUIRE(sizeof...(args) == 1); }; d1.add(l1); d1.add(l2); d1.add(l3); d2.add(l1); d2.add(l2); d2.add(l3); auto l1a = [](auto&, auto...args) -> int { static_assert(sizeof...(args) == 1, "auto... resolved as without SELF"); return 1; }; auto l2a = [](auto&&...args) -> int { static_assert(sizeof...(args) == 2, "auto... resolved as without SELF"); return 2; }; auto l3a = [](auto&&...args) { REQUIRE(sizeof...(args) == 2); return 3; }; d2.add_event_listener(l1a); d2.add_event_listener(l2a); d2.add_event_listener(l3a); } TEST("killing dispatcher") { Dispatcher* d = new Dispatcher; function cb = [&](int) { delete d; }; d->add(cb); bool called = false; function check = [&](int) { called = true; }; d->add(check, true); (*d)(10); REQUIRE_FALSE(called); }