~aleteoryx/muditaos

ba1a6f25274d9f8d181739ab371cca4490591165 — Paweł Joński 4 years ago 3246570
[BH-691] Alarms handling

Handle alarms in service-time
M module-db/Interface/AlarmEventRecord.cpp => module-db/Interface/AlarmEventRecord.cpp +5 -0
@@ 173,3 173,8 @@ std::vector<AlarmEventRecord> generateRecordsVector(const std::vector<AlarmEvent
    }
    return recordVector;
}

std::shared_ptr<EventRecord> AlarmEventRecord::getCopy()
{
    return std::make_shared<AlarmEventRecord>(*this);
}

M module-db/Interface/AlarmEventRecord.hpp => module-db/Interface/AlarmEventRecord.hpp +2 -1
@@ 45,8 45,9 @@ struct AlarmEventRecord : public EventRecord
                     bool enabled,
                     uint32_t snoozeDuration);

    AlarmEventRecord(const AlarmEventsTableRow &aeRow);
    explicit AlarmEventRecord(const AlarmEventsTableRow &aeRow);

    std::shared_ptr<EventRecord> getCopy() override;
    auto isValid() const -> bool;
};


M module-db/Interface/EventRecord.cpp => module-db/Interface/EventRecord.cpp +6 -1
@@ 62,9 62,14 @@ std::vector<SingleEventRecord> EventRecord::generateSingleEvents(TimePoint from,
    return singleEvents;
}

std::shared_ptr<EventRecord> EventRecord::getCopy()
{
    return std::make_shared<EventRecord>(*this);
}

SingleEventRecord EventRecord::getNextSingleEvent(TimePoint from)
{
    auto parentEvent = std::make_shared<EventRecord>(this);
    auto parentEvent = getCopy();

    if (rruleText.empty()) {
        if (startDate < from) {

M module-db/Interface/EventRecord.hpp => module-db/Interface/EventRecord.hpp +4 -0
@@ 44,11 44,15 @@ struct EventRecord : public Record, public EventInfo
                uint32_t duration,
                bool isAllDay,
                const std::string &rruleText);

    virtual ~EventRecord(){};
    explicit EventRecord(EventRecord *record);

    auto generateSingleEvents(TimePoint from, TimePoint to, uint32_t count) -> std::vector<SingleEventRecord>;
    auto getNextSingleEvent(TimePoint from) -> SingleEventRecord;
    auto isValid() const -> bool;

    virtual std::shared_ptr<EventRecord> getCopy();
};

struct SingleEventRecord : public Record, public EventInfo

M module-db/tests/EventRecord_tests.cpp => module-db/tests/EventRecord_tests.cpp +20 -0
@@ 3,6 3,7 @@

#include "common.hpp"

#include <Interface/AlarmEventRecord.hpp>
#include <Interface/EventRecord.hpp>

#include <time/dateCommon.hpp>


@@ 117,4 118,23 @@ TEST_CASE("EventRecord tests")
        REQUIRE(event.startDate == TimePointFromString("2021-03-01 12:00:00"));
        REQUIRE(event.endDate == TimePointFromString("2021-03-01 13:00:00"));
    }

    SECTION("Generate next AlarmEvent daily")
    {
        AlarmEventRecord eventRecord(
            1, testName, testEventStart, testDuration, testIsAllDay, "FREQ=DAILY", "", true, 15);

        auto event = eventRecord.getNextSingleEvent(TimePointFromString("2000-01-01 00:00:00"));
        REQUIRE(event.name == eventRecord.name);
        REQUIRE(event.startDate == eventRecord.startDate);
        REQUIRE(event.endDate == TimePointFromString("2020-01-11 13:00:00"));
        REQUIRE(event.duration == eventRecord.duration);
        REQUIRE(event.isAllDay == eventRecord.isAllDay);
        REQUIRE(event.parent != nullptr);
        auto parent = std::dynamic_pointer_cast<AlarmEventRecord>(event.parent);
        REQUIRE(parent->ID == eventRecord.ID);
        REQUIRE(parent->musicTone == eventRecord.musicTone);
        REQUIRE(parent->enabled == eventRecord.enabled);
        REQUIRE(parent->snoozeDuration == eventRecord.snoozeDuration);
    }
}

