sched-host-tran: handle with the scheduled time

Add the basic process to handle with the scheduled time

Tested:
1. Scheduled time is 0
  # busctl set-property xyz.openbmc_project.State.ScheduledHostTransition \
    /xyz/openbmc_project/state/host0 \
    xyz.openbmc_project.State.ScheduledHostTransition ScheduledTime t 1
    ------
    Feb 19 08:09:47 witherspoon phosphor-scheduled-host-transition[28263]: \
    The function Scheduled Host Transition is disabled.
2. Scheduled time is the past
 # busctl set-property xyz.openbmc_project.State.ScheduledHostTransition \
   /xyz/openbmc_project/state/host0 \
   xyz.openbmc_project.State.ScheduledHostTransition ScheduledTime t 1582100042
   ------
   Failed to set property ScheduledTime on interface xyz.openbmc_project.State.\
   ScheduledHostTransition: Scheduled time is in the past
   Feb 19 08:14:42 witherspoon phosphor-scheduled-host-transition[28263]: \
   Scheduled time is earlier than current time. Fail to do host transition.
   Feb 19 08:14:42 witherspoon phosphor-scheduled-host-transition[28263]: \
   Scheduled time is in the past

Change-Id: I0b6a98dcb6d0e70336bf42fc88a633abf3e64633
Signed-off-by: Carol Wang <wangkair@cn.ibm.com>
diff --git a/meson.build b/meson.build
index 5359c5e..790677d 100644
--- a/meson.build
+++ b/meson.build
@@ -145,6 +145,7 @@
 # need to define and build the tests from here
 if not build_tests.disabled()
 gtest = dependency('gtest', main: true, disabler: true, required: build_tests)
+gmock = dependency('gmock', disabler: true, required: build_tests)
   test(
       'test_systemd_parser',
       executable('test_systemd_parser',
@@ -170,4 +171,17 @@
           include_directories: '../'
       )
   )
+
+  test(
+      'test_scheduled_host_transition',
+      executable('test_scheduled_host_transition',
+          './test/test_scheduled_host_transition.cpp',
+          'scheduled_host_transition.cpp',
+          dependencies: [
+              gtest, gmock, sdbusplus, sdeventplus, phosphorlogging,
+          ],
+          implicit_include_directories: true,
+          include_directories: '../'
+      )
+  )
 endif
diff --git a/scheduled_host_transition.cpp b/scheduled_host_transition.cpp
index e636579..4d59ff9 100644
--- a/scheduled_host_transition.cpp
+++ b/scheduled_host_transition.cpp
@@ -1,5 +1,11 @@
 #include "scheduled_host_transition.hpp"
 
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/ScheduledTime/error.hpp>
+#include <chrono>
+
 namespace phosphor
 {
 namespace state
@@ -7,14 +13,45 @@
 namespace manager
 {
 
+using namespace std::chrono;
+using namespace phosphor::logging;
+using namespace xyz::openbmc_project::ScheduledTime;
+using InvalidTimeError =
+    sdbusplus::xyz::openbmc_project::ScheduledTime::Error::InvalidTime;
 using HostTransition =
     sdbusplus::xyz::openbmc_project::State::server::ScheduledHostTransition;
 
 uint64_t ScheduledHostTransition::scheduledTime(uint64_t value)
 {
+    if (value == 0)
+    {
+        // 0 means the function Scheduled Host Transition is disabled
+        // to do: check timer, stop timer
+        log<level::INFO>("The function Scheduled Host Transition is disabled.");
+        return HostTransition::scheduledTime(value);
+    }
+
+    auto deltaTime = seconds(value) - getTime();
+    if (deltaTime < seconds(0))
+    {
+        log<level::ERR>("Scheduled time is earlier than current time. Fail to "
+                        "do host transition.");
+        elog<InvalidTimeError>(
+            InvalidTime::REASON("Scheduled time is in the past"));
+    }
+    else
+    {
+        // start timer
+    }
     return HostTransition::scheduledTime(value);
 }
 
+seconds ScheduledHostTransition::getTime()
+{
+    auto now = system_clock::now();
+    return duration_cast<seconds>(now.time_since_epoch());
+}
+
 } // namespace manager
 } // namespace state
 } // namespace phosphor
diff --git a/scheduled_host_transition.hpp b/scheduled_host_transition.hpp
index ebf3fba..c125638 100644
--- a/scheduled_host_transition.hpp
+++ b/scheduled_host_transition.hpp
@@ -4,6 +4,8 @@
 #include <phosphor-logging/log.hpp>
 #include <xyz/openbmc_project/State/ScheduledHostTransition/server.hpp>
 
+class TestScheduledHostTransition;
+
 namespace phosphor
 {
 namespace state
@@ -38,7 +40,15 @@
      * error.
      **/
     uint64_t scheduledTime(uint64_t value) override;
+
+  private:
+    friend class TestScheduledHostTransition;
+    /** @brief Get current time
+     *
+     *  @return - return current epoch time
+     */
+    std::chrono::seconds getTime();
 };
 } // namespace manager
 } // namespace state
-} // namespace phosphor
\ No newline at end of file
+} // namespace phosphor
diff --git a/test/test_scheduled_host_transition.cpp b/test/test_scheduled_host_transition.cpp
new file mode 100644
index 0000000..f5ffa2c
--- /dev/null
+++ b/test/test_scheduled_host_transition.cpp
@@ -0,0 +1,54 @@
+#include "scheduled_host_transition.hpp"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/test/sdbus_mock.hpp>
+#include <xyz/openbmc_project/ScheduledTime/error.hpp>
+
+namespace phosphor
+{
+namespace state
+{
+namespace manager
+{
+
+using namespace std::chrono;
+using InvalidTimeError =
+    sdbusplus::xyz::openbmc_project::ScheduledTime::Error::InvalidTime;
+
+class TestScheduledHostTransition : public testing::Test
+{
+  public:
+    sdbusplus::SdBusMock sdbusMock;
+    sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
+    ScheduledHostTransition scheduledHostTransition;
+
+    TestScheduledHostTransition() : scheduledHostTransition(mockedBus, "")
+    {
+        // Empty
+    }
+
+    seconds getCurrentTime()
+    {
+        return scheduledHostTransition.getTime();
+    }
+};
+
+TEST_F(TestScheduledHostTransition, disableHostTransition)
+{
+    EXPECT_EQ(scheduledHostTransition.scheduledTime(0), 0);
+}
+
+TEST_F(TestScheduledHostTransition, invalidScheduledTime)
+{
+    // scheduled time is 1 min earlier than current time
+    uint64_t schTime =
+        static_cast<uint64_t>((getCurrentTime() - seconds(60)).count());
+    EXPECT_THROW(scheduledHostTransition.scheduledTime(schTime),
+                 InvalidTimeError);
+}
+
+} // namespace manager
+} // namespace state
+} // namespace phosphor