// 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/eventqueue.h" #include "eventpp/utilities/anydata.h" #include #include #include #include namespace { template bool isWithinAnyData(const T & anyData, const void * address) { return (std::ptrdiff_t)address >= (std::ptrdiff_t)&anyData && (std::ptrdiff_t)address < (std::ptrdiff_t)&anyData + (std::ptrdiff_t)sizeof(T) ; } template bool isWithinAnyData(const T & anyData) { return isWithinAnyData(anyData, anyData.getAddress()); } template bool isLargeAnyData(const T & anyData) { return ! isWithinAnyData(anyData); } TEST_CASE("AnyData, maxSizeOf") { REQUIRE(eventpp::maxSizeOf< std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t >() == sizeof(std::uint64_t) ); REQUIRE(eventpp::maxSizeOf< std::uint64_t, std::uint32_t, std::uint8_t, std::uint16_t >() == sizeof(std::uint64_t) ); } struct LargeDataBase { std::array largeData; }; template bool isLargeDataBase() { return sizeof(T) >= sizeof(LargeDataBase); } struct DataUniquePtr { std::unique_ptr ptr; }; struct LargeDataUniquePtr : DataUniquePtr, LargeDataBase {}; TEMPLATE_TEST_CASE("AnyData, DataUniquePtr", "", DataUniquePtr, LargeDataUniquePtr) { using Type = TestType; using MyAnyData = eventpp::AnyData; Type data {}; data.ptr = std::unique_ptr(new std::string("Hello")); REQUIRE(data.ptr); MyAnyData anyData { std::move(data) }; REQUIRE(anyData.isType()); REQUIRE(! data.ptr); REQUIRE(isLargeAnyData(anyData) == isLargeDataBase()); REQUIRE(*anyData.get().ptr == "Hello"); REQUIRE(*anyData.get().ptr == "Hello"); } struct DataSharedPtr { std::shared_ptr ptr; }; struct LargeDataSharedPtr : DataSharedPtr, LargeDataBase {}; TEMPLATE_TEST_CASE("AnyData, DataSharedPtr", "", DataSharedPtr, LargeDataSharedPtr) { using Type = TestType; using MyAnyData = eventpp::AnyData; Type data {}; data.ptr = std::make_shared("Hello"); REQUIRE(data.ptr.use_count() == 1); SECTION("copy") { MyAnyData anyData { data }; REQUIRE(anyData.isType()); REQUIRE(data.ptr.use_count() == 2); REQUIRE(anyData.get().ptr.use_count() == 2); REQUIRE(isLargeAnyData(anyData) == isLargeDataBase()); REQUIRE(*anyData.get().ptr == "Hello"); REQUIRE(*anyData.get().ptr == "Hello"); *data.ptr = "world"; REQUIRE(*anyData.get().ptr == "world"); REQUIRE(*anyData.get().ptr == "world"); } SECTION("move") { MyAnyData anyData { std::move(data) }; REQUIRE(anyData.isType()); REQUIRE(data.ptr.use_count() == 0); REQUIRE(anyData.get().ptr.use_count() == 1); REQUIRE(isLargeAnyData(anyData) == isLargeDataBase()); REQUIRE(*anyData.get().ptr == "Hello"); REQUIRE(*anyData.get().ptr == "Hello"); } } struct LifeCounter { int ctors; }; struct DataLifeCounter { explicit DataLifeCounter(LifeCounter * counter) : counter(counter) { ++counter->ctors; } DataLifeCounter(const DataLifeCounter & other) : counter(other.counter) { ++counter->ctors; } DataLifeCounter(DataLifeCounter && other) : counter(other.counter) { ++counter->ctors; } ~DataLifeCounter() { --counter->ctors; } LifeCounter * counter; }; struct LargeDataLifeCounter : DataLifeCounter, LargeDataBase { explicit LargeDataLifeCounter(LifeCounter * counter) : DataLifeCounter(counter), LargeDataBase() { } }; TEMPLATE_TEST_CASE("AnyData, DataLifeCounter", "", DataLifeCounter, LargeDataLifeCounter) { using Type = TestType; using MyAnyData = eventpp::AnyData; LifeCounter lifeCounter {}; REQUIRE(lifeCounter.ctors == 0); { Type data { &lifeCounter }; REQUIRE(lifeCounter.ctors == 1); { MyAnyData anyData { data }; REQUIRE(anyData.isType()); REQUIRE(lifeCounter.ctors == 2); REQUIRE(isLargeAnyData(anyData) == isLargeDataBase()); } REQUIRE(lifeCounter.ctors == 1); } REQUIRE(lifeCounter.ctors == 0); } enum class EventType { mouse = 1, key = 2 }; struct Event { EventType type; explicit Event(const EventType type) : type(type) { } }; struct EventKey : Event { int key; explicit EventKey(const int key) : Event(EventType::key), key(key) { } }; struct EventMouse : Event { int x; int y; EventMouse(const int x, const int y) : Event(EventType::mouse), x(x), y(y) { } }; struct LargeEventKey : EventKey, LargeDataBase { explicit LargeEventKey(const int key) : EventKey(key), LargeDataBase() { } }; struct LargeEventMouse : EventMouse, LargeDataBase { explicit LargeEventMouse(const int x, const int y) : EventMouse(x, y), LargeDataBase() { } }; constexpr std::size_t eventMaxSize = eventpp::maxSizeOf< Event, EventKey, EventMouse, std::string >(); TEST_CASE("AnyData, use AnyData as callback argument") { using Data = eventpp::AnyData; eventpp::EventQueue queue; queue.appendListener(EventType::key, [](const Data & value) { REQUIRE(value.isType()); REQUIRE(value.get().type == EventType::key); REQUIRE(value.get().key == 5); }); queue.enqueue(EventType::key, EventKey(5)); queue.process(); } TEST_CASE("AnyData, use Event as callback argument") { using Data = eventpp::AnyData; eventpp::EventQueue queue; int expectedKey; int expectedX; int expectedY; queue.appendListener(EventType::key, [&expectedKey](const Event & event) { REQUIRE(event.type == EventType::key); REQUIRE(static_cast(event).key == expectedKey); }); queue.appendListener(EventType::mouse, [&expectedX, &expectedY](const Event & event) { REQUIRE(event.type == EventType::mouse); REQUIRE(static_cast(event).x == expectedX); REQUIRE(static_cast(event).y == expectedY); }); expectedKey = 5; queue.enqueue(EventType::key, EventKey(5)); queue.process(); expectedKey = 8; expectedX = 12345678; expectedY = 9876532; queue.enqueue(EventType::mouse, LargeEventMouse(12345678, 9876532)); queue.enqueue(EventType::key, LargeEventKey(8)); queue.process(); } } // unnamed namespace