M module-services/service-time/AlarmMessageHandler.cpp => module-services/service-time/AlarmMessageHandler.cpp +11 -0
@@ 77,6 77,17 @@ namespace alarms
            });
    }

    auto AlarmMessageHandler::handleMinuteUpdated() -> void
    {
        alarmOperations->minuteUpdated(TimePointNow());
    }

    auto AlarmMessageHandler::addAlarmExecutionHandler(const alarms::AlarmType type,
                                                       const std::shared_ptr<alarms::AlarmHandler> handler) -> void
    {
        alarmOperations->addAlarmExecutionHandler(type, handler);
    }

    template <class RequestType, class ResponseType, class CallbackParamType>
    auto AlarmMessageHandler::handleWithCallback(
        RequestType *request,

M module-services/service-time/AlarmMessageHandler.hpp => module-services/service-time/AlarmMessageHandler.hpp +4 -0
@@ 29,6 29,10 @@ namespace alarms
            -> std::shared_ptr<AlarmsGetInRangeResponseMessage>;
        auto handleGetNextSingleEvents(AlarmGetNextSingleEventsRequestMessage *request)
            -> std::shared_ptr<AlarmGetNextSingleEventsResponseMessage>;
        auto handleMinuteUpdated() -> void;

        auto addAlarmExecutionHandler(const alarms::AlarmType type, const std::shared_ptr<alarms::AlarmHandler> handler)
            -> void;

      private:
        stm::ServiceTime *service = nullptr;

M module-services/service-time/AlarmOperations.cpp => module-services/service-time/AlarmOperations.cpp +98 -5
@@ 3,10 3,22 @@

#include "AlarmOperations.hpp"

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

namespace alarms
{
    AlarmOperations::AlarmOperations(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo)
        : alarmEventsRepo{std::move(alarmEventsRepo)} {};
    AlarmOperations::AlarmOperations(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
                                     GetCurrentTime getCurrentTimeCallback)
        : alarmEventsRepo{std::move(alarmEventsRepo)}, getCurrentTimeCallback{getCurrentTimeCallback} {};

    void AlarmOperations::updateEventsCache(TimePoint now)
    {
        OnGetNextSingleProcessed callback = [&](std::vector<SingleEventRecord> singleEvents) {
            nextSingleEvents = std::move(singleEvents);
        };
        getNextSingleEvents(now, callback);
    }

    void AlarmOperations::getAlarm(const std::uint32_t alarmId, OnGetAlarmProcessed callback)
    {


@@ 16,18 28,44 @@ namespace alarms

    void AlarmOperations::addAlarm(AlarmEventRecord record, OnAddAlarmProcessed callback)
    {
        OnAddAlarmEventCallback repoCallback = [callback](bool success) { callback(success); };
        OnAddAlarmEventCallback repoCallback = [&, callback, record](bool success) mutable {
            checkAndUpdateCache(record);
            callback(success);
        };
        alarmEventsRepo->addAlarmEvent(record, repoCallback);
    }

    void AlarmOperations::updateAlarm(AlarmEventRecord record, OnUpdateAlarmProcessed callback)
    {
        OnUpdateAlarmEventCallback repoCallback = [callback](bool success) { callback(success); };
        OnUpdateAlarmEventCallback repoCallback = [&, callback, record](bool success) mutable {
            auto found =
                std::find_if(nextSingleEvents.begin(),
                             nextSingleEvents.end(),
                             [recordId = record.ID](const SingleEventRecord &e) { return e.parent->ID == recordId; });

            if (found != std::end(nextSingleEvents)) {
                updateEventsCache(getCurrentTime());
            }
            else {
                checkAndUpdateCache(record);
            }
            callback(success);
        };
        alarmEventsRepo->updateAlarmEvent(record, repoCallback);
    }

    void AlarmOperations::removeAlarm(const std::uint32_t alarmId, OnRemoveAlarmProcessed callback)
    {
        OnRemoveAlarmEventCallback repoCallback = [callback](bool success) { callback(success); };
        OnRemoveAlarmEventCallback repoCallback = [&, callback, alarmId](bool success) {
            auto found = std::find_if(nextSingleEvents.begin(),
                                      nextSingleEvents.end(),
                                      [alarmId](const SingleEventRecord &e) { return e.parent->ID == alarmId; });

            if (found != std::end(nextSingleEvents) && nextSingleEvents.size() == 1) {
                updateEventsCache(getCurrentTime());
            }
            callback(success);
        };
        alarmEventsRepo->removeAlarmEvent(alarmId, repoCallback);
    }



@@ 104,4 142,59 @@ namespace alarms
        }
        handledCallback(outEvents);
    }

    void AlarmOperations::checkAndUpdateCache(AlarmEventRecord record)
    {
        auto nearestNewSingleEvent = record.getNextSingleEvent(getCurrentTime());

        if (nearestNewSingleEvent.EventInfo::isValid() && nearestNewSingleEvent.startDate > getCurrentTime()) {
            auto alarmEvent = std::dynamic_pointer_cast<AlarmEventRecord>(nearestNewSingleEvent.parent);
            if (record.enabled &&
                (nextSingleEvents.empty() || nearestNewSingleEvent.startDate <= nextSingleEvents.front().startDate)) {
                updateEventsCache(getCurrentTime());
            }
        }
    }

    auto AlarmOperations::minuteUpdated(TimePoint now) -> void
    {
        if (nextSingleEvents.empty() || nextSingleEvents.front().startDate > now) {
            return;
        }

        executeAlarm(nextSingleEvents.front());
    }

    void AlarmOperations::addAlarmExecutionHandler(const alarms::AlarmType type,
                                                   const std::shared_ptr<alarms::AlarmHandler> handler)
    {
        alarmHandlerFactory.addHandler(type, handler);
    }

    void AlarmOperations::executeAlarm(const SingleEventRecord &singleAlarmEvent)
    {
        alarms::AlarmType alarmType = alarms::AlarmType::None;
        if (typeid(*(singleAlarmEvent.parent)) == typeid(AlarmEventRecord)) {
            alarmType = alarms::AlarmType::Clock;
        }

        auto alarmEventPtr = std::dynamic_pointer_cast<AlarmEventRecord>(singleAlarmEvent.parent);
        if (alarmEventPtr) {
            auto handler = alarmHandlerFactory.getHandler(alarmType);
            if (handler) {
                handler->handle(*alarmEventPtr);
            }
        }
        else {
            LOG_WARN("Parent type is not AlarmEventRecord!");
        }
    }

    TimePoint AlarmOperations::getCurrentTime()
    {
        if (!getCurrentTimeCallback) {
            return TIME_POINT_INVALID;
        }
        return getCurrentTimeCallback();
    }
} // namespace alarms

