Add first version
[ric-plt/sdl.git] / tst / timerfd_test.cpp
diff --git a/tst/timerfd_test.cpp b/tst/timerfd_test.cpp
new file mode 100644 (file)
index 0000000..7c90846
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+   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 <type_traits>
+#include <memory>
+#include <boost/optional.hpp>
+#include <sys/timerfd.h>
+#include <gmock/gmock.h>
+#include "private/timerfd.hpp"
+#include "private/tst/systemmock.hpp"
+#include "private/tst/enginemock.hpp"
+
+using namespace shareddatalayer;
+using namespace shareddatalayer::tst;
+using namespace testing;
+
+namespace
+{
+    class TimerFDTest: public testing::Test
+    {
+    public:
+        const int tfd;
+        NiceMock<SystemMock> systemMock;
+        EngineMock engineMock;
+        std::unique_ptr<TimerFD> timerFD;
+        std::unique_ptr<Timer> timer1;
+        std::unique_ptr<Timer> timer2;
+        Engine::EventHandler savedEventHandler;
+
+        TimerFDTest(): tfd(123)
+        {
+            InSequence dummy;
+            EXPECT_CALL(systemMock, timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC))
+                .Times(1)
+                .WillOnce(Return(tfd));
+            EXPECT_CALL(engineMock, addMonitoredFD(Matcher<FileDescriptor&>(_), Engine::EVENT_IN, _))
+                .Times(1)
+                .WillOnce(Invoke([this] (FileDescriptor& fd, unsigned int, const Engine::EventHandler& eh)
+                                 {
+                                     EXPECT_EQ(tfd, static_cast<int>(fd));
+                                     savedEventHandler = eh;
+                                 }));
+            timerFD.reset(new TimerFD(systemMock, engineMock));
+
+            Mock::VerifyAndClear(&systemMock);
+            Mock::VerifyAndClear(&engineMock);
+
+            EXPECT_CALL(engineMock, armTimer(_, _, _))
+                 .Times(AnyNumber())
+                 .WillRepeatedly(Invoke([this] (Timer& timer, const Timer::Duration& duration, const Timer::Callback& cb)
+                      {
+                          timerFD.get()->arm(timer, duration, cb);
+                      }));
+
+            EXPECT_CALL(engineMock, disarmTimer(_))
+                 .Times(AnyNumber())
+                 .WillRepeatedly(Invoke([this](const Timer& timer)
+                     {
+                         timerFD.get()->disarm(timer);
+                     }));
+
+            ON_CALL(systemMock, time_since_epoch())
+                .WillByDefault(Return(std::chrono::steady_clock::duration(0)));
+
+            timer1.reset(new Timer(engineMock));
+            timer2.reset(new Timer(engineMock));
+        }
+
+        void expectSetTime(int seconds, int nanoseconds)
+        {
+            EXPECT_CALL(systemMock, timerfd_settime(tfd, TFD_TIMER_ABSTIME, NotNull(), nullptr))
+                .Times(1)
+                .WillOnce(Invoke([seconds, nanoseconds] (int, int, const itimerspec* new_value, itimerspec*)
+                                 {
+                                     EXPECT_EQ(0, new_value->it_interval.tv_sec);
+                                     EXPECT_EQ(0, new_value->it_interval.tv_nsec);
+                                     EXPECT_EQ(seconds, new_value->it_value.tv_sec);
+                                     EXPECT_EQ(nanoseconds, new_value->it_value.tv_nsec);
+                                 }));
+        }
+
+        void expectRead(const boost::optional<uint64_t>& count)
+        {
+            EXPECT_CALL(systemMock, read(tfd, NotNull(), sizeof(uint64_t)))
+                .Times(1)
+                .WillOnce(Invoke([count] (int, void* buf, size_t) -> ssize_t
+                                 {
+                                     if (count)
+                                     {
+                                         *static_cast<uint64_t *>(buf) = *count;
+                                         return sizeof(uint64_t);
+                                     }
+                                     return -1;
+                                 }));
+        }
+
+        void expectTimeSinceEpoch(const std::chrono::steady_clock::duration& ret)
+        {
+            EXPECT_CALL(systemMock, time_since_epoch())
+                .Times(1)
+                .WillOnce(Return(ret));
+        }
+
+        void arm(Timer& timer, int seconds, int nanoseconds, const std::string& param = std::string())
+        {
+            timer.arm(std::chrono::duration_cast<Timer::Duration>(std::chrono::seconds(seconds) + std::chrono::nanoseconds(nanoseconds)),
+                      std::bind(&TimerFDTest::callback, this, param));
+        }
+
+        void arm(int seconds, int nanoseconds, const std::string& param = std::string())
+        {
+            arm(*timer1, seconds, nanoseconds, param);
+        }
+
+        void disarm(Timer& timer)
+        {
+            timer.disarm();
+        }
+
+        void disarm()
+        {
+            disarm(*timer1);
+        }
+
+        MOCK_METHOD1(callback, void(const std::string& param));
+    };
+}
+
+TEST_F(TimerFDTest, IsNotCopyableAndIsNotMovable)
+{
+    EXPECT_FALSE(std::is_copy_assignable<TimerFD>::value);
+    EXPECT_FALSE(std::is_move_assignable<TimerFD>::value);
+    EXPECT_FALSE(std::is_copy_constructible<TimerFD>::value);
+    EXPECT_FALSE(std::is_move_constructible<TimerFD>::value);
+}
+
+TEST_F(TimerFDTest, ArmingTheFirstTimerCallsSetTimeWithProperValues)
+{
+    expectSetTime(3, 4000);
+    arm(3, 4000);
+    Mock::VerifyAndClear(&systemMock);
+}
+
+TEST_F(TimerFDTest, ArmingAnotherTimerWithLongerTimeoutDoesntCallSetTime)
+{
+    expectSetTime(3, 0);
+    arm(*timer1, 3, 0);
+    arm(*timer2, 10, 0);
+    Mock::VerifyAndClear(&systemMock);
+}
+
+TEST_F(TimerFDTest, DisarminTheOnlyTimerCallsSetTimeWithZeroValues)
+{
+    arm(3, 0);
+    expectSetTime(0, 0);
+    disarm();
+}
+
+TEST_F(TimerFDTest, DisarminTheFirstTimerCallsSetTimeWithProperValues)
+{
+    arm(*timer1, 3, 0);
+    arm(*timer2, 4, 0);
+    expectSetTime(4, 0);
+    disarm(*timer1);
+    Mock::VerifyAndClear(&systemMock);
+}
+
+TEST_F(TimerFDTest, AfterExecutingTheFirstTimerSetTimeIsCalledWithProperValues)
+{
+    InSequence dummy;
+    arm(*timer1, 1, 0, "first");
+    arm(*timer2, 2, 0, "second");
+    expectRead(1);
+    EXPECT_CALL(*this, callback("first"))
+        .Times(1);
+    expectSetTime(2, 0);
+    savedEventHandler(Engine::EVENT_IN);
+    Mock::VerifyAndClear(&systemMock);
+}
+
+TEST_F(TimerFDTest, AfterExecutingTheLastTimerSetTimeIsCalledWithZeroValues)
+{
+    InSequence dummy;
+    arm(*timer1, 1, 0, "first");
+    expectRead(1);
+    EXPECT_CALL(*this, callback("first"))
+        .Times(1);
+    expectSetTime(0, 0);
+    savedEventHandler(Engine::EVENT_IN);
+    Mock::VerifyAndClear(&systemMock);
+}
+
+TEST_F(TimerFDTest, IfReadReturnsNegativeOnHandleEventsNothingIsDone)
+{
+    arm(10, 0);
+    expectRead(boost::none);
+    EXPECT_CALL(*this, callback(_))
+        .Times(0);
+    EXPECT_CALL(systemMock, timerfd_settime(_, _, _, _))
+        .Times(0);
+    savedEventHandler(Engine::EVENT_IN);
+    Mock::VerifyAndClear(&systemMock);
+}
+
+TEST_F(TimerFDTest, IfReadReturnsNoEventsOnHandleEventsNothingIsDone)
+{
+    arm(10, 0);
+    expectRead(static_cast<uint64_t>(0U));
+    EXPECT_CALL(*this, callback(_))
+        .Times(0);
+    EXPECT_CALL(systemMock, timerfd_settime(_, _, _, _))
+        .Times(0);
+    savedEventHandler(Engine::EVENT_IN);
+    Mock::VerifyAndClear(&systemMock);
+}
+
+TEST_F(TimerFDTest, AllTimersThatHaveExpiredDuringTheEventLoopAreExecutedWithTheSameTimerFdExpiration)
+{
+    InSequence dummy;
+
+    /* The first timer is armed to expire after 10 seconds */
+    expectTimeSinceEpoch(std::chrono::seconds(0));
+    arm(*timer1, 10, 0, "first");
+
+    /* Time has passed 2 seconds, the second timer is armed to expire after 8 seconds */
+    expectTimeSinceEpoch(std::chrono::seconds(2));
+    arm(*timer2, 8, 0, "second");
+
+    /* Time has passed 8 more seconds, both timers expire at once */
+    expectRead(1);
+    expectTimeSinceEpoch(std::chrono::seconds(10));
+    EXPECT_CALL(*this, callback("first"))
+        .Times(1);
+    EXPECT_CALL(*this, callback("second"))
+        .Times(1);
+    savedEventHandler(Engine::EVENT_IN);
+}