#include "config.h" #include "host_epoch.hpp" #include "types.hpp" #include "utils.hpp" #include #include #include namespace phosphor { namespace time { using namespace std::chrono; using namespace std::chrono_literals; using NotAllowed = sdbusplus::xyz::openbmc_project::Time::Error::NotAllowed; const constexpr microseconds USEC_ZERO{0}; class TestHostEpoch : public testing::Test { public: sdbusplus::bus::bus bus; HostEpoch hostEpoch; static constexpr auto FILE_NOT_EXIST = "path/to/file-not-exist"; static constexpr auto FILE_OFFSET = "saved_host_offset"; const microseconds delta = 2s; TestHostEpoch() : bus(sdbusplus::bus::new_default()), hostEpoch(bus, OBJPATH_HOST) { // Make sure the file does not exist std::remove(FILE_NOT_EXIST); } ~TestHostEpoch() { // Cleanup test file std::remove(FILE_OFFSET); } // Proxies for HostEpoch's private members and functions Mode getTimeMode() { return hostEpoch.timeMode; } Owner getTimeOwner() { return hostEpoch.timeOwner; } microseconds getOffset() { return hostEpoch.offset; } void setOffset(microseconds us) { hostEpoch.offset = us; } void setTimeOwner(Owner 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; EXPECT_THROW(hostEpoch.elapsed(hostEpoch.elapsed() + diff.count()), NotAllowed); EXPECT_EQ(0, getOffset().count()); } 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(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(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()); } }; TEST_F(TestHostEpoch, empty) { // Default mode/owner is MANUAL/BOTH EXPECT_EQ(Mode::Manual, getTimeMode()); EXPECT_EQ(Owner::Both, getTimeOwner()); } TEST_F(TestHostEpoch, readDataFileNotExist) { // When file does not exist, the default offset shall be 0 microseconds offset(0); auto value = utils::readData(FILE_NOT_EXIST); EXPECT_EQ(0, value); } TEST_F(TestHostEpoch, writeAndReadData) { // Write offset to file microseconds offsetToWrite(1234567); utils::writeData(FILE_OFFSET, offsetToWrite.count()); // Read it back microseconds offsetToRead; offsetToRead = microseconds(utils::readData(FILE_OFFSET)); EXPECT_EQ(offsetToWrite, offsetToRead); } TEST_F(TestHostEpoch, setElapsedInNtpBmc) { // Set time in NTP/BMC is not allowed setTimeMode(Mode::NTP); setTimeOwner(Owner::BMC); checkSettingTimeNotAllowed(); } 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, setElapsedInNtpBoth) { // 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(); 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 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, clearOffsetOnOwnerChange) { EXPECT_EQ(USEC_ZERO, getOffset()); setTimeOwner(Owner::Split); hostEpoch.onBmcTimeChanged(microseconds(hostEpoch.elapsed()) + 1min); // Now offset shall be non zero EXPECT_NE(USEC_ZERO, getOffset()); setTimeOwner(Owner::Both); // Now owner is BOTH, the offset shall be cleared EXPECT_EQ(USEC_ZERO, getOffset()); } } // namespace time } // namespace phosphor