~aleteoryx/muditaos

3a9ab31a76de2d7f9c4fff34f770ad93541aa804 — Piotr Tański 4 years ago d83e8fe
[BH-776] Implemented PreWakeUp unit tests

Unit tests added for the PreWakeUp basic implementation.
M module-services/service-time/tests/CMakeLists.txt => module-services/service-time/tests/CMakeLists.txt +2 -0
@@ 30,4 30,6 @@ add_gtest_executable(
        service-time
    INCLUDE
        $<TARGET_PROPERTY:service-time,INCLUDE_DIRECTORIES>
    DEFS
        COMMON_ALARM_OPERATIONS_TEST
)

M module-services/service-time/tests/tests-AlarmOperations.cpp => module-services/service-time/tests/tests-AlarmOperations.cpp +11 -4
@@ 16,6 16,7 @@ class MockAlarmHandler : public alarms::AlarmHandler
    MOCK_METHOD(bool, handle, (const AlarmEventRecord &record), ());
    MOCK_METHOD(bool, handleOff, (const AlarmEventRecord &record), ());
};

class MockAlarmEventsRepository : public alarms::AbstractAlarmEventsRepository
{
  public:


@@ 111,12 112,18 @@ alarms::IAlarmOperations::OnUpdateAlarmProcessed universalBoolCallback = [](bool
class AlarmOperationsFixture : public ::testing::Test
{
  protected:
    auto getMockedAlarmOperations(std::unique_ptr<MockAlarmEventsRepository> &alarmRepo)
    {
        return std::make_unique<alarms::AlarmOperationsCommon>(std::move(alarmRepo), timeInjector);
    }
    std::unique_ptr<alarms::IAlarmOperations> getMockedAlarmOperations(
        std::unique_ptr<MockAlarmEventsRepository> &alarmRepo);
};

#if defined COMMON_ALARM_OPERATIONS_TEST
std::unique_ptr<alarms::IAlarmOperations> AlarmOperationsFixture::getMockedAlarmOperations(
    std::unique_ptr<MockAlarmEventsRepository> &alarmRepo)
{
    return std::make_unique<alarms::AlarmOperationsCommon>(std::move(alarmRepo), timeInjector);
}
#endif

constexpr auto defName     = "";
constexpr auto defDuration = 60;
constexpr auto defAllDay   = false;

M products/BellHybrid/BellHybridMain.cpp => products/BellHybrid/BellHybridMain.cpp +1 -1
@@ 68,7 68,7 @@ int main()
    systemServices.emplace_back(sys::CreatorFor<ServiceDB>());
    systemServices.emplace_back(sys::CreatorFor<ServiceAudio>());
    systemServices.emplace_back(sys::CreatorFor<ServiceDesktop>());
    systemServices.emplace_back(sys::CreatorFor<stm::ServiceTime>());
    systemServices.emplace_back(sys::CreatorFor<stm::ServiceTime>(alarms::AlarmOperationsFactory{}));
    systemServices.emplace_back(sys::CreatorFor<service::eink::ServiceEink>());
    systemServices.emplace_back(sys::CreatorFor<service::gui::ServiceGUI>());


M products/BellHybrid/services/CMakeLists.txt => products/BellHybrid/services/CMakeLists.txt +2 -0
@@ 1,3 1,5 @@
module_is_test_entity(bell-services)

add_subdirectory(appmgr)
add_subdirectory(evtmgr)
add_subdirectory(db)

M products/BellHybrid/services/time/AlarmOperations.cpp => products/BellHybrid/services/time/AlarmOperations.cpp +19 -19
@@ 9,7 9,7 @@ namespace alarms
{
    namespace
    {
        class MockedPreWakeUpSettingsProvider : public PreWakeUpSettingsProvider
        class FakePreWakeUpSettingsProvider : public PreWakeUpSettingsProvider
        {
          public:
            auto getChimeSettings() -> Settings override


@@ 29,7 29,7 @@ namespace alarms
        std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
        IAlarmOperations::GetCurrentTime getCurrentTimeCallback) const
    {
        auto settingsProvider = std::make_unique<MockedPreWakeUpSettingsProvider>();
        auto settingsProvider = std::make_unique<FakePreWakeUpSettingsProvider>();
        auto alarmOperations  = std::make_unique<AlarmOperations>(
            std::move(alarmEventsRepo), getCurrentTimeCallback, std::move(settingsProvider));
        alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::PreWakeUpChime,


@@ 43,16 43,16 @@ namespace alarms
                                     GetCurrentTime getCurrentTimeCallback,
                                     std::unique_ptr<PreWakeUpSettingsProvider> &&settingsProvider)
        : AlarmOperationsCommon{std::move(alarmEventsRepo), std::move(getCurrentTimeCallback)},
          settingsProvider{std::move(settingsProvider)}
          preWakeUp(std::move(settingsProvider))
    {}

    void AlarmOperations::minuteUpdated(TimePoint now)
    {
        AlarmOperationsCommon::minuteUpdated(now);
        processPreWakeUpEvents(now);
        processPreWakeUp(now);
    }

    void AlarmOperations::processPreWakeUpEvents(TimePoint now)
    void AlarmOperations::processPreWakeUp(TimePoint now)
    {
        if (nextSingleEvents.empty()) {
            return;


@@ 63,30 63,30 @@ namespace alarms
            return;
        }

        PreWakeUpHandler handler{settingsProvider.get()};
        preWakeUp(nextEvent, handler.handle(now, nextEvent));
    }

    void AlarmOperations::preWakeUp(const SingleEventRecord &event, PreWakeUpHandler::Result handleResult)
    {
        if (!handleResult.timeForChime && !handleResult.timeForFrontlight) {
        const auto decision = preWakeUp.decide(now, nextEvent);
        if (!decision.timeForChime && !decision.timeForFrontlight) {
            return;
        }
        handlePreWakeUp(nextEvent, decision);
    }

    void AlarmOperations::handlePreWakeUp(const SingleEventRecord &event, PreWakeUp::Decision decision)
    {
        if (auto alarmEventPtr = std::dynamic_pointer_cast<AlarmEventRecord>(event.parent); alarmEventPtr) {
            if (handleResult.timeForChime) {
            if (decision.timeForChime) {
                handleAlarmEvent(alarmEventPtr, alarms::AlarmType::PreWakeUpChime, true);
            }
            if (handleResult.timeForFrontlight) {
            if (decision.timeForFrontlight) {
                handleAlarmEvent(alarmEventPtr, alarms::AlarmType::PreWakeUpFrontlight, true);
            }
        }
    }

    PreWakeUpHandler::PreWakeUpHandler(PreWakeUpSettingsProvider *settingsProvider) : settingsProvider{settingsProvider}
    PreWakeUp::PreWakeUp(std::unique_ptr<PreWakeUpSettingsProvider> &&settingsProvider)
        : settingsProvider{std::move(settingsProvider)}
    {}

    auto PreWakeUpHandler::handle(TimePoint now, const SingleEventRecord &event) -> Result
    auto PreWakeUp::decide(TimePoint now, const SingleEventRecord &event) -> Decision
    {
        const auto chimeSettings       = settingsProvider->getChimeSettings();
        const auto frontlightSettings  = settingsProvider->getFrontlightSettings();


@@ 95,9 95,9 @@ namespace alarms
        return {isTimeForChime, isTimeForFrontlight};
    }

    auto PreWakeUpHandler::isTimeForPreWakeUp(TimePoint now,
                                              const SingleEventRecord &event,
                                              PreWakeUpSettingsProvider::Settings settings) -> bool
    auto PreWakeUp::isTimeForPreWakeUp(TimePoint now,
                                       const SingleEventRecord &event,
                                       PreWakeUpSettingsProvider::Settings settings) -> bool
    {
        const auto expectedAlarmStart = std::chrono::floor<std::chrono::minutes>(now) + settings.timeBeforeAlarm;
        return settings.enabled && std::chrono::floor<std::chrono::minutes>(event.startDate) == expectedAlarmStart;

M products/BellHybrid/services/time/CMakeLists.txt => products/BellHybrid/services/time/CMakeLists.txt +4 -0
@@ 22,3 22,7 @@ target_link_libraries(bell-time
    PUBLIC
        service-time
)

if (${ENABLE_TESTS})
    add_subdirectory(tests)
endif ()

M products/BellHybrid/services/time/include/time/AlarmOperations.hpp => products/BellHybrid/services/time/include/time/AlarmOperations.hpp +8 -8
@@ 30,24 30,24 @@ namespace alarms
        virtual auto getFrontlightSettings() -> Settings = 0;
    };

    class PreWakeUpHandler
    class PreWakeUp
    {
      public:
        struct Result
        struct Decision
        {
            bool timeForChime;
            bool timeForFrontlight;
        };

        explicit PreWakeUpHandler(PreWakeUpSettingsProvider *settingsProvider);
        auto handle(TimePoint now, const SingleEventRecord &event) -> Result;
        explicit PreWakeUp(std::unique_ptr<PreWakeUpSettingsProvider> &&settingsProvider);
        auto decide(TimePoint now, const SingleEventRecord &event) -> Decision;

      private:
        auto isTimeForPreWakeUp(TimePoint now,
                                const SingleEventRecord &event,
                                PreWakeUpSettingsProvider::Settings settings) -> bool;

        PreWakeUpSettingsProvider *settingsProvider;
        std::unique_ptr<PreWakeUpSettingsProvider> settingsProvider;
    };

    class AlarmOperations : public AlarmOperationsCommon


@@ 58,11 58,11 @@ namespace alarms
                        std::unique_ptr<PreWakeUpSettingsProvider> &&settingsProvider);

        void minuteUpdated(TimePoint now) override;
        void processPreWakeUp(TimePoint now);

      private:
        void processPreWakeUpEvents(TimePoint now);
        void preWakeUp(const SingleEventRecord &event, PreWakeUpHandler::Result handleResult);
        void handlePreWakeUp(const SingleEventRecord &event, PreWakeUp::Decision decision);

        std::unique_ptr<PreWakeUpSettingsProvider> settingsProvider;
        PreWakeUp preWakeUp;
    };
} // namespace alarms

A products/BellHybrid/services/time/tests/CMakeLists.txt => products/BellHybrid/services/time/tests/CMakeLists.txt +12 -0
@@ 0,0 1,12 @@
add_gtest_executable(
    NAME
        service-time-bell-alarm-operations
    SRCS
        test-BellAlarmOperations.cpp
    LIBS
        module-sys
        bell::time
    INCLUDE
        $<TARGET_PROPERTY:bell::time,INCLUDE_DIRECTORIES>
        $<TARGET_PROPERTY:service-time,SOURCE_DIR>
)

A products/BellHybrid/services/time/tests/test-BellAlarmOperations.cpp => products/BellHybrid/services/time/tests/test-BellAlarmOperations.cpp +236 -0
@@ 0,0 1,236 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <AlarmOperations.hpp>
#include <module-db/Interface/AlarmEventRecord.hpp>

#include <products/BellHybrid/services/time/include/time/AlarmOperations.hpp>

#include <gtest/gtest.h>

// Included a .cpp file with AlarmOperations tests for the common part (the base class' implementation).
// It needs to be built into this test in order to test the possible regression introduced by
// the Bell-specific AlarmOperations class.
// Generally, this should not be solved this way in other cases.
#include <service-time/tests/tests-AlarmOperations.cpp>

namespace
{
    class MockedPreWakeUpSettingsProvider : public alarms::PreWakeUpSettingsProvider
    {
      public:
        void setChimeSettings(bool enabled, std::chrono::minutes minutes)
        {
            chimeSettings = Settings{enabled, minutes};
        }

        void setFrontlightSettings(bool enabled, std::chrono::minutes minutes)
        {
            frontlightSettings = Settings{enabled, minutes};
        }

        auto getChimeSettings() -> Settings override
        {
            return chimeSettings;
        }

        auto getFrontlightSettings() -> Settings override
        {
            return frontlightSettings;
        }

      private:
        Settings chimeSettings{true, std::chrono::minutes{5}};
        Settings frontlightSettings{true, std::chrono::minutes{2}};
    };

    std::unique_ptr<alarms::IAlarmOperations> getMockedAlarmOperations(
        std::unique_ptr<MockAlarmEventsRepository> &alarmRepo,
        std::unique_ptr<alarms::PreWakeUpSettingsProvider> &&settingsProvider)
    {
        return std::make_unique<alarms::AlarmOperations>(
            std::move(alarmRepo), timeInjector, std::move(settingsProvider));
    }
} // namespace

// This function replaces the `getMockedAlarmOperations` function from `service-time/tests/tests-AlarmOperations.cpp`
std::unique_ptr<alarms::IAlarmOperations> AlarmOperationsFixture::getMockedAlarmOperations(
    std::unique_ptr<MockAlarmEventsRepository> &alarmRepo)
{
    return ::getMockedAlarmOperations(alarmRepo, std::make_unique<MockedPreWakeUpSettingsProvider>());
}

TEST(PreWakeUp, TooEarlyForPreWakeUp)
{
    alarms::PreWakeUp preWakeUp(std::make_unique<MockedPreWakeUpSettingsProvider>());
    auto event           = AlarmEventRecord(1,
                                  defName,
                                  TimePointFromString("2022-11-11 05:06:00"),
                                  defDuration,
                                  defAllDay,
                                  defRRule,
                                  defMusic,
                                  defEnabled,
                                  defSnooze);
    const auto nextEvent = event.getNextSingleEvent(TimePointFromString("2022-11-11 05:00:00"));
    EXPECT_TRUE(static_cast<EventInfo>(nextEvent).isValid());

    const auto decision = preWakeUp.decide(timeInjector(), nextEvent);
    ASSERT_FALSE(decision.timeForChime);
    ASSERT_FALSE(decision.timeForFrontlight);
}

TEST(PreWakeUp, TimeToPlayChime)
{
    alarms::PreWakeUp preWakeUp(std::make_unique<MockedPreWakeUpSettingsProvider>());
    auto event           = AlarmEventRecord(1,
                                  defName,
                                  TimePointFromString("2022-11-11 05:05:00"),
                                  defDuration,
                                  defAllDay,
                                  defRRule,
                                  defMusic,
                                  defEnabled,
                                  defSnooze);
    const auto nextEvent = event.getNextSingleEvent(TimePointFromString("2022-11-11 05:00:00"));
    EXPECT_TRUE(static_cast<EventInfo>(nextEvent).isValid());

    const auto decision = preWakeUp.decide(timeInjector(), nextEvent);
    ASSERT_TRUE(decision.timeForChime);
    ASSERT_FALSE(decision.timeForFrontlight);
}

TEST(PreWakeUp, TimeToPlayChimeButDisabled)
{
    auto settingsProvider = std::make_unique<MockedPreWakeUpSettingsProvider>();
    settingsProvider->setChimeSettings(false, std::chrono::minutes::zero());
    alarms::PreWakeUp preWakeUp(std::move(settingsProvider));
    auto event           = AlarmEventRecord(1,
                                  defName,
                                  TimePointFromString("2022-11-11 05:05:00"),
                                  defDuration,
                                  defAllDay,
                                  defRRule,
                                  defMusic,
                                  defEnabled,
                                  defSnooze);
    const auto nextEvent = event.getNextSingleEvent(TimePointFromString("2022-11-11 05:00:00"));
    EXPECT_TRUE(static_cast<EventInfo>(nextEvent).isValid());

    const auto decision = preWakeUp.decide(timeInjector(), nextEvent);
    ASSERT_FALSE(decision.timeForChime);
    ASSERT_FALSE(decision.timeForFrontlight);
}

TEST(PreWakeUp, TimeToLightFrontlight)
{
    alarms::PreWakeUp preWakeUp(std::make_unique<MockedPreWakeUpSettingsProvider>());
    auto event           = AlarmEventRecord(1,
                                  defName,
                                  TimePointFromString("2022-11-11 05:02:00"),
                                  defDuration,
                                  defAllDay,
                                  defRRule,
                                  defMusic,
                                  defEnabled,
                                  defSnooze);
    const auto nextEvent = event.getNextSingleEvent(TimePointFromString("2022-11-11 05:00:00"));
    EXPECT_TRUE(static_cast<EventInfo>(nextEvent).isValid());

    const auto decision = preWakeUp.decide(timeInjector(), nextEvent);
    ASSERT_FALSE(decision.timeForChime);
    ASSERT_TRUE(decision.timeForFrontlight);
}

TEST(PreWakeUp, TimeToLightFrontlightButDisabled)
{
    auto settingsProvider = std::make_unique<MockedPreWakeUpSettingsProvider>();
    settingsProvider->setFrontlightSettings(false, std::chrono::minutes::zero());
    alarms::PreWakeUp preWakeUp(std::move(settingsProvider));
    auto event           = AlarmEventRecord(1,
                                  defName,
                                  TimePointFromString("2022-11-11 05:02:00"),
                                  defDuration,
                                  defAllDay,
                                  defRRule,
                                  defMusic,
                                  defEnabled,
                                  defSnooze);
    const auto nextEvent = event.getNextSingleEvent(TimePointFromString("2022-11-11 05:00:00"));
    EXPECT_TRUE(static_cast<EventInfo>(nextEvent).isValid());

    const auto decision = preWakeUp.decide(timeInjector(), nextEvent);
    ASSERT_FALSE(decision.timeForChime);
    ASSERT_FALSE(decision.timeForFrontlight);
}

TEST(PreWakeUp, TimePointIsNotRoundedToFullMinute)
{
    alarms::PreWakeUp preWakeUp(std::make_unique<MockedPreWakeUpSettingsProvider>());
    auto event           = AlarmEventRecord(1,
                                  defName,
                                  TimePointFromString("2022-11-11 05:05:01"),
                                  defDuration,
                                  defAllDay,
                                  defRRule,
                                  defMusic,
                                  defEnabled,
                                  defSnooze);
    const auto nextEvent = event.getNextSingleEvent(TimePointFromString("2022-11-11 05:00:00"));
    EXPECT_TRUE(static_cast<EventInfo>(nextEvent).isValid());

    const auto decision = preWakeUp.decide(timeInjector(), nextEvent);
    ASSERT_TRUE(decision.timeForChime);
    ASSERT_FALSE(decision.timeForFrontlight);
}

class BellAlarmOperationsFixture : public ::testing::Test
{
  protected:
    void SetUp() override
    {
        auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
        alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                              defName,
                                                              TimePointFromString("2022-11-11 09:00:00"),
                                                              defDuration,
                                                              defAllDay,
                                                              defRRule,
                                                              defMusic,
                                                              defEnabled,
                                                              defSnooze));
        chimeHandler      = std::make_shared<MockAlarmHandler>();
        frontlightHandler = std::make_shared<MockAlarmHandler>();

        alarmOperations =
            ::getMockedAlarmOperations(alarmRepoMock, std::make_unique<MockedPreWakeUpSettingsProvider>());
        alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::PreWakeUpChime, chimeHandler);
        alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::PreWakeUpFrontlight, frontlightHandler);
        alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 08:00:00"));
    }

    std::unique_ptr<alarms::IAlarmOperations> alarmOperations;
    std::shared_ptr<MockAlarmHandler> chimeHandler;
    std::shared_ptr<MockAlarmHandler> frontlightHandler;
};

TEST_F(BellAlarmOperationsFixture, PreWakeUp_TooEarlyToHandle)
{
    EXPECT_CALL(*chimeHandler, handle(testing::_)).Times(0);
    EXPECT_CALL(*frontlightHandler, handle(testing::_)).Times(0);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 08:54:00"));
}

TEST_F(BellAlarmOperationsFixture, PreWakeUp_TimeToPlayChime)
{
    EXPECT_CALL(*chimeHandler, handle(testing::_)).Times(1);
    EXPECT_CALL(*frontlightHandler, handle(testing::_)).Times(0);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 08:55:00"));
}

TEST_F(BellAlarmOperationsFixture, PreWakeUp_TimeToLightFrontlight)
{
    EXPECT_CALL(*chimeHandler, handle(testing::_)).Times(0);
    EXPECT_CALL(*frontlightHandler, handle(testing::_)).Times(1);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 08:58:00"));
}