Autotest: Clean up unneeded test-health storage.

The complete_failures.py script was still using file storage but was not doing
anything with it. This patch removes this storage as there is no longer plans to
store this information.

BUG=None
TEST=Unit tests.

Change-Id: I5fe092d6aebe133676c95d8fc5681881f9d1c894
Reviewed-on: https://gerrit.chromium.org/gerrit/65658
Reviewed-by: Dennis Jeffrey <dennisjeffrey@chromium.org>
Tested-by: Keyar Hood <keyar@chromium.org>
Commit-Queue: Keyar Hood <keyar@chromium.org>
diff --git a/frontend/health/complete_failures.py b/frontend/health/complete_failures.py
index e7efcf1..1b33de6 100755
--- a/frontend/health/complete_failures.py
+++ b/frontend/health/complete_failures.py
@@ -5,7 +5,7 @@
 # found in the LICENSE file.
 
 
-import datetime, shelve, sys
+import datetime, sys
 
 import common
 from autotest_lib.client.common_lib import mail
@@ -17,7 +17,6 @@
 from autotest_lib.frontend.health import utils
 
 
-_STORAGE_FILE = 'failure_storage'
 # Mark a test as failing too long if it has not passed in this many days
 _DAYS_TO_BE_FAILING_TOO_LONG = 60
 # Ignore any tests that have not ran in this many days
@@ -26,29 +25,6 @@
 _MAIL_RESULTS_TO = 'chromeos-lab-infrastructure@google.com'
 
 
-def load_storage():
-    """
-    Loads the storage object from disk.
-
-    This object keeps track of which tests we have already sent mail about so
-    we only send emails when the status of a test changes.
-
-    @return the storage object.
-
-    """
-    return shelve.open(_STORAGE_FILE)
-
-
-def save_storage(storage):
-    """
-    Saves the storage object to disk.
-
-    @param storage: The storage object to save to disk.
-
-    """
-    storage.close()
-
-
 def is_valid_test_name(name):
     """
     Returns if a test name is valid or not.
@@ -128,48 +104,40 @@
     return dict(always_failed.items() + running_passes.items())
 
 
-def store_results(tests, storage):
+def email_about_test_failure(failed_tests, all_tests):
     """
-    Store information about tests that have been failing for a long time.
+    Send an email about all the tests that have failed if there are any.
 
-
-    @param tests: The test_name:time_of_last_pass pairs.
-    @param storage: The storage object.
-
-    """
-    failing_time_cutoff = datetime.timedelta(_DAYS_TO_BE_FAILING_TOO_LONG)
-
-    today = datetime.datetime.today()
-    for test, last_fail in tests.iteritems():
-        if today - last_fail >= failing_time_cutoff:
-            if test not in storage:
-                storage[test] = today
-        else:
-            try:
-                del storage[test]
-            except KeyError:
-                pass
-
-
-def email_about_test_failure(storage, all_tests):
-    """
-    Send an email about all the tests in the storage object if there are any.
-
-    @param storage: The storage object.
+    @param failed_tests: The list of failed tests. This will be sorted in this
+        function.
     @param all_tests: All the names of tests that have been recently ran.
 
     """
-    if storage:
-        tests = sorted(storage.keys())
+    if failed_tests:
+        failed_tests.sort()
         mail.send(_MAIL_RESULTS_FROM,
                   [_MAIL_RESULTS_TO],
                   [],
                   'Long Failing Tests',
                   '%d/%d tests have been failing for at least %d days.\n'
                   'They are the following:\n\n%s'
-                  % (len(storage), len(all_tests),
+                  % (len(failed_tests), len(all_tests),
                      _DAYS_TO_BE_FAILING_TOO_LONG,
-                     '\n'.join(tests)))
+                     '\n'.join(failed_tests)))
+
+
+def filter_out_good_tests(tests):
+    """
+    Remove all tests that have passed recently enough to be good.
+
+    @param tests: The tests to filter on.
+
+    @return: A list of tests that have not passed for a long time.
+
+    """
+    cutoff = (datetime.datetime.today() -
+              datetime.timedelta(_DAYS_TO_BE_FAILING_TOO_LONG))
+    return [name for name, last_pass in tests.items() if last_pass < cutoff]
 
 
 def main():
@@ -180,15 +148,11 @@
     important if a nice way to test this code can be determined.
 
     """
