Add unit tests for duration metrics

As a side-effect, move utils::GetMonotonicTime() into the newly added
ClockInterface type.

BUG=None
TEST=Unit tests pass

Change-Id: I972a7e4ba37b63f96348fbeda901697b8ba2fc05
Reviewed-on: https://gerrit.chromium.org/gerrit/48814
Reviewed-by: Chris Sosa <sosa@chromium.org>
Tested-by: David Zeuthen <zeuthen@chromium.org>
Commit-Queue: David Zeuthen <zeuthen@chromium.org>
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index e7b7ed2..28017dc 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -11,9 +11,11 @@
 #include "gtest/gtest.h"
 
 #include "update_engine/constants.h"
+#include "update_engine/fake_clock.h"
 #include "update_engine/mock_system_state.h"
 #include "update_engine/omaha_request_action.h"
 #include "update_engine/payload_state.h"
+#include "update_engine/prefs.h"
 #include "update_engine/prefs_mock.h"
 #include "update_engine/test_utils.h"
 #include "update_engine/utils.h"
@@ -745,4 +747,69 @@
   EXPECT_EQ(0, payload_state.GetNumReboots());
 }
 
+TEST(PayloadStateTest, DurationsAreCorrect) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  MockSystemState mock_system_state;
+  FakeClock fake_clock;
+  Prefs prefs;
+  string temp_dir;
+
+  // Set the clock to a well-known time - 1 second on the wall-clock
+  // and 2 seconds on the monotonic clock
+  fake_clock.SetWallclockTime(Time::FromInternalValue(1000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(2000000));
+
+  // We need persistent preferences for this test
+  EXPECT_TRUE(utils::MakeTempDirectory("/tmp/PayloadStateDurationTests.XXXXXX",
+                                       &temp_dir));
+  prefs.Init(FilePath(temp_dir));
+
+  mock_system_state.set_clock(&fake_clock);
+  mock_system_state.set_prefs(&prefs);
+  EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
+
+  // Check that durations are correct for a successful update where
+  // time has advanced 7 seconds on the wall clock and 4 seconds on
+  // the monotonic clock.
+  SetupPayloadStateWith2Urls("Hash8593", &payload_state, &response);
+  fake_clock.SetWallclockTime(Time::FromInternalValue(8000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(6000000));
+  payload_state.UpdateSucceeded();
+  EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 7000000);
+  EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 4000000);
+
+  // Check that durations are reset when a new response comes in.
+  SetupPayloadStateWith2Urls("Hash8594", &payload_state, &response);
+  EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 0);
+  EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 0);
+
+  // Advance time a bit (10 secs), simulate download progress and
+  // check that durations are updated.
+  fake_clock.SetWallclockTime(Time::FromInternalValue(18000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(16000000));
+  payload_state.DownloadProgress(10);
+  EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 10000000);
+  EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 10000000);
+
+  // Now simulate a reboot by resetting monotonic time (to 5000) and
+  // creating a new PayloadState object and check that we load the
+  // durations correctly (e.g. they are the same as before).
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(5000));
+  PayloadState payload_state2;
+  EXPECT_TRUE(payload_state2.Initialize(&mock_system_state));
+  EXPECT_EQ(payload_state2.GetUpdateDuration().InMicroseconds(), 10000000);
+  EXPECT_EQ(payload_state2.GetUpdateDurationUptime().InMicroseconds(),10000000);
+
+  // Advance wall-clock by 7 seconds and monotonic clock by 6 seconds
+  // and check that the durations are increased accordingly.
+  fake_clock.SetWallclockTime(Time::FromInternalValue(25000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(6005000));
+  payload_state2.UpdateSucceeded();
+  EXPECT_EQ(payload_state2.GetUpdateDuration().InMicroseconds(), 17000000);
+  EXPECT_EQ(payload_state2.GetUpdateDurationUptime().InMicroseconds(),16000000);
+
+  EXPECT_TRUE(utils::RecursiveUnlinkDir(temp_dir));
+}
+
 }