M module-services/service-time/AlarmOperations.hpp => module-services/service-time/AlarmOperations.hpp +29 -4
@@ 5,6 5,8 @@

#include "AlarmRepository.hpp"

#include <service-time/AlarmHandlerFactory.hpp>

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

namespace alarms


@@ 22,22 24,32 @@ namespace alarms

        virtual ~IAlarmOperations() noexcept = default;

        using GetCurrentTime = std::function<TimePoint()>;

        virtual void updateEventsCache(TimePoint now) = 0;

        virtual void getAlarm(const std::uint32_t alarmId, OnGetAlarmProcessed callback)       = 0;
        virtual void addAlarm(AlarmEventRecord record, OnAddAlarmProcessed callback)           = 0;
        virtual void updateAlarm(AlarmEventRecord record, OnUpdateAlarmProcessed callback)     = 0;
        virtual void removeAlarm(const std::uint32_t alarmId, OnRemoveAlarmProcessed callback) = 0;
        virtual void addAlarm(AlarmEventRecord record, OnAddAlarmProcessed callback)               = 0;
        virtual void updateAlarm(AlarmEventRecord record, OnUpdateAlarmProcessed callback)         = 0;
        virtual void removeAlarm(const std::uint32_t alarmId, OnRemoveAlarmProcessed callback)     = 0;
        virtual void getAlarmsInRange(TimePoint start,
                                      TimePoint end,
                                      std::uint32_t offset,
                                      std::uint32_t limit,
                                      OnGetAlarmsInRangeProcessed callback)                    = 0;
        virtual void getNextSingleEvents(TimePoint start, OnGetNextSingleProcessed callback)   = 0;
        virtual void minuteUpdated(TimePoint now)                                                  = 0;
        virtual void addAlarmExecutionHandler(const alarms::AlarmType type,
                                              const std::shared_ptr<alarms::AlarmHandler> handler) = 0;
    };

    class AlarmOperations : public IAlarmOperations
    {
      public:
        explicit AlarmOperations(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo);
        explicit AlarmOperations(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
                                 GetCurrentTime getCurrentTimeCallback);

        void updateEventsCache(TimePoint now) override;

        void getAlarm(const std::uint32_t alarmId, OnGetAlarmProcessed callback) override;
        void addAlarm(AlarmEventRecord record, OnAddAlarmProcessed callback) override;


@@ 49,9 61,18 @@ namespace alarms
                              std::uint32_t limit,
                              OnGetAlarmsInRangeProcessed callback) override;
        void getNextSingleEvents(TimePoint start, OnGetNextSingleProcessed callback) override;
        void minuteUpdated(TimePoint now) override;
        void addAlarmExecutionHandler(const alarms::AlarmType type,
                                      const std::shared_ptr<alarms::AlarmHandler> handler) override;

      private:
        std::unique_ptr<AbstractAlarmEventsRepository> alarmEventsRepo;
        AlarmHandlerFactory alarmHandlerFactory;

        // Events we are waiting for (on one timepoint)
        std::vector<SingleEventRecord> nextSingleEvents;

        GetCurrentTime getCurrentTimeCallback;

        // Max 100 alarms for one minute seems reasonable, next events will be dropped
        constexpr static auto getNextSingleEventsOffset = 0;