-    storage = load_storage()
     all_test_names = get_recently_ran_test_names()
     last_passes = utils.get_last_pass_times()
     tests = get_tests_to_analyze(all_test_names, last_passes)
-    store_results(tests, storage)
-    email_about_test_failure(storage, all_test_names)
-    save_storage(storage)
-
-    return 0
+    failures = filter_out_good_tests(tests)
+    email_about_test_failure(failures, all_test_names)
 
 
 if __name__ == '__main__':
diff --git a/frontend/health/complete_failures_functional_test.py b/frontend/health/complete_failures_functional_test.py
index a2ebb6f..0685863 100755
--- a/frontend/health/complete_failures_functional_test.py
+++ b/frontend/health/complete_failures_functional_test.py
@@ -28,6 +28,29 @@
 GOOD_STATUS_IDX = 6
 FAIL_STATUS_IDX = 4
 
+ERROR_STATUS = models.Status(status_idx=2, word='ERROR')
+ABORT_STATUS = models.Status(status_idx=3, word='ABORT')
+FAIL_STATUS = models.Status(status_idx=4, word='FAIL')
+WARN_STATUS = models.Status(status_idx=5, word='WARN')
+GOOD_STATUS = models.Status(status_idx=6, word='GOOD')
+ALERT_STATUS = models.Status(status_idx=7, word='ALERT')
+
+
+def add_statuses():
+    """
+    Save the statuses to the in-memory database.
+
+    These normally exist in the database and the code expects them. However, the
+    normal test database setup does not do this for us.
+    """
+    ERROR_STATUS.save()
+    ABORT_STATUS.save()
+    FAIL_STATUS.save()
+    WARN_STATUS.save()
+    GOOD_STATUS.save()
+    ALERT_STATUS.save()
+
+
 # During the tests there is a point where Django does a type check on
 # datetime.datetime. Unfortunately this means when datetime is mocked out,
 # horrible failures happen when Django tries to do this check. The solution
@@ -44,15 +67,14 @@
     """
     Does a functional test of the complete_failures script.
 
-    It uses an in-memory database, mocks out the saving and loading of the
-    storage object and mocks out the sending of the email. Everything else
-    is a full run.
+    This uses an in-memory database but everything else is a full run.
 
     """
 
     def setUp(self):
         super(CompleteFailuresFunctionalTests, self).setUp()
         setup_test_environment.set_up()
+        add_statuses()
         # All of our tests will involve mocking out the datetime.today() class
         # method.
         self.mox.StubOutWithMock(MockDatetime, 'today')
@@ -61,10 +83,6 @@
         # We need to mock out the send function in all tests or else the
         # emails will be sent out during tests.
         self.mox.StubOutWithMock(mail, 'send')
-        # We also need to mock out the storage access as we do not want
-        # to worry about hitting a real file system
-        self.mox.StubOutWithMock(complete_failures, 'load_storage')
-        self.mox.StubOutWithMock(complete_failures, 'save_storage')
 
         self._orignal_too_late = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
 
@@ -84,30 +102,32 @@
         success_status = models.Status(status_idx=GOOD_STATUS_IDX)
         fail_status = models.Status(status_idx=FAIL_STATUS_IDX)
 
-        passing_test = models.Test(job=job, status=success_status,
-                                   kernel=kernel, machine=machine,
-                                   test='test1',
-                                   started_time=self.datetime(2012, 1, 1))
-        passing_test.save()
+        old_passing_test = models.Test(job=job, status=success_status,
+                                       kernel=kernel, machine=machine,
+                                       test='test1',
+                                       started_time=self.datetime(2012, 1, 1))
+        old_passing_test.save()
         failing_test = models.Test(job=job, status=fail_status,
                                    kernel=kernel, machine=machine,
                                    test='test2',
                                    started_time=self.datetime(2012,1,1))
         failing_test.save()
+        good_test = models.Test(job=job, status=success_status,
+                                kernel=kernel, machine=machine,
+                                test='test3',
+                                started_time=self.datetime(2012, 1, 20))
+        good_test.save()
 
         complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 10
