diff --git a/UNITTESTS/CMakeLists.txt b/UNITTESTS/CMakeLists.txt index bb00da96e3..469cb320a6 100644 --- a/UNITTESTS/CMakeLists.txt +++ b/UNITTESTS/CMakeLists.txt @@ -58,3 +58,5 @@ if (VALGRIND) endif(VALGRIND) add_subdirectory(stubs) +add_subdirectory(fakes) + diff --git a/UNITTESTS/fakes/CMakeLists.txt b/UNITTESTS/fakes/CMakeLists.txt new file mode 100644 index 0000000000..1e50c44a9e --- /dev/null +++ b/UNITTESTS/fakes/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright (c) 2020 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory(events) diff --git a/UNITTESTS/fakes/events/CMakeLists.txt b/UNITTESTS/fakes/events/CMakeLists.txt new file mode 100644 index 0000000000..e50f30ac10 --- /dev/null +++ b/UNITTESTS/fakes/events/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2020 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +add_library(mbed-fakes-event-queue) + +target_sources(mbed-fakes-event-queue + PRIVATE + events/EventQueue.cpp +) + +target_include_directories(mbed-fakes-event-queue + PUBLIC + . +) + +target_link_libraries(mbed-fakes-event-queue + PRIVATE + mbed-headers + gcov +) diff --git a/UNITTESTS/fakes/events/events/EventQueue.cpp b/UNITTESTS/fakes/events/events/EventQueue.cpp new file mode 100644 index 0000000000..4f937c240c --- /dev/null +++ b/UNITTESTS/fakes/events/events/EventQueue.cpp @@ -0,0 +1,120 @@ +/* mbed Microcontroller Library + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "EventQueue.h" + +namespace events { + +handle_t EventQueue::call_handler(function_t handler) +{ + return call_handler_in(0, handler); +} + +handle_t EventQueue::call_handler_in(tick_t ms, function_t handler) +{ + _handler_id++; + + _handlers.push_back( + internal_event{ + std::unique_ptr(new function_t(handler)), + _now + ms, + _handler_id + } + ); + + return _handler_id; +} + +bool EventQueue::cancel_handler(handle_t handle) +{ + if (!handle) { + return false; + } + + auto found = std::remove_if( + _handlers.begin(), + _handlers.end(), + [handle](internal_event& element) -> bool { + return (handle == element.handle); + } + ); + + if (found != _handlers.end()) { + _handlers.erase( + found, + _handlers.end() + ); + return true; + } + + return false; +} + +void EventQueue::process_events(tick_t duration_ms) +{ + // execute all events during the duration + for (uint64_t i = 0; i < duration_ms; ++i) { + process_events(); + _now++; + } + + // last round to execute immediate events + process_events(); +} + +void EventQueue::process_events() { + while (true) { + if (_handlers.empty()) { + return; + } + + /* to guarantee order we only dispatch one tick at a time*/ + auto smallest = std::min_element( + _handlers.begin(), + _handlers.end(), + [](internal_event& element, internal_event& smallest){ + return (element.tick < smallest.tick); + } + ); + tick_t earliest_tick = smallest->tick; + + /* stop if all elements happen later */ + if (earliest_tick > _now) { + return; + } + + /* dispatch all handlers that happen at this time */ + auto found = std::remove_if( + _handlers.begin(), + _handlers.end(), + [earliest_tick](internal_event& element) -> bool { + if (earliest_tick >= element.tick) { + (*(element.handler))(); + return true; + } else { + return false; + } + } + ); + + if (found != _handlers.end()) { + _handlers.erase(found, _handlers.end()); + } + } +} + +} diff --git a/UNITTESTS/fakes/events/events/EventQueue.h b/UNITTESTS/fakes/events/events/EventQueue.h new file mode 100644 index 0000000000..6958298654 --- /dev/null +++ b/UNITTESTS/fakes/events/events/EventQueue.h @@ -0,0 +1,137 @@ +/* mbed Microcontroller Library + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef EVENTQUEUE_FAKE_H +#define EVENTQUEUE_FAKE_H + +#include +#include +#include +#include +#include +#include "events/EventQueue.h" +#include +#include + +namespace events { + +typedef int handle_t; +typedef std::function function_t; +typedef unsigned tick_t; + +class EventQueue { + using duration = std::chrono::duration; + +public: + EventQueue(unsigned size = 0, unsigned char *buffer = NULL) { delete buffer; }; + + ~EventQueue() { }; + + /** This will advence time by given amount of milliseonds and then dispatch all events that were set to happen in that time. + * + * @param ms number of miliseconds to advance time + */ + void dispatch(int milliseconds = -1) { + if (milliseconds > 0) { + process_events(milliseconds); + } else { + _now = (tick_t)-1; + process_events(); + _now = 0; + } + }; + + tick_t tick() { + return _now; + }; + + bool cancel(handle_t id) { + return cancel_handler(id); + }; + + /** Get number of events in queue. + * + * @return Number of events waiting in the queue. + */ + size_t size() const { + return _handlers.size(); + } + + template + handle_t call(F f, ArgTs... args) { + return call_handler( + [f, args = mstd::make_tuple(args...)]() { + mstd::apply(f, args); + } + ); + } + + template + handle_t call_in(duration ms, F f, ArgTs... args) { + return call_handler_in( + ms.count(), + [f, args = mstd::make_tuple(args...)]() { + mstd::apply(f, args); + } + ); + } + + template + int call(T *obj, R(T::*method)(ArgTs...), ArgTs... args) { + return call_handler( + [obj, method, args = mstd::make_tuple(args...)]() { + mstd::apply(method, obj, args); + } + ); + } + + template + int call_in(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) { + return call_handler_in( + ms.count(), + [obj, method, args = mstd::make_tuple(args...)]() { + mstd::apply(method, obj, args); + } + ); + } + +private: + handle_t call_handler(function_t handler); + + handle_t call_handler_in(tick_t ms, function_t handler); + + bool cancel_handler(handle_t handle); + + void process_events(tick_t duration_ms); + + void process_events(); + +private: + struct internal_event { + std::unique_ptr handler; + tick_t tick; + handle_t handle; + }; + + std::vector _handlers; + tick_t _now = 0; + handle_t _handler_id = 0; +}; + +} + +#endif //EVENTQUEUE_FAKE_H