#include "test.h"
#include <panda/function_utils.h>
#include <panda/CallbackDispatcher.h>
using
Dispatcher = CallbackDispatcher<
int
(
int
)>;
using
Event = Dispatcher::Event;
#define TEST(name) TEST_CASE("callback dispatcher: " name, "[callback dispatcher]")
TEST(
"empty"
) {
Dispatcher d;
d(1);
REQUIRE(
true
);
}
TEST(
"synopsis"
) {
CallbackDispatcher<
void
()> d1;
string output;
function<
void
()> hello = [&]() { output +=
"Hello,"
; };
function<
void
()> world = [&]() { output +=
" World!"
; };
d1.add(hello);
d1.add(world);
d1();
assert
(output ==
"Hello, World!"
);
d1.
remove
(world);
d1();
assert
(output ==
"Hello, World!Hello,"
);
using
IntInt = CallbackDispatcher<
int
(
int
)>;
IntInt d2;
d2.add_event_listener([](IntInt::Event& event,
int
val) {
return
event.next(val).value_or(val) * 2;
});
d2.add_event_listener([](IntInt::Event&
,
int
val) {
return
val + 1;
});
d2.add_event_listener([](IntInt::Event&
,
int
) ->
int
{
throw
std::exception();
});
optional<
int
> result = d2(42);
assert
(result.value() == 86);
}
TEST(
"dispatcher void()"
) {
CallbackDispatcher<
void
()> d;
bool
called =
false
;
CallbackDispatcher<
void
()>::SimpleCallback f = [&](){called =
true
;};
d.add(f);
d();
REQUIRE(called);
}
TEST(
"simplest dispatcher"
) {
Dispatcher d;
function<panda::optional<
int
> (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);
REQUIRE(Tracer::copy_calls == 0);
REQUIRE(Tracer::move_calls == 1);
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<
void
(int16_t)> 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<S,
int
>::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<S,Dispatcher::Event&,
int
>::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<
void
,
int
>& 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<optional<
int
>, 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<panda::optional<
int
>(
int
)> f = d;
REQUIRE(f(10).value_or(0) == 20);
}
TEST(
"dispatcher 2 string calls"
) {
using
Dispatcher = CallbackDispatcher<
void
(string)>;
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<
void
()>;
Dispatcher d;
std::vector<
int
> 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<
int
>({1,2,3}));
}
TEST(
"front order"
) {
using
Dispatcher = CallbackDispatcher<
void
()>;
Dispatcher d;
std::vector<
int
> 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<
int
>({3,2,1}));
}
TEST(
"const ref arg move"
) {
Tracer::refresh();
CallbackDispatcher<
void
(
const
Tracer&)> 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<
void
(
int
)> d1;
CallbackDispatcher<
int
(
int
)> 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<
void
(
int
)> cb = [&](
int
) {
delete
d;
};
d->add(cb);
bool
called =
false
;
function<
void
(
int
)> check = [&](
int
) {
called =
true
;
};
d->add(check,
true
);
(*d)(10);
REQUIRE_FALSE(called);
}