-        storage = {}
-        complete_failures.load_storage().AndReturn(storage)
         MockDatetime.today().AndReturn(self.datetime(2012, 1, 21))
         MockDatetime.today().AndReturn(self.datetime(2012, 1, 21))
         mail.send('chromeos-test-health@google.com',
                   ['chromeos-lab-infrastructure@google.com'],
                   [],
                   'Long Failing Tests',
-                  '2/2 tests have been failing for at least %d days.\n'
+                  '2/3 tests have been failing for at least %d days.\n'
                   'They are the following:\n\ntest1\ntest2'
                   % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
-        complete_failures.save_storage(storage)
 
         self.mox.ReplayAll()
         complete_failures.main()
diff --git a/frontend/health/complete_failures_unittest.py b/frontend/health/complete_failures_unittest.py
index a5e0035..b49e0d8 100755
--- a/frontend/health/complete_failures_unittest.py
+++ b/frontend/health/complete_failures_unittest.py
@@ -4,7 +4,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import collections, datetime, unittest
+import datetime, unittest
 
 import mox
 
@@ -27,96 +27,9 @@
     pass
 
 
-class StoreResultsTests(mox.MoxTestBase):
-    """Test that entries are properly stored in the storage object."""
-
-    def setUp(self):
-        super(StoreResultsTests, self).setUp()
-        self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
-
-
-    def tearDown(self):
-        complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long
-        super(StoreResultsTests, self).tearDown()
-
-
-    def test_add_failing_test(self):
-        """Test adding a failing test to storage."""
-        # We will want to keep all the datetime logic intact and so we need to
-        # keep a reference to the unmocked datetime.
-        self.datetime = datetime.datetime
-        self.mox.StubOutWithMock(datetime, 'datetime')
-        datetime.datetime.today().AndReturn(self.datetime(2012, 1, 1))
-        complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
-
-        storage = {}
-
-        self.mox.ReplayAll()
-        complete_failures.store_results(
-                {'test': datetime.datetime.min}, storage)
-
-        self.assertEqual(storage['test'], self.datetime(2012, 1, 1))
-
-
-    def test_remove_test_if_it_has_succeeded_recently_enough(self):
-        """Test that we remove a passing test from the storage object."""
-        storage = {'test': datetime.datetime(2012, 1, 1)}
-        complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
-        today = datetime.datetime(2012, 4, 10)
-        safe_date = datetime.datetime(2012, 4, 9)
-
-        self.mox.StubOutWithMock(datetime, 'datetime')
-        datetime.datetime.today().AndReturn(today)
-
-        self.mox.ReplayAll()
-        complete_failures.store_results({'test': safe_date}, storage)
-
-        self.assertTrue('test' not in storage)
-
-
-    def test_no_crashing_on_test_that_has_never_failed_for_too_long(self):
-        """Test that we do not crash for tests that have always passed."""
-        storage = {}
-        complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
-        today = datetime.datetime(2012, 4, 10)
-        safe_date = datetime.datetime(2012, 4, 9)
-
-        self.mox.StubOutWithMock(datetime, 'datetime')
-        datetime.datetime.today().AndReturn(today)
-
-        self.mox.ReplayAll()
-        complete_failures.store_results({'test': safe_date}, storage)
-
-        self.assertTrue('test' not in storage)
-
-
-    def test_do_not_delete_if_still_failing(self):
-        """Test that an old failing test is not removed from storage."""
-        # We will want to keep all the datetime logic intact and so we need to
-        # keep a reference to the unmocked datetime.
-        self.datetime = datetime.datetime
-        today = datetime.datetime(2012, 1, 1)
-        self.mox.StubOutWithMock(datetime, 'datetime')
-        datetime.datetime.today().AndReturn(today)
-
-        storage = {'test': datetime.datetime.min}
-
-        # The ReplayAll is required or else a mox object sneaks its way into
-        # the storage object somehow.
-        self.mox.ReplayAll()
-        complete_failures.store_results(
-            {'test': datetime.datetime.min}, storage)
-
-        self.assertTrue('test' in storage)
-
-
 class EmailAboutTestFailureTests(mox.MoxTestBase):
     """
     Tests that emails are sent about failed tests.
-
-    This currently means an email is sent about all the entries of the
-    storage object.
-
     """
     def setUp(self):
         super(EmailAboutTestFailureTests, self).setUp()
@@ -133,8 +46,8 @@
         super(EmailAboutTestFailureTests, self).tearDown()
 
 
-    def test_email_sent_about_all_entries_in_storage(self):
-        """Test that the email report mentions all the entries in storage."""
+    def test_email_sent_about_all_failed_tests(self):
+        """Test that the email report mentions all the failed_tests."""
         complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
 
         mail.send(
@@ -146,13 +59,11 @@
                 'They are the following:\n\ntest'
                     % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
 
-        storage = {'test': datetime.datetime.min}
-        all_tests = set(storage.keys())
+        failures = ['test']
+        all_tests = set(failures)
 
-        # The ReplayAll is required or else a mox object sneaks its way into
-        # the storage object somehow.
         self.mox.ReplayAll()
-        complete_failures.email_about_test_failure(storage, all_tests)
+        complete_failures.email_about_test_failure(failures, all_tests)
 
 
     def test_email_has_test_names_sorted_alphabetically(self):
@@ -170,14 +81,11 @@
 
         # We use an OrderedDict to gurantee that the elements would be out of
         # order if we did a simple traversal.
-        storage = collections.OrderedDict((('test2', datetime.datetime.min),
-                                           ('test1', datetime.datetime.min)))
-        all_tests = set(storage.keys())
+        failures = ['test2', 'test1']
+        all_tests = set(failures)
 
-        # The ReplayAll is required or else a mox object sneaks its way into
-        # the storage object somehow.
         self.mox.ReplayAll()
-        complete_failures.email_about_test_failure(storage, all_tests)
+        complete_failures.email_about_test_failure(failures, all_tests)
 
 
     def test_email_count_of_total_number_of_tests(self):
@@ -193,13 +101,11 @@
                 'They are the following:\n\ntest'
                     % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
 
-        storage = {'test': datetime.datetime.min}
-        all_tests = set(storage.keys()) | {'not_failure'}
+        failures = ['test']
+        all_tests = set(failures) | {'not_failure'}
 
-        # The ReplayAll is required or else a mox object sneaks its way into
-        # the storage object somehow.
         self.mox.ReplayAll()
-        complete_failures.email_about_test_failure(storage, all_tests)
+        complete_failures.email_about_test_failure(failures, all_tests)
 
 
 class IsValidTestNameTests(test.TestCase):
@@ -239,7 +145,6 @@
         self.assertEqual(results, {})
 
 
-
 class GetRecentlyRanTestNamesTests(mox.MoxTestBase, test.TestCase):
     """Tests the get_recently_ran_test_names function."""
 
@@ -344,5 +249,48 @@
         self.assertEqual(results, {'test': datetime.datetime.min})
 
 
+class FilterOutGoodTestsTests(mox.MoxTestBase):
+    """Tests the filter_our_good_tests function."""
+
+    def setUp(self):
+        super(FilterOutGoodTestsTests, self).setUp()
+        self.mox.StubOutWithMock(MockDatetime, 'today')
+        self.datetime = datetime.datetime
+        datetime.datetime = MockDatetime
+        self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
+
+
+    def tearDown(self):
+        datetime.datetime = self.datetime
+        complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long
+        super(FilterOutGoodTestsTests, self).tearDown()
+
+
+    def test_remove_all_tests_that_have_passed_recently_enough(self):
+        """Test that all recently passing tests are not returned."""
+
+        complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 10
+        datetime.datetime.today().AndReturn(self.datetime(2012, 1, 21))
+
+        self.mox.ReplayAll()
+        result = complete_failures.filter_out_good_tests(
+                {'test': self.datetime(2012, 1, 20)})
+
+        self.assertEqual(result, [])
+
+
+    def test_keep_all_tests_that_have_not_passed_recently_enough(self):
+        """Test that the tests that have not recently passed are returned."""
+
+        complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 10
+        datetime.datetime.today().AndReturn(self.datetime(2012, 1, 21))
+
+        self.mox.ReplayAll()
+        result = complete_failures.filter_out_good_tests(
+                {'test': self.datetime(2012, 1, 10)})
+
+        self.assertEqual(result, ['test'])
+
+
 if __name__ == '__main__':
     unittest.main()