// eventpp library // Copyright (C) 2018 Wang Qi (wqking) // Github: https://github.com/wqking/eventpp // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "test.h" #include "eventpp/hetercallbacklist.h" TEST_CASE("HeterCallbackList, empty") { using CL = eventpp::HeterCallbackList >; SECTION("empty") { CL callbackList; REQUIRE(callbackList.empty()); } SECTION("append") { CL callbackList; callbackList.append([](){}); REQUIRE(! callbackList.empty()); } SECTION("prepend") { CL callbackList; callbackList.prepend([](int){}); REQUIRE(! callbackList.empty()); } SECTION("remove") { CL callbackList; auto handle = callbackList.append([](){}); REQUIRE(! callbackList.empty()); callbackList.remove(handle); REQUIRE(callbackList.empty()); } } TEST_CASE("HeterCallbackList, append") { using CL = eventpp::HeterCallbackList >; CL callbackList; std::vector orderList(5); int order; callbackList.append([&orderList, &order]() { orderList[order++] = 1; }); callbackList.append([&orderList, &order]() { orderList[order++] = 2; }); callbackList.append([&orderList, &order](int) { orderList[order++] = 3; }); callbackList.append([&orderList, &order](int) { orderList[order++] = 4; }); callbackList.append([&orderList, &order](int) { orderList[order++] = 5; }); REQUIRE(orderList == std::vector{ 0, 0, 0, 0, 0 }); order = 0; callbackList(3); REQUIRE(orderList == std::vector{ 3, 4, 5, 0, 0 }); callbackList(); REQUIRE(orderList == std::vector{ 3, 4, 5, 1, 2 }); } TEST_CASE("HeterCallbackList, prepend") { using CL = eventpp::HeterCallbackList >; CL callbackList; std::vector orderList(5); int order; callbackList.prepend([&orderList, &order]() { orderList[order++] = 1; }); callbackList.prepend([&orderList, &order]() { orderList[order++] = 2; }); callbackList.prepend([&orderList, &order](int) { orderList[order++] = 3; }); callbackList.prepend([&orderList, &order](int) { orderList[order++] = 4; }); callbackList.prepend([&orderList, &order](int) { orderList[order++] = 5; }); REQUIRE(orderList == std::vector{ 0, 0, 0, 0, 0 }); order = 0; callbackList(3); REQUIRE(orderList == std::vector{ 5, 4, 3, 0, 0 }); callbackList(); REQUIRE(orderList == std::vector{ 5, 4, 3, 2, 1 }); } TEST_CASE("HeterCallbackList, insert") { using CL = eventpp::HeterCallbackList >; CL callbackList; std::vector orderList(5); int order; auto h1 = callbackList.append([&orderList, &order]() { orderList[order++] = 1; }); callbackList.insert([&orderList, &order]() { orderList[order++] = 2; }, h1); auto h2 = callbackList.append([&orderList, &order](int) { orderList[order++] = 3; }); callbackList.insert([&orderList, &order](int) { orderList[order++] = 4; }, h2); // Increase h2.index to trigger the check `if(before.index != PrototypeInfo::index)` in HeterCallbackList::insert // This works as if the callback is appended rather than inserted. ++h2.index; callbackList.insert([&orderList, &order](int) { orderList[order++] = 5; }, h2); REQUIRE(orderList == std::vector{ 0, 0, 0, 0, 0 }); order = 0; callbackList(3); REQUIRE(orderList == std::vector{ 4, 3, 5, 0, 0 }); callbackList(); REQUIRE(orderList == std::vector{ 4, 3, 5, 2, 1 }); } TEST_CASE("HeterCallbackList, remove") { using CL = eventpp::HeterCallbackList >; CL callbackList; std::vector dataList(5); auto h1 = callbackList.append([&dataList]() { ++dataList[0]; }); auto h2 = callbackList.append([&dataList]() { ++dataList[1]; }); auto h3 = callbackList.append([&dataList](int) { ++dataList[2]; }); auto h4 = callbackList.append([&dataList](int) { ++dataList[3]; }); auto h5 = callbackList.append([&dataList](int) { ++dataList[4]; }); REQUIRE(dataList == std::vector{ 0, 0, 0, 0, 0 }); // Remove non-exist handle REQUIRE(! callbackList.remove(decltype(h1)())); callbackList(3); REQUIRE(dataList == std::vector{ 0, 0, 1, 1, 1 }); callbackList(); REQUIRE(dataList == std::vector{ 1, 1, 1, 1, 1 }); REQUIRE(callbackList.remove(h2)); callbackList(3); callbackList(); REQUIRE(dataList == std::vector{ 2, 1, 2, 2, 2 }); // double remove, no effect REQUIRE(! callbackList.remove(h2)); callbackList(3); callbackList(); REQUIRE(dataList == std::vector{ 3, 1, 3, 3, 3 }); REQUIRE(callbackList.remove(h3)); callbackList(3); callbackList(); REQUIRE(dataList == std::vector{ 4, 1, 3, 4, 4 }); REQUIRE(callbackList.remove(h5)); callbackList(3); callbackList(); REQUIRE(dataList == std::vector{ 5, 1, 3, 5, 4 }); REQUIRE(callbackList.remove(h1)); callbackList(3); callbackList(); REQUIRE(dataList == std::vector{ 5, 1, 3, 6, 4 }); REQUIRE(callbackList.remove(h4)); callbackList(3); callbackList(); REQUIRE(dataList == std::vector{ 5, 1, 3, 6, 4 }); } TEST_CASE("HeterCallbackList, forEach") { using CL = eventpp::HeterCallbackList >; CL callbackList; callbackList.append([]() { return 1; }); callbackList.append([]() { return 2; }); callbackList.append([]() { return 3; }); callbackList.append([](int n) { return n + 5 + 0; }); callbackList.append([](int n) { return n + 5 + 1; }); callbackList.append([](int n) { return n + 5 + 2; }); int i = 1; callbackList.forEach([&i](auto callback) { REQUIRE(callback() == i); ++i; }); i = 0; callbackList.forEach([&i](const std::function & callback) { REQUIRE(callback(3) == 8 + i); ++i; }); } TEST_CASE("HeterCallbackList, forEachIf") { using CL = eventpp::HeterCallbackList >; CL callbackList; std::vector dataListNoArg(3); std::vector dataListWithArg(3); callbackList.append([&dataListNoArg]() { dataListNoArg[0] += 1; }); callbackList.append([&dataListNoArg]() { dataListNoArg[1] += 2; }); callbackList.append([&dataListNoArg]() { dataListNoArg[2] += 3; }); callbackList.append([&dataListWithArg](int n) { dataListWithArg[0] += n; }); callbackList.append([&dataListWithArg](int n) { dataListWithArg[1] += n * 2; }); callbackList.append([&dataListWithArg](int n) { dataListWithArg[2] += n * 3; }); REQUIRE(dataListNoArg == std::vector{ 0, 0, 0 }); REQUIRE(dataListWithArg == std::vector{ 0, 0, 0 }); int i = 0; bool result = callbackList.forEachIf([&i](const std::function & callback) -> bool { callback(); ++i; return i != 2; }); REQUIRE(! result); REQUIRE(dataListNoArg == std::vector{ 1, 2, 0 }); REQUIRE(dataListWithArg == std::vector{ 0, 0, 0 }); i = 0; result = callbackList.forEachIf([&i](const std::function & callback) -> bool { callback(3); ++i; return i != 1; }); REQUIRE(! result); REQUIRE(dataListNoArg == std::vector{ 1, 2, 0 }); REQUIRE(dataListWithArg == std::vector{ 3, 0, 0 }); } TEST_CASE("HeterCallbackList, invoke") { using CL = eventpp::HeterCallbackList >; CL callbackList; std::vector dataListNoArg(3); std::vector dataListWithArg(3); callbackList.append([&dataListNoArg]() { dataListNoArg[0] += 1; }); callbackList.append([&dataListNoArg]() { dataListNoArg[1] += 2; }); callbackList.append([&dataListNoArg]() { dataListNoArg[2] += 3; }); callbackList.append([&dataListWithArg](int n) { dataListWithArg[0] += n; }); callbackList.append([&dataListWithArg](int n) { dataListWithArg[1] += n * 2; }); callbackList.append([&dataListWithArg](int n) { dataListWithArg[2] += n * 3; }); REQUIRE(dataListNoArg == std::vector{ 0, 0, 0 }); REQUIRE(dataListWithArg == std::vector{ 0, 0, 0 }); callbackList(); REQUIRE(dataListNoArg == std::vector{ 1, 2, 3 }); REQUIRE(dataListWithArg == std::vector{ 0, 0, 0 }); callbackList(); REQUIRE(dataListNoArg == std::vector{ 2, 4, 6 }); REQUIRE(dataListWithArg == std::vector{ 0, 0, 0 }); callbackList(1); REQUIRE(dataListNoArg == std::vector{ 2, 4, 6 }); REQUIRE(dataListWithArg == std::vector{ 1, 2, 3 }); callbackList(2); REQUIRE(dataListNoArg == std::vector{ 2, 4, 6 }); REQUIRE(dataListWithArg == std::vector{ 3, 6, 9 }); } TEST_CASE("HeterCallbackList, prototype convert") { struct MyClass { MyClass(int) {} }; using CL = eventpp::HeterCallbackList >; CL callbackList; std::vector dataList(2); callbackList.append([&dataList](int) { ++dataList[0]; }); callbackList.append([&dataList](const MyClass &) { ++dataList[1]; }); REQUIRE(dataList == std::vector{ 0, 0 }); callbackList((int)5); REQUIRE(dataList == std::vector{ 1, 1 }); callbackList((char)5); REQUIRE(dataList == std::vector{ 2, 2 }); }