[autotest] Bug fix on how lock is counted in host history.

The logic to count lock status in host history was not correct. This CL changes
the behavior to only apply lock to Ready status, since statuses other than
`Repair Failed` and Repairing indicate a dut is working on something, and
a dut with status of `Repair Failed` and Repairing is not 'available' any way.

BUG=None
TEST=unittest, run against a dut in lab
./host_history.py --hosts chromeos2-row4-rack6-host7 -l 24  -v

Change-Id: I513210f1d106fdd70214aa58330e0beed3c43f23
Reviewed-on: https://chromium-review.googlesource.com/229432
Tested-by: Dan Shi <dshi@chromium.org>
Reviewed-by: Fang Deng <fdeng@chromium.org>
Commit-Queue: Dan Shi <dshi@chromium.org>
diff --git a/site_utils/host_history_utils.py b/site_utils/host_history_utils.py
index 62a7018..bfb13bf 100644
--- a/site_utils/host_history_utils.py
+++ b/site_utils/host_history_utils.py
@@ -437,6 +437,12 @@
                            locked_intervals):
     """Returns a list of intervals along w/ statuses associated with them.
 
+    If the dut is in status Ready, i.e., int_status==Ready, the lock history
+    should be applied so that the time period when dut is locked is considered
+    as not available. Any other status is considered that dut is doing something
+    and being used. `Repair Failed` and Repairing are not checked with lock
+    status, since these two statuses indicate the dut is not available any way.
+
     @param t_start: start time
     @param t_end: end time
     @param int_status: status of [t_start, t_end] if not locked
@@ -459,7 +465,7 @@
                    'metadata': metadata}
     locked_info = {'status': 'Locked',
                    'metadata': {}}
-    if not locked_intervals:
+    if int_status != 'Ready' or not locked_intervals:
         statuses[(t_start, t_end)] = status_info
         return statuses
     for lock_start, lock_end in locked_intervals:
@@ -469,29 +475,37 @@
             # Timeline of status change: t_start t_end
             # Timeline of lock action:                   lock_start lock_end
             break
-        elif lock_end < t_start:
+        elif lock_end < prev_interval_end:
             # case 1
-            #                      t_start    t_end
+            #                      prev_interval_end    t_end
             # lock_start lock_end
             continue
-        elif lock_end < t_end and lock_start > t_start:
+        elif lock_end <= t_end and lock_start >= prev_interval_end:
             # case 2
-            # t_start                       t_end
-            #          lock_start lock_end
-            statuses[(prev_interval_end, lock_start)] = status_info
-            statuses[(lock_start, lock_end)] = locked_info
-        elif lock_end > t_start and lock_start < t_start:
+            # prev_interval_end                       t_end
+            #                    lock_start lock_end
+            # Lock happened in the middle, while the host stays in the same
+            # status, consider the lock has no effect on host history.
+            statuses[(prev_interval_end, lock_end)] = locked_info
+            prev_interval_end = lock_end
+        elif lock_end > prev_interval_end and lock_start < prev_interval_end:
             # case 3
-            #             t_start          t_end
-            # lock_start          lock_end
-            statuses[(t_start, lock_end)] = locked_info
+            #             prev_interval_end          t_end
+            # lock_start                    lock_end
+            # If the host status changed in the middle of being locked, consider
+            # the new status change as part of the host history.
+            statuses[(prev_interval_end, lock_end)] = locked_info
+            prev_interval_end = lock_end
         elif lock_start < t_end and lock_end > t_end:
             # case 4
-            # t_start             t_end
-            #          lock_start        lock_end
-            statuses[(prev_interval_end, lock_start)] = status_info
+            # prev_interval_end             t_end
+            #                    lock_start        lock_end
+            # If the lock happens in the middle of host status change, consider
+            # the lock has no effect on the host history for that status.
+            statuses[(prev_interval_end, t_end)] = status_info
             statuses[(lock_start, t_end)] = locked_info
-        prev_interval_end = lock_end
+            prev_interval_end = t_end
+            break
         # Otherwise we are in the case where lock_end < t_start OR
         # lock_start > t_end, which means the lock doesn't apply.
     if t_end > prev_interval_end:
diff --git a/site_utils/host_history_utils_unittest.py b/site_utils/host_history_utils_unittest.py
new file mode 100644
index 0000000..53be8c6
--- /dev/null
+++ b/site_utils/host_history_utils_unittest.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+
+import common
+from autotest_lib.client.common_lib.test_utils import unittest
+from autotest_lib.site_utils import host_history_utils
+
+class HostHistoryUtilsTests(unittest.TestCase):
+    """Test functions in host_history_utils.
+    """
+
+    def testCalculateStatusTimes(self):
+        """Test function calculate_status_times.
+        """
+        locked_intervals = [(2, 4), (4, 8)]
+        results = host_history_utils.calculate_status_times(
+                t_start=0, t_end=10, int_status='Ready', metadata={},
+                locked_intervals=locked_intervals)
+        expected = collections.OrderedDict(
+                [((0, 4), {'status': 'Locked', 'metadata': {}}),
+                 ((4, 8), {'status': 'Locked', 'metadata': {}}),
+                 ((8, 10), {'status': 'Ready', 'metadata': {}})])
+        self.assertEqual(results, expected)
+
+        locked_intervals = [(0, 4), (11, 14), (16, 18)]
+        results = host_history_utils.calculate_status_times(
+                t_start=10, t_end=15, int_status='Ready', metadata={},
+                locked_intervals=locked_intervals)
+        expected = collections.OrderedDict(
+                [((10, 14), {'status': 'Locked', 'metadata': {}}),
+                 ((14, 15), {'status': 'Ready', 'metadata': {}})])
+        self.assertEqual(results, expected)
+
+        locked_intervals = [(2, 4), (4, 8)]
+        results = host_history_utils.calculate_status_times(
+                t_start=0, t_end=10, int_status='Running', metadata={},
+                locked_intervals=locked_intervals)
+        expected = collections.OrderedDict(
+                [((0, 10), {'status': 'Running', 'metadata': {}})])
+        self.assertEqual(results, expected)
+
+
+if __name__ == '__main__':
+    unittest.main()