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>
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 @@
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 @@
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 @@
{
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 @@
// 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 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());
+ // Set time in NTP/BMC is not allowed
+ setTimeMode(Mode::NTP);
+ setTimeOwner(Owner::BMC);
+ checkSettingTimeNotAllowed();
}
-TEST_F(TestHostEpoch, setElapsedInFutureAndGet)
+TEST_F(TestHostEpoch, setElapsedInNtpHost)
{
- // Set to HOST owner so that we can set elapsed
+ // 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();
@@ -111,52 +254,38 @@
// 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());
- // 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());
+ setTimeOwner(Owner::BOTH);
+
+ // Now owner is BOTH, the offset shall be cleared
+ EXPECT_EQ(USEC_ZERO, getOffset());
}
}