@@ 66,5 87,9 @@ namespace alarms
                                               std::shared_ptr<std::vector<AlarmEventRecord>> nextEvents,
                                               TimePoint start,
                                               std::vector<AlarmEventRecord> records);

        void checkAndUpdateCache(AlarmEventRecord record);
        void executeAlarm(const SingleEventRecord &singleAlarmEvent);
        TimePoint getCurrentTime();
    };
} // namespace alarms

M module-services/service-time/AlarmRepository.hpp => module-services/service-time/AlarmRepository.hpp +3 -3
@@ 10,9 10,9 @@
#include <ctime>
#include <vector>

class EventRecord;
class SingleEvent;
class AlarmEventRecord;
struct EventRecord;
struct SingleEvent;
struct AlarmEventRecord;

/**
 * @brief Basic interface alarm API

M module-services/service-time/ServiceTime.cpp => module-services/service-time/ServiceTime.cpp +16 -2
@@ 42,7 42,8 @@ namespace stm
        timeManager = std::make_unique<TimeManager>(std::make_unique<RTCCommand>(this));

        auto alarmEventsRepo = std::make_unique<alarms::AlarmEventsDBRepository>(this);
        auto alarmOperations = std::make_unique<alarms::AlarmOperations>(std::move(alarmEventsRepo));
        auto alarmOperations = std::make_unique<alarms::AlarmOperations>(std::move(alarmEventsRepo), TimePointNow);
        alarmOperations->updateEventsCache(TimePointNow());
        alarmMessageHandler  = std::make_unique<alarms::AlarmMessageHandler>(this, std::move(alarmOperations));
    }



@@ 93,8 94,21 @@ namespace stm
                }
            }
        }
        return std::make_shared<sys::ResponseMessage>();
        if (msgl->messageType == MessageType::EVMMinuteUpdated) {
            alarmMessageHandler->handleMinuteUpdated();
            return std::make_shared<sys::ResponseMessage>();
        }
        else {
            return std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Unresolved);
        }
    }

    void ServiceTime::addAlarmExecutionHandler(const alarms::AlarmType type,
                                               const std::shared_ptr<alarms::AlarmHandler> handler)
    {
        alarmMessageHandler->addAlarmExecutionHandler(type, handler);
    }

    void ServiceTime::registerMessageHandlers()
    {
        connect(typeid(CellularTimeNotificationMessage), [&](sys::Message *request) -> sys::MessagePointer {

M module-services/service-time/ServiceTime.hpp => module-services/service-time/ServiceTime.hpp +3 -0
@@ 57,6 57,9 @@ namespace stm
        sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override final;

        sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp = nullptr) override;

        void addAlarmExecutionHandler(const alarms::AlarmType type,
                                      const std::shared_ptr<alarms::AlarmHandler> handler);
    };

} /* namespace stm */

