diff options
author | Lei YU <mine260309@gmail.com> | 2017-02-09 12:10:13 +0800 |
---|---|---|
committer | Lei YU <mine260309@gmail.com> | 2017-10-16 20:40:01 +0800 |
commit | 7b21879622a974d4ca92da760be7c7008732b6c3 (patch) | |
tree | 601a054f428ddcff726fa314064119326f678c34 | |
parent | 7f4fca554b250d40230d5735d1d0ddf9ac6af801 (diff) | |
download | phosphor-time-manager-7b21879622a974d4ca92da760be7c7008732b6c3.tar.gz phosphor-time-manager-7b21879622a974d4ca92da760be7c7008732b6c3.zip |
Implement HostEpoch set time logic
1. When setting host epoch, follow below logic:
Mode | Owner | Set Host Time
----- | ----- | -------------
NTP | BMC | Not allowed
NTP | HOST | Not allowed
NTP | SPLIT | OK, and just save offset
NTP | BOTH | Not allowed
MANUAL| BMC | Not allowed
MANUAL| HOST | OK, and set time to BMC
MANUAL| SPLIT | OK, and just save offset
MANUAL| BOTH | OK, and set time to BMC
2. If owner is SPLIT and BMC time is changed, update the offset accordinly;
3. Use timerfd to get notified on BMC time change, and update host time
diff accordingly;
4. Add unit test cases.
Change-Id: I2d60a821f7da9b689c579ae7ab672cc37967322c
Signed-off-by: Lei YU <mine260309@gmail.com>
-rw-r--r-- | bmc_epoch.cpp | 119 | ||||
-rw-r--r-- | bmc_epoch.hpp | 55 | ||||
-rw-r--r-- | bmc_time_change_listener.hpp | 24 | ||||
-rw-r--r-- | host_epoch.cpp | 86 | ||||
-rw-r--r-- | host_epoch.hpp | 24 | ||||
-rw-r--r-- | main.cpp | 25 | ||||
-rw-r--r-- | test/TestBmcEpoch.cpp | 53 | ||||
-rw-r--r-- | test/TestHostEpoch.cpp | 227 | ||||
-rw-r--r-- | test/mocked_bmc_time_change_listener.hpp | 16 |
9 files changed, 548 insertions, 81 deletions
diff --git a/bmc_epoch.cpp b/bmc_epoch.cpp index ed482ad..7bc6c22 100644 --- a/bmc_epoch.cpp +++ b/bmc_epoch.cpp @@ -1,6 +1,22 @@ #include "bmc_epoch.hpp" +#include <phosphor-logging/elog.hpp> +#include <phosphor-logging/elog-errors.hpp> #include <phosphor-logging/log.hpp> +#include <xyz/openbmc_project/Common/error.hpp> + +#include <sys/timerfd.h> +#include <unistd.h> + + +// Neeed to do this since its not exported outside of the kernel. +// Refer : https://gist.github.com/lethean/446cea944b7441228298 +#ifndef TFD_TIMER_CANCEL_ON_SET +#define TFD_TIMER_CANCEL_ON_SET (1 << 1) +#endif + +// Needed to make sure timerfd does not misfire even though we set CANCEL_ON_SET +#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1) namespace phosphor { @@ -11,9 +27,61 @@ using namespace phosphor::logging; BmcEpoch::BmcEpoch(sdbusplus::bus::bus& bus, const char* objPath) - : EpochBase(bus, objPath) + : EpochBase(bus, objPath), + bus(bus) +{ + initialize(); +} + +void BmcEpoch::initialize() +{ + using InternalFailure = sdbusplus::xyz::openbmc_project::Common:: + Error::InternalFailure; + + // Subscribe time change event + // Choose the MAX time that is possible to avoid mis fires. + constexpr itimerspec maxTime = { + {0, 0}, // it_interval + {TIME_T_MAX, 0}, //it_value + }; + + timeFd = timerfd_create(CLOCK_REALTIME, 0); + if (timeFd == -1) + { + log<level::ERR>("Failed to create timerfd", + entry("ERRNO=%d", errno), + entry("ERR=%s", strerror(errno))); + elog<InternalFailure>(); + } + + auto r = timerfd_settime(timeFd, + TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &maxTime, + nullptr); + if (r != 0) + { + log<level::ERR>("Failed to set timerfd", + entry("ERRNO=%d", errno), + entry("ERR=%s", strerror(errno))); + elog<InternalFailure>(); + } + + sd_event_source* es; + r = sd_event_add_io(bus.get_event(), &es, + timeFd, EPOLLIN, onTimeChange, this); + if (r < 0) + { + log<level::ERR>("Failed to add event", + entry("ERRNO=%d", -r), + entry("ERR=%s", strerror(-r))); + elog<InternalFailure>(); + } + timeChangeEventSource.reset(es); +} + +BmcEpoch::~BmcEpoch() { - // Empty + close(timeFd); } uint64_t BmcEpoch::elapsed() const @@ -24,6 +92,18 @@ uint64_t BmcEpoch::elapsed() const uint64_t BmcEpoch::elapsed(uint64_t value) { + /* + Mode | Owner | Set BMC Time + ----- | ----- | ------------- + NTP | BMC | Not allowed + NTP | HOST | Not allowed + NTP | SPLIT | Not allowed + NTP | BOTH | Not allowed + MANUAL| BMC | OK + MANUAL| HOST | Not allowed + MANUAL| SPLIT | OK + MANUAL| BOTH | OK + */ if (timeMode == Mode::NTP) { log<level::ERR>("Setting BmcTime with NTP mode is not allowed"); @@ -37,13 +117,46 @@ uint64_t BmcEpoch::elapsed(uint64_t value) return 0; } - auto time = std::chrono::microseconds(value); + auto time = microseconds(value); setTime(time); + notifyBmcTimeChange(time); + server::EpochTime::elapsed(value); return value; } +void BmcEpoch::setBmcTimeChangeListener(BmcTimeChangeListener* listener) +{ + timeChangeListener = listener; +} + +void BmcEpoch::notifyBmcTimeChange(const microseconds& time) +{ + // Notify listener if it exists + if (timeChangeListener) + { + timeChangeListener->onBmcTimeChanged(time); + } +} + +int BmcEpoch::onTimeChange(sd_event_source* es, int fd, + uint32_t /* revents */, void* userdata) +{ + auto bmcEpoch = static_cast<BmcEpoch*>(userdata); + + std::array<char, 64> time {}; + + // We are not interested in the data here. + // So read until there is no new data here in the FD + while (read(fd, time.data(), time.max_size()) > 0); + + log<level::INFO>("BMC system time is changed"); + bmcEpoch->notifyBmcTimeChange(bmcEpoch->getTime()); + + return 0; +} + } // namespace time } // namespace phosphor diff --git a/bmc_epoch.hpp b/bmc_epoch.hpp index 8c841a7..228a39d 100644 --- a/bmc_epoch.hpp +++ b/bmc_epoch.hpp @@ -1,12 +1,17 @@ #pragma once +#include "bmc_time_change_listener.hpp" #include "epoch_base.hpp" +#include <chrono> + namespace phosphor { namespace time { +using namespace std::chrono; + /** @class BmcEpoch * @brief OpenBMC BMC EpochTime implementation. * @details A concrete implementation for xyz.openbmc_project.Time.EpochTime @@ -18,6 +23,7 @@ class BmcEpoch : public EpochBase friend class TestBmcEpoch; BmcEpoch(sdbusplus::bus::bus& bus, const char* objPath); + ~BmcEpoch(); /** * @brief Get value of Elapsed property @@ -33,6 +39,55 @@ class BmcEpoch : public EpochBase * @return The updated elapsed microseconds since UTC **/ uint64_t elapsed(uint64_t value) override; + + /** @brief Set the listner for bmc time change + * + * @param[in] listener - The pointer to the listener + */ + void setBmcTimeChangeListener(BmcTimeChangeListener* listener); + + private: + /** @brief The fd for time change event */ + int timeFd = -1; + + /** @brief Initialize timerFd related resource */ + void initialize(); + + /** @brief Notify the listeners that bmc time is changed + * + * @param[in] time - The epoch time in microseconds to notify + */ + void notifyBmcTimeChange(const microseconds& time); + + /** @brief The callback function on system time change + * + * @param[in] es - Source of the event + * @param[in] fd - File descriptor of the timer + * @param[in] revents - Not used + * @param[in] userdata - User data pointer + */ + static int onTimeChange(sd_event_source* es, int fd, + uint32_t revents, void* userdata); + + /** @brief The reference of sdbusplus bus */ + sdbusplus::bus::bus& bus; + + /** @brief The deleter of sd_event_source */ + std::function<void(sd_event_source*)> sdEventSourceDeleter = + [] (sd_event_source* p) { + if (p) + { + sd_event_source_unref(p); + } + }; + using SdEventSource = std::unique_ptr<sd_event_source, + decltype(sdEventSourceDeleter)>; + + /** @brief The event source on system time change */ + SdEventSource timeChangeEventSource {nullptr, sdEventSourceDeleter}; + + /** @brief The listener for bmc time change */ + BmcTimeChangeListener* timeChangeListener = nullptr; }; } // namespace time diff --git a/bmc_time_change_listener.hpp b/bmc_time_change_listener.hpp new file mode 100644 index 0000000..44fc479 --- /dev/null +++ b/bmc_time_change_listener.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <chrono> + +namespace phosphor +{ +namespace time +{ + +class BmcTimeChangeListener +{ + public: + virtual ~BmcTimeChangeListener() = default; + + /** @brief Notified on bmc time is changed + * + * @param[in] bmcTime - The epoch time in microseconds + */ + virtual void onBmcTimeChanged( + const std::chrono::microseconds& bmcTime) = 0; +}; + +} +} diff --git a/host_epoch.cpp b/host_epoch.cpp index 3b4c00e..129d224 100644 --- a/host_epoch.cpp +++ b/host_epoch.cpp @@ -9,41 +9,105 @@ namespace time { using namespace sdbusplus::xyz::openbmc_project::Time; using namespace phosphor::logging; +using namespace std::chrono; HostEpoch::HostEpoch(sdbusplus::bus::bus& bus, const char* objPath) : EpochBase(bus, objPath), offset(utils::readData<decltype(offset)::rep>(offsetFile)) { - // Empty + // Initialize the diffToSteadyClock + auto steadyTime = duration_cast<microseconds>( + steady_clock::now().time_since_epoch()); + diffToSteadyClock = getTime() + offset - steadyTime; } uint64_t HostEpoch::elapsed() const { - // It does not needs to check owner when getting time - return (getTime() + offset).count(); + auto ret = getTime(); + if (timeOwner == Owner::SPLIT) + { + ret += offset; + } + return ret.count(); } uint64_t HostEpoch::elapsed(uint64_t value) { - if (timeOwner == Owner::BMC) + /* + Mode | Owner | Set Host Time + ----- | ----- | ------------- + NTP | BMC | Not allowed + NTP | HOST | Not allowed + NTP | SPLIT | OK, and just save offset + NTP | BOTH | Not allowed + MANUAL| BMC | Not allowed + MANUAL| HOST | OK, and set time to BMC + MANUAL| SPLIT | OK, and just save offset + MANUAL| BOTH | OK, and set time to BMC + */ + if (timeOwner == Owner::BMC || + (timeMode == Mode::NTP + && (timeOwner == Owner::HOST || timeOwner == Owner::BOTH))) { - log<level::ERR>("Setting HostTime in BMC owner is not allowed"); + log<level::ERR>("Setting HostTime is not allowed"); // TODO: throw NotAllowed exception return 0; } - // TODO: implement the logic of setting host time - // based on timeOwner and timeMode + auto time = microseconds(value); + if (timeOwner == Owner::SPLIT) + { + // Calculate the offset between host and bmc time + offset = time - getTime(); + saveOffset(); + + // Calculate the diff between host and steady time + auto steadyTime = duration_cast<microseconds>( + steady_clock::now().time_since_epoch()); + diffToSteadyClock = time - steadyTime; + } + else + { + // Set time to BMC + setTime(time); + } - auto time = std::chrono::microseconds(value); - offset = time - getTime(); + server::EpochTime::elapsed(value); + return value; +} +void HostEpoch::onOwnerChanged(Owner owner) +{ + // If timeOwner is changed to SPLIT, the offset shall be preserved + // Otherwise it shall be cleared; + timeOwner = owner; + if (timeOwner != Owner::SPLIT) + { + offset = microseconds(0); + saveOffset(); + } +} + +void HostEpoch::saveOffset() +{ // Store the offset to file utils::writeData(offsetFile, offset.count()); +} - server::EpochTime::elapsed(value); - return value; +void HostEpoch::onBmcTimeChanged(const microseconds& bmcTime) +{ + // If owner is split and BMC time is changed, + // the offset shall be adjusted + if (timeOwner == Owner::SPLIT) + { + auto steadyTime = duration_cast<microseconds>( + steady_clock::now().time_since_epoch()); + auto hostTime = steadyTime + diffToSteadyClock; + offset = hostTime - bmcTime; + + saveOffset(); + } } } // namespace time diff --git a/host_epoch.hpp b/host_epoch.hpp index 3798f8f..1eb3167 100644 --- a/host_epoch.hpp +++ b/host_epoch.hpp @@ -1,5 +1,6 @@ #pragma once +#include "bmc_time_change_listener.hpp" #include "config.h" #include "epoch_base.hpp" @@ -15,7 +16,7 @@ namespace time * @details A concrete implementation for xyz.openbmc_project.Time.EpochTime * DBus API for HOST's epoch time. */ -class HostEpoch : public EpochBase +class HostEpoch : public EpochBase, public BmcTimeChangeListener { public: friend class TestHostEpoch; @@ -38,10 +39,31 @@ class HostEpoch : public EpochBase **/ uint64_t elapsed(uint64_t value) override; + /** @brief Notified on time owner changed */ + void onOwnerChanged(Owner owner) override; + + /** @brief Notified on bmc time is changed + * + * @param[in] bmcTime - The epoch time in microseconds + */ + void onBmcTimeChanged( + const std::chrono::microseconds& bmcTime) override; + private: /** @brief The diff between BMC and Host time */ std::chrono::microseconds offset; + /** + * @brief The diff between host time and steady clock + * @details This diff is used to calculate the host time if BMC time + * is changed and the owner is SPLIT. + * Without this the host time is lost if BMC time is changed. + */ + std::chrono::microseconds diffToSteadyClock; + + /** @brief Save the offset value into offsetFile */ + void saveOffset(); + /** @brief The file to store the offset in File System. * Read back when starts **/ @@ -8,6 +8,20 @@ int main() { auto bus = sdbusplus::bus::new_default(); + sd_event* event = nullptr; + + auto eventDeleter = [](sd_event* e) { + e = sd_event_unref(e); + }; + using SdEvent = std::unique_ptr<sd_event, decltype(eventDeleter)>; + + // acquire a referece to the default event loop + sd_event_default(&event); + SdEvent sdEvent {event, eventDeleter}; + event = nullptr; + + // attach bus to this event loop + bus.attach_event(sdEvent.get(), SD_EVENT_PRIORITY_NORMAL); // Add sdbusplus ObjectManager sdbusplus::server::manager::manager bmcEpochObjManager(bus, OBJPATH_BMC); @@ -19,13 +33,14 @@ int main() manager.addListener(&bmc); manager.addListener(&host); + bmc.setBmcTimeChangeListener(&host); bus.request_name(BUSNAME); - while (true) - { - bus.process_discard(); - bus.wait(); - } + // Start event loop for all sd-bus events and timer event + sd_event_loop(bus.get_event()); + + bus.detach_event(); + return 0; } diff --git a/test/TestBmcEpoch.cpp b/test/TestBmcEpoch.cpp index 2fd55a5..4661e20 100644 --- a/test/TestBmcEpoch.cpp +++ b/test/TestBmcEpoch.cpp @@ -1,45 +1,67 @@ #include <sdbusplus/bus.hpp> #include <gtest/gtest.h> +#include <memory> #include "bmc_epoch.hpp" #include "config.h" #include "types.hpp" +#include "mocked_bmc_time_change_listener.hpp" namespace phosphor { namespace time { +using ::testing::_; using namespace std::chrono; + class TestBmcEpoch : public testing::Test { public: sdbusplus::bus::bus bus; - BmcEpoch bmcEpoch; + sd_event* event; + MockBmcTimeChangeListener listener; + std::unique_ptr<BmcEpoch> bmcEpoch; TestBmcEpoch() - : bus(sdbusplus::bus::new_default()), - bmcEpoch(bus, OBJPATH_BMC) + : bus(sdbusplus::bus::new_default()) { - // Empty + // BmcEpoch requires sd_event to init + sd_event_default(&event); + bus.attach_event(event, SD_EVENT_PRIORITY_NORMAL); + bmcEpoch = std::make_unique<BmcEpoch>(bus, OBJPATH_BMC); + bmcEpoch->setBmcTimeChangeListener(&listener); + } + + ~TestBmcEpoch() + { + bus.detach_event(); + sd_event_unref(event); } // Proxies for BmcEpoch's private members and functions Mode getTimeMode() { - return bmcEpoch.timeMode; + return bmcEpoch->timeMode; } Owner getTimeOwner() { - return bmcEpoch.timeOwner; + return bmcEpoch->timeOwner; } void setTimeOwner(Owner owner) { - bmcEpoch.timeOwner = owner; + bmcEpoch->timeOwner = owner; } void setTimeMode(Mode mode) { - bmcEpoch.timeMode = mode; + bmcEpoch->timeMode = mode; + } + void triggerTimeChange() + { + bmcEpoch->onTimeChange(nullptr, + -1, + 0, + bmcEpoch.get()); } }; @@ -51,9 +73,9 @@ TEST_F(TestBmcEpoch, empty) TEST_F(TestBmcEpoch, getElapsed) { - auto t1 = bmcEpoch.elapsed(); + auto t1 = bmcEpoch->elapsed(); EXPECT_NE(0, t1); - auto t2 = bmcEpoch.elapsed(); + auto t2 = bmcEpoch->elapsed(); EXPECT_GE(t2, t1); } @@ -62,13 +84,13 @@ TEST_F(TestBmcEpoch, setElapsedNotAllowed) auto epochNow = duration_cast<microseconds>( system_clock::now().time_since_epoch()).count(); // In NTP mode, setting time is not allowed - auto ret = bmcEpoch.elapsed(epochNow); + auto ret = bmcEpoch->elapsed(epochNow); EXPECT_EQ(0, ret); // In Host owner, setting time is not allowed setTimeMode(Mode::MANUAL); setTimeOwner(Owner::HOST); - ret = bmcEpoch.elapsed(epochNow); + ret = bmcEpoch->elapsed(epochNow); EXPECT_EQ(0, ret); } @@ -79,5 +101,12 @@ TEST_F(TestBmcEpoch, setElapsedOK) // But for now we can not test it } +TEST_F(TestBmcEpoch, onTimeChange) +{ + // On BMC time change, the listner is expected to be notified + EXPECT_CALL(listener, onBmcTimeChanged(_)).Times(1); + triggerTimeChange(); +} + } } diff --git a/test/TestHostEpoch.cpp b/test/TestHostEpoch.cpp index 0ff8a11..13d604d 100644 --- a/test/TestHostEpoch.cpp +++ b/test/TestHostEpoch.cpp @@ -14,6 +14,8 @@ namespace time using namespace std::chrono; using namespace std::chrono_literals; +const constexpr microseconds USEC_ZERO{0}; + class TestHostEpoch : public testing::Test { public: @@ -22,7 +24,7 @@ class TestHostEpoch : public testing::Test static constexpr auto FILE_NOT_EXIST = "path/to/file-not-exist"; static constexpr auto FILE_OFFSET = "saved_host_offset"; - static constexpr auto delta = 2s; + const microseconds delta = 2s; TestHostEpoch() : bus(sdbusplus::bus::new_default()), @@ -50,9 +52,86 @@ class TestHostEpoch : public testing::Test { return hostEpoch.offset; } + void setOffset(microseconds us) + { + hostEpoch.offset = us; + } void setTimeOwner(Owner owner) { - hostEpoch.timeOwner = owner; + hostEpoch.onOwnerChanged(owner); + } + void setTimeMode(Mode mode) + { + hostEpoch.onModeChanged(mode); + } + + void checkSettingTimeNotAllowed() + { + // By default offset shall be 0 + EXPECT_EQ(0, getOffset().count()); + + // Set time is not allowed, + // so verify offset is still 0 after set time + microseconds diff = 1min; + hostEpoch.elapsed(hostEpoch.elapsed() + diff.count()); + EXPECT_EQ(0, getOffset().count()); + // TODO: when gmock is ready, check there is no call to timedatectl + } + + void checkSetSplitTimeInFuture() + { + // Get current time, and set future +1min time + auto t1 = hostEpoch.elapsed(); + EXPECT_NE(0, t1); + microseconds diff = 1min; + auto t2 = t1 + diff.count(); + hostEpoch.elapsed(t2); + + // Verify that the offset shall be positive, + // and less or equal to diff, and shall be not too less. + auto offset = getOffset(); + EXPECT_GT(offset, USEC_ZERO); + EXPECT_LE(offset, diff); + diff -= delta; + EXPECT_GE(offset, diff); + + // Now get time shall be around future +1min time + auto epochNow = duration_cast<microseconds>( + system_clock::now().time_since_epoch()).count(); + auto elapsedGot = hostEpoch.elapsed(); + EXPECT_LT(epochNow, elapsedGot); + auto epochDiff = elapsedGot - epochNow; + diff = 1min; + EXPECT_GT(epochDiff, (diff - delta).count()); + EXPECT_LT(epochDiff, (diff + delta).count()); + } + void checkSetSplitTimeInPast() + { + // Get current time, and set past -1min time + auto t1 = hostEpoch.elapsed(); + EXPECT_NE(0, t1); + microseconds diff = 1min; + auto t2 = t1 - diff.count(); + hostEpoch.elapsed(t2); + + // Verify that the offset shall be negative, and the absolute value + // shall be equal or greater than diff, and shall not be too greater + auto offset = getOffset(); + EXPECT_LT(offset, USEC_ZERO); + offset = -offset; + EXPECT_GE(offset, diff); + diff += 10s; + EXPECT_LE(offset, diff); + + // Now get time shall be around past -1min time + auto epochNow = duration_cast<microseconds>( + system_clock::now().time_since_epoch()).count(); + auto elapsedGot = hostEpoch.elapsed(); + EXPECT_LT(elapsedGot, epochNow); + auto epochDiff = epochNow - elapsedGot; + diff = 1min; + EXPECT_GT(epochDiff, (diff - delta).count()); + EXPECT_LT(epochDiff, (diff + delta).count()); } }; @@ -80,26 +159,90 @@ TEST_F(TestHostEpoch, writeAndReadData) // Read it back microseconds offsetToRead; offsetToRead = microseconds( - utils::readData<decltype(offsetToRead)::rep>(FILE_OFFSET)); + utils::readData<decltype(offsetToRead)::rep>(FILE_OFFSET)); EXPECT_EQ(offsetToWrite, offsetToRead); } -TEST_F(TestHostEpoch, setElapsedNotAllowed) +TEST_F(TestHostEpoch, setElapsedInNtpBmc) { - // By default offset shall be 0 - EXPECT_EQ(0, getOffset().count()); + // Set time in NTP/BMC is not allowed + setTimeMode(Mode::NTP); + setTimeOwner(Owner::BMC); + checkSettingTimeNotAllowed(); +} - // Set time in BMC mode is not allowed, - // so verify offset is still 0 after set time - microseconds diff = 1min; - hostEpoch.elapsed(hostEpoch.elapsed() + diff.count()); - EXPECT_EQ(0, getOffset().count()); +TEST_F(TestHostEpoch, setElapsedInNtpHost) +{ + // Set time in NTP/HOST is not allowed + setTimeMode(Mode::NTP); + setTimeOwner(Owner::HOST); + checkSettingTimeNotAllowed(); +} + +TEST_F(TestHostEpoch, setElapsedInNtpSplit) +{ + // Set time in NTP/SPLIT, offset will be set + setTimeMode(Mode::NTP); + setTimeOwner(Owner::SPLIT); + + checkSetSplitTimeInFuture(); + + // Reset offset + setOffset(USEC_ZERO); + checkSetSplitTimeInPast(); } -TEST_F(TestHostEpoch, setElapsedInFutureAndGet) +TEST_F(TestHostEpoch, setElapsedInNtpBoth) { - // Set to HOST owner so that we can set elapsed + // Set time in NTP/BOTH is not allowed + setTimeMode(Mode::NTP); + setTimeOwner(Owner::BOTH); + checkSettingTimeNotAllowed(); +} + +TEST_F(TestHostEpoch, setElapsedInManualBmc) +{ + // Set time in MANUAL/BMC is not allowed + setTimeMode(Mode::MANUAL); + setTimeOwner(Owner::BMC); + checkSettingTimeNotAllowed(); +} + +TEST_F(TestHostEpoch, setElapsedInManualHost) +{ + // Set time in MANUAL/HOST, time will be set to BMC + // However it requies gmock to test this case + // TODO: when gmock is ready, test this case. + setTimeMode(Mode::MANUAL); setTimeOwner(Owner::HOST); +} + +TEST_F(TestHostEpoch, setElapsedInManualSplit) +{ + // Set to SPLIT owner so that offset will be set + setTimeMode(Mode::MANUAL); + setTimeOwner(Owner::SPLIT); + + checkSetSplitTimeInFuture(); + + // Reset offset + setOffset(USEC_ZERO); + checkSetSplitTimeInPast(); +} + +TEST_F(TestHostEpoch, setElapsedInManualBoth) +{ + // Set time in MANUAL/BOTH, time will be set to BMC + // However it requies gmock to test this case + // TODO: when gmock is ready, test this case. + setTimeMode(Mode::MANUAL); + setTimeOwner(Owner::BOTH); +} + +TEST_F(TestHostEpoch, setElapsedInSplitAndBmcTimeIsChanged) +{ + // Set to SPLIT owner so that offset will be set + setTimeOwner(Owner::SPLIT); // Get current time, and set future +1min time auto t1 = hostEpoch.elapsed(); @@ -111,52 +254,38 @@ TEST_F(TestHostEpoch, setElapsedInFutureAndGet) // Verify that the offset shall be positive, // and less or equal to diff, and shall be not too less. auto offset = getOffset(); - EXPECT_GT(offset, microseconds(0)); + EXPECT_GT(offset, USEC_ZERO); EXPECT_LE(offset, diff); diff -= delta; EXPECT_GE(offset, diff); - // Now get time shall be around future +1min time - auto epochNow = duration_cast<microseconds>( - system_clock::now().time_since_epoch()).count(); - auto elapsedGot = hostEpoch.elapsed(); - EXPECT_LT(epochNow, elapsedGot); - auto epochDiff = elapsedGot - epochNow; - diff = 1min; - EXPECT_GT(epochDiff, (diff - delta).count()); - EXPECT_LT(epochDiff, (diff + delta).count()); + // Now BMC time is changed to future +1min + hostEpoch.onBmcTimeChanged(microseconds(t2)); + + // Verify that the offset shall be around zero since it's almost + // the same as BMC time + offset = getOffset(); + if (offset.count() < 0) + { + offset = microseconds(-offset.count()); + } + EXPECT_LE(offset, delta); } -TEST_F(TestHostEpoch, setElapsedInPastAndGet) +TEST_F(TestHostEpoch, clearOffsetOnOwnerChange) { - // Set to HOST owner so that we can set elapsed - setTimeOwner(Owner::HOST); + EXPECT_EQ(USEC_ZERO, getOffset()); - // Get current time, and set past -1min time - auto t1 = hostEpoch.elapsed(); - EXPECT_NE(0, t1); - microseconds diff = 1min; - auto t2 = t1 - diff.count(); - hostEpoch.elapsed(t2); + setTimeOwner(Owner::SPLIT); + hostEpoch.onBmcTimeChanged(microseconds(hostEpoch.elapsed()) + 1min); - // Verify that the offset shall be negative, and the absolute value - // shall be equal or greater than diff, and shall not be too greater - auto offset = getOffset(); - EXPECT_LT(offset, microseconds(0)); - offset = -offset; - EXPECT_GE(offset, diff); - diff += 10s; - EXPECT_LE(offset, diff); + // Now offset shall be non zero + EXPECT_NE(USEC_ZERO, getOffset()); + + setTimeOwner(Owner::BOTH); - // Now get time shall be around past -1min time - auto epochNow = duration_cast<microseconds>( - system_clock::now().time_since_epoch()).count(); - auto elapsedGot = hostEpoch.elapsed(); - EXPECT_LT(elapsedGot, epochNow); - auto epochDiff = epochNow - elapsedGot; - diff = 1min; - EXPECT_GT(epochDiff, (diff - delta).count()); - EXPECT_LT(epochDiff, (diff + delta).count()); + // Now owner is BOTH, the offset shall be cleared + EXPECT_EQ(USEC_ZERO, getOffset()); } } diff --git a/test/mocked_bmc_time_change_listener.hpp b/test/mocked_bmc_time_change_listener.hpp new file mode 100644 index 0000000..9b2c81c --- /dev/null +++ b/test/mocked_bmc_time_change_listener.hpp @@ -0,0 +1,16 @@ +#pragma once +#include <gmock/gmock.h> +#include "bmc_time_change_listener.hpp" + +namespace phosphor { +namespace time { + +class MockBmcTimeChangeListener : public BmcTimeChangeListener { + public: + MOCK_METHOD1(onBmcTimeChanged, + void(const std::chrono::microseconds&)); +}; + +} // namespace time +} // namespace phosphor + |