| /* |
| Copyright (c) 2018-2019 Nokia. |
| |
| 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 <memory> |
| #include <sys/epoll.h> |
| #include <gmock/gmock.h> |
| #include "private/filedescriptor.hpp" |
| #include "private/engineimpl.hpp" |
| #include "private/tst/systemmock.hpp" |
| |
| using namespace shareddatalayer; |
| using namespace shareddatalayer::tst; |
| using namespace testing; |
| |
| namespace |
| { |
| class EventHandlerMock |
| { |
| public: |
| MOCK_METHOD1(handleEvents, void(unsigned int events)); |
| }; |
| |
| class EngineImplTest: public testing::Test |
| { |
| public: |
| const int fd1; |
| const int fd2; |
| const int epfd; |
| NiceMock<SystemMock> systemMock; |
| std::shared_ptr<EngineImpl> services; |
| EventHandlerMock eventHandlerMock1; |
| EventHandlerMock eventHandlerMock2; |
| |
| EngineImplTest(): fd1(100), fd2(200), epfd(300) |
| { |
| EXPECT_CALL(systemMock, epoll_create1(EPOLL_CLOEXEC)) |
| .Times(1) |
| .WillOnce(Return(epfd)); |
| services.reset(new EngineImpl(systemMock)); |
| Mock::VerifyAndClear(&systemMock); |
| } |
| |
| ~EngineImplTest() |
| { |
| stopEngineImpl(); |
| } |
| |
| void addMonitoredFD(int fd, unsigned int events) |
| { |
| services->addMonitoredFD(fd, events, [] (unsigned int) { }); |
| } |
| |
| void addMonitoredFD(int fd, unsigned int events, EventHandlerMock& eventHandlerMock) |
| { |
| services->addMonitoredFD(fd, events, std::bind(&EventHandlerMock::handleEvents, &eventHandlerMock, std::placeholders::_1)); |
| } |
| |
| void modifyMonitoredFD(int fd, unsigned int events) |
| { |
| services->modifyMonitoredFD(fd, events); |
| } |
| |
| void deleteMonitoredFD(int fd) |
| { |
| services->deleteMonitoredFD(fd); |
| } |
| |
| void expectEpollCtl(int epfd, int op, int fd, unsigned int events) |
| { |
| EXPECT_CALL(systemMock, epoll_ctl(epfd, op, fd, NotNull())) |
| .Times(1) |
| .WillOnce(Invoke([fd, events] (int, int, int, epoll_event* event) |
| { |
| epoll_data data = { }; |
| data.fd = fd; |
| EXPECT_EQ(data.fd, event->data.fd); |
| EXPECT_EQ(data.u64, event->data.u64); |
| EXPECT_EQ(data.ptr, event->data.ptr); |
| EXPECT_EQ(events, event->events); |
| })); |
| } |
| |
| void expectAtLeastOneEpollCtl(int epfd, int op, int fd, unsigned int events) |
| { |
| EXPECT_CALL(systemMock, epoll_ctl(epfd, op, fd, NotNull())) |
| .Times(AtLeast(1)) |
| .WillOnce(Invoke([fd, events] (int, int, int, epoll_event* event) |
| { |
| EXPECT_EQ(fd, event->data.fd); |
| EXPECT_EQ(events, event->events); |
| })); |
| } |
| |
| void stopEngineImpl() |
| { |
| if (!services) |
| return; |
| |
| EXPECT_CALL(systemMock, close(epfd)).Times(1); |
| services.reset(); |
| Mock::VerifyAndClear(&systemMock); |
| } |
| }; |
| |
| using EngineImplDeathTest = EngineImplTest; |
| } |
| |
| TEST_F(EngineImplTest, HandleEventsWithoutAnyAddedFDsDoesNothing) |
| { |
| EXPECT_CALL(systemMock, epoll_wait(_, _, _, _)) |
| .Times(0); |
| services->handleEvents(); |
| } |
| |
| TEST_F(EngineImplTest, FDReturnsTheEpollFD) |
| { |
| EXPECT_EQ(epfd, services->fd()); |
| } |
| |
| TEST_F(EngineImplTest, AddingFDAddsTheFDToEpoll) |
| { |
| expectEpollCtl(epfd, EPOLL_CTL_ADD, fd1, Engine::EVENT_IN); |
| addMonitoredFD(fd1, Engine::EVENT_IN); |
| } |
| |
| TEST_F(EngineImplTest, AddingFileDescriptorSetsAtCloseCallback) |
| { |
| FileDescriptor fd(systemMock, fd1); |
| expectEpollCtl(epfd, EPOLL_CTL_ADD, fd1, Engine::EVENT_IN); |
| services->addMonitoredFD(fd, Engine::EVENT_IN, Engine::EventHandler()); |
| |
| InSequence dummy; |
| EXPECT_CALL(systemMock, epoll_ctl(epfd, EPOLL_CTL_DEL, fd1, nullptr)) |
| .Times(1); |
| EXPECT_CALL(systemMock, close(fd1)).Times(1); |
| } |
| |
| TEST_F(EngineImplDeathTest, AddingAlreadyAddedFDCallsSHAREDDATALAYER_ABORT) |
| { |
| expectAtLeastOneEpollCtl(epfd, EPOLL_CTL_ADD, fd1, Engine::EVENT_IN); |
| addMonitoredFD(fd1, Engine::EVENT_IN); |
| EXPECT_EXIT(addMonitoredFD(fd1, Engine::EVENT_IN), |
| KilledBySignal(SIGABRT), "ABORT.*engineimpl\\.cpp"); |
| } |
| |
| TEST_F(EngineImplTest, ModifyingFDModifiesTheFDInEpoll) |
| { |
| addMonitoredFD(fd1, Engine::EVENT_IN); |
| expectEpollCtl(epfd, EPOLL_CTL_MOD, fd1, Engine::EVENT_OUT); |
| modifyMonitoredFD(fd1, Engine::EVENT_OUT); |
| } |
| |
| TEST_F(EngineImplDeathTest, ModifyingNonExistingFDCallsSHAREDDATALAYER_ABORT) |
| { |
| EXPECT_EXIT(modifyMonitoredFD(fd1, 0U), |
| KilledBySignal(SIGABRT), "ABORT.*engineimpl\\.cpp"); |
| } |
| |
| TEST_F(EngineImplDeathTest, DellingFDDelsTheFDFromEpollAndFromTheMap) |
| { |
| addMonitoredFD(fd1, Engine::EVENT_IN); |
| EXPECT_CALL(systemMock, epoll_ctl(epfd, EPOLL_CTL_DEL, fd1, nullptr)) |
| .Times(1); |
| deleteMonitoredFD(fd1); |
| EXPECT_EXIT(modifyMonitoredFD(fd1, 0U), |
| KilledBySignal(SIGABRT), "ABORT.*engineimpl\\.cpp"); |
| } |
| |
| TEST_F(EngineImplDeathTest, DellingNonExistingFDCallsSHAREDDATALAYER_ABORT) |
| { |
| EXPECT_EXIT(deleteMonitoredFD(fd1), |
| KilledBySignal(SIGABRT), "ABORT.*engineimpl\\.cpp"); |
| } |
| |
| TEST_F(EngineImplTest, HandleEventsCallsAddedEventHandlersAccordingToEpollReturnValue) |
| { |
| addMonitoredFD(fd1, Engine::EVENT_IN, eventHandlerMock1); |
| addMonitoredFD(fd2, Engine::EVENT_IN, eventHandlerMock2); |
| InSequence dummy; |
| EXPECT_CALL(systemMock, epoll_wait(epfd, NotNull(), 2, 0)) |
| .Times(1) |
| .WillOnce(Invoke([this] (int, epoll_event* events, int, int) -> int |
| { |
| events[0].events = EPOLLIN; |
| events[0].data.fd = fd1; |
| events[1].events = EPOLLOUT; |
| events[1].data.fd = fd2; |
| return 2; |
| })); |
| EXPECT_CALL(eventHandlerMock1, handleEvents(Engine::EVENT_IN)) |
| .Times(1); |
| EXPECT_CALL(eventHandlerMock2, handleEvents(Engine::EVENT_OUT)) |
| .Times(1); |
| services->handleEvents(); |
| } |
| |
| TEST_F(EngineImplTest, PendingEventsOfDeletedFileDescriptorAreForgotten) |
| { |
| addMonitoredFD(fd1, Engine::EVENT_IN, eventHandlerMock1); |
| addMonitoredFD(fd2, Engine::EVENT_IN, eventHandlerMock2); |
| InSequence dummy; |
| EXPECT_CALL(eventHandlerMock2, handleEvents(_)) |
| .Times(0); |
| EXPECT_CALL(systemMock, epoll_wait(epfd, NotNull(), 2, 0)) |
| .Times(1) |
| .WillOnce(Invoke([this] (int, epoll_event* events, int, int) -> int |
| { |
| events[0].events = EPOLLIN; |
| events[0].data.fd = fd1; |
| events[1].events = EPOLLIN; |
| events[1].data.fd = fd2; |
| return 2; |
| })); |
| EXPECT_CALL(eventHandlerMock1, handleEvents(_)) |
| .Times(1) |
| .WillOnce(Invoke([this](unsigned int) |
| { |
| deleteMonitoredFD(fd2); |
| addMonitoredFD(fd2, Engine::EVENT_IN, eventHandlerMock2); |
| })); |
| services->handleEvents(); |
| } |