M module-services/service-time/include/service-time/AlarmServiceAPI.hpp => module-services/service-time/include/service-time/AlarmServiceAPI.hpp +1 -1
@@ 10,7 10,7 @@
#include <ctime>
#include <vector>

class AlarmEventRecord;
struct AlarmEventRecord;

/**
 * @brief Basic interface alarm API

M module-services/service-time/tests/tests-AlarmOperations.cpp => module-services/service-time/tests/tests-AlarmOperations.cpp +349 -13
@@ 3,11 3,18 @@

#include <AlarmOperations.hpp>
#include <AlarmRepository.hpp>
#include <service-time/AlarmHandler.hpp>

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

#include <gtest/gtest.h>
#include <gmock/gmock.h>

class MockAlarmHandler : public alarms::AlarmHandler
{
  public:
    MOCK_METHOD(bool, handle, (const AlarmEventRecord &record), ());
};
class MockAlarmEventsRepository : public alarms::AbstractAlarmEventsRepository
{
  public:


@@ 30,18 37,47 @@ class MockAlarmEventsRepository : public alarms::AbstractAlarmEventsRepository
                 std::uint32_t limit,
                 const alarms::OnGetAlarmEventsInRangeCallback &callback),
                ());
    MOCK_METHOD(void,
                addAlarmEvent,
                (const AlarmEventRecord &alarmEvent, const alarms::OnAddAlarmEventCallback &callback),
                ());
    MOCK_METHOD(void,
                updateAlarmEvent,
                (const AlarmEventRecord &alarmEvent, const alarms::OnUpdateAlarmEventCallback &callback),
                ());
    MOCK_METHOD(void,
                removeAlarmEvent,
                (const std::uint32_t id, const alarms::OnRemoveAlarmEventCallback &callback),
                ());

    auto addAlarmEvent(const AlarmEventRecord &alarmEvent, const alarms::OnAddAlarmEventCallback &callback) -> void
    {
        addSingleEvent(alarmEvent);
        if (!alarmEvent.rruleText.empty()) {
            addRecurringEvent(alarmEvent);
        }
        callback({true});
    }

    auto updateAlarmEvent(const AlarmEventRecord &alarmEvent, const alarms::OnAddAlarmEventCallback &callback) -> void
    {
        nextRecords.erase(std::remove_if(nextRecords.begin(),
                                         nextRecords.end(),
                                         [&alarmEvent](const AlarmEventRecord &ae) { return ae.ID == alarmEvent.ID; }),
                          nextRecords.end());
        recurringRecords.erase(
            std::remove_if(recurringRecords.begin(),
                           recurringRecords.end(),
                           [&alarmEvent](const AlarmEventRecord &ae) { return ae.ID == alarmEvent.ID; }),
            recurringRecords.end());

        addSingleEvent(alarmEvent);
        if (!alarmEvent.rruleText.empty()) {
            addRecurringEvent(alarmEvent);
        }
        callback({true});
    }
    auto removeAlarmEvent(const std::uint32_t alarmId, const alarms::OnRemoveAlarmEventCallback &callback) -> void
    {
        nextRecords.erase(std::remove_if(nextRecords.begin(),
                                         nextRecords.end(),
                                         [&alarmId](const AlarmEventRecord &ae) { return ae.ID == alarmId; }),
                          nextRecords.end());
        recurringRecords.erase(std::remove_if(recurringRecords.begin(),
                                              recurringRecords.end(),
                                              [&alarmId](const AlarmEventRecord &ae) { return ae.ID == alarmId; }),
                               recurringRecords.end());
        callback({true});
    }

    void getAlarmEventsRecurringInRange(TimePoint start,
                                        TimePoint end,
                                        std::uint32_t offset,


@@ 68,12 104,15 @@ class MockAlarmEventsRepository : public alarms::AbstractAlarmEventsRepository
    }
};

alarms::IAlarmOperations::GetCurrentTime timeInjector = []() { return TimePointFromString("2022-11-11 05:00:00"); };
alarms::IAlarmOperations::OnUpdateAlarmProcessed universalBoolCallback = [](bool success) { EXPECT_EQ(success, true); };

class AlarmOperationsFixture : public ::testing::Test
{
  protected:
    auto getMockedAlarmOperations(std::unique_ptr<MockAlarmEventsRepository> &alarmRepo)
    {
        return std::make_unique<alarms::AlarmOperations>(std::move(alarmRepo));
        return std::make_unique<alarms::AlarmOperations>(std::move(alarmRepo), timeInjector);
    }
};



@@ 366,3 405,300 @@ TEST_F(AlarmOperationsFixture, getNextMultipleEventsWithRecursive)
    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->getNextSingleEvents(TimePointFromString("2022-01-01 00:00:00"), callback);
}

TEST_F(AlarmOperationsFixture, handleFirstEvent)
{
    alarms::IAlarmOperations::OnGetNextSingleProcessed callback = [](std::vector<SingleEventRecord> records) {
        EXPECT_EQ(records.size(), 1);
        EXPECT_EQ(records[0].startDate, TimePointFromString("2022-11-11 09:00:00"));
        EXPECT_EQ(records[0].parent->ID, 1);
    };

    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                          defName,
                                                          TimePointFromString("2022-11-11 11:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(2,
                                                          defName,
                                                          TimePointFromString("2022-11-11 09:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));
    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 09:00:00"));
}

TEST_F(AlarmOperationsFixture, handleEventAfterAddCacheChanged)
{
    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                          defName,
                                                          TimePointFromString("2022-11-11 15:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));
    alarmOperations->addAlarm(AlarmEventRecord(2,
                                               defName,
                                               TimePointFromString("2022-11-11 11:00:00"),
                                               defDuration,
                                               defAllDay,
                                               defRRule,
                                               defMusic,
                                               defEnabled,
                                               defSnooze),
                              universalBoolCallback);
    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 11:00:00"));
}

TEST_F(AlarmOperationsFixture, handleEventAfterAddCacheNotChanged)
{
    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                          defName,
                                                          TimePointFromString("2022-11-11 15:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));
    alarmOperations->addAlarm(AlarmEventRecord(2,
                                               defName,
                                               TimePointFromString("2022-11-11 17:00:00"),
                                               defDuration,
                                               defAllDay,
                                               defRRule,
                                               defMusic,
                                               defEnabled,
                                               defSnooze),
                              universalBoolCallback);
    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 15:00:00"));
}

TEST_F(AlarmOperationsFixture, handleEventAfterAddCacheNotChanged2)
{
    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                          defName,
                                                          TimePointFromString("2022-11-11 15:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));
    alarmOperations->addAlarm(AlarmEventRecord(2,
                                               defName,
                                               TimePointFromString("2022-11-10 12:00:00"),
                                               defDuration,
                                               defAllDay,
                                               defRRule,
                                               defMusic,
                                               defEnabled,
                                               defSnooze),
                              universalBoolCallback);
    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 15:00:00"));
}

TEST_F(AlarmOperationsFixture, handleEventAfterAddDisabled)
{
    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                          defName,
                                                          TimePointFromString("2022-11-11 15:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));
    alarmOperations->addAlarm(AlarmEventRecord(2,
                                               defName,
                                               TimePointFromString("2022-11-11 11:00:00"),
                                               defDuration,
                                               defAllDay,
                                               defRRule,
                                               defMusic,
                                               false,
                                               defSnooze),
                              universalBoolCallback);
    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 15:00:00"));
}

TEST_F(AlarmOperationsFixture, handleAfterRemoveNearest)
{
    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                          defName,
                                                          TimePointFromString("2022-11-11 11:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(2,
                                                          defName,
                                                          TimePointFromString("2022-11-11 09:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));

    alarmOperations->removeAlarm(2, universalBoolCallback);

    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 11:00:00"));
}

TEST_F(AlarmOperationsFixture, handleAfterRemoveNotNearest)
{
    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                          defName,
                                                          TimePointFromString("2022-11-11 11:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(2,
                                                          defName,
                                                          TimePointFromString("2022-11-11 09:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));

    alarmOperations->removeAlarm(1, universalBoolCallback);

    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 09:00:00"));
}

TEST_F(AlarmOperationsFixture, handleAfterUpdateNearestDelayed)
{
    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(1,
                                                          defName,
                                                          TimePointFromString("2022-11-11 11:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));
    auto nearestAlarm = AlarmEventRecord(2,
                                         defName,
                                         TimePointFromString("2022-11-11 09:00:00"),
                                         defDuration,
                                         defAllDay,
                                         defRRule,
                                         defMusic,
                                         defEnabled,
                                         defSnooze);
    alarmRepoMock->nextRecords.push_back(nearestAlarm);

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));

    nearestAlarm.startDate = TimePointFromString("2022-11-11 15:00:00");
    alarmOperations->updateAlarm(nearestAlarm, universalBoolCallback);

    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 11:00:00"));
}

TEST_F(AlarmOperationsFixture, handleAfterUpdateSecondGetsFirst)
{
    auto alarmRepoMock = std::make_unique<MockAlarmEventsRepository>();
    auto secondAlarm   = AlarmEventRecord(1,
                                        defName,
                                        TimePointFromString("2022-11-11 11:00:00"),
                                        defDuration,
                                        defAllDay,
                                        defRRule,
                                        defMusic,
                                        defEnabled,
                                        defSnooze);
    alarmRepoMock->nextRecords.push_back(secondAlarm);

    alarmRepoMock->nextRecords.push_back(AlarmEventRecord(2,
                                                          defName,
                                                          TimePointFromString("2022-11-11 09:00:00"),
                                                          defDuration,
                                                          defAllDay,
                                                          defRRule,
                                                          defMusic,
                                                          defEnabled,
                                                          defSnooze));

    auto alarmOperations = getMockedAlarmOperations(alarmRepoMock);
    alarmOperations->updateEventsCache(TimePointFromString("2022-11-11 09:00:00"));

    secondAlarm.startDate = TimePointFromString("2022-11-11 7:00:00");
    alarmOperations->updateAlarm(secondAlarm, universalBoolCallback);

    auto handler = std::make_shared<MockAlarmHandler>();
    EXPECT_CALL(*handler, handle(testing::_)).Times(1);
    alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::Clock, handler);
    alarmOperations->minuteUpdated(TimePointFromString("2022-11-11 7:00:00"));
}