1#!/usr/bin/python 2# 3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import datetime, unittest 8 9import mox 10 11import common 12# This must come before the import of complete_failures in order to use the 13# in memory database. 14from autotest_lib.frontend import setup_django_readonly_environment 15from autotest_lib.frontend import setup_test_environment 16import complete_failures 17from autotest_lib.client.common_lib import mail 18from autotest_lib.frontend.tko import models 19from django import test 20 21 22GOOD_STATUS_IDX = 6 23 24# See complte_failurs_functional_tests.py for why we need this. 25class MockDatetime(datetime.datetime): 26 """Used to mock out parts of datetime.datetime.""" 27 pass 28 29 30class EmailAboutTestFailureTests(mox.MoxTestBase): 31 """ 32 Tests that emails are sent about failed tests. 33 """ 34 def setUp(self): 35 super(EmailAboutTestFailureTests, self).setUp() 36 37 # We need to mock out the send function in all tests or else the 38 # emails will be sent out during tests. 39 self.mox.StubOutWithMock(mail, 'send') 40 41 self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG 42 43 44 def tearDown(self): 45 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long 46 super(EmailAboutTestFailureTests, self).tearDown() 47 48 49 def test_email_sent_about_all_failed_tests(self): 50 """Test that the email report mentions all the failed_tests.""" 51 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60 52 53 mail.send( 54 'chromeos-test-health@google.com', 55 ['chromeos-lab-infrastructure@google.com'], 56 [], 57 'Long Failing Tests', 58 '1/1 tests have been failing for at least %d days.\n' 59 'They are the following:\n\ntest' 60 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG) 61 62 failures = ['test'] 63 all_tests = set(failures) 64 65 self.mox.ReplayAll() 66 complete_failures.email_about_test_failure(failures, all_tests) 67 68 69 def test_email_has_test_names_sorted_alphabetically(self): 70 """Test that the email report has entries sorted alphabetically.""" 71 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60 72 73 mail.send( 74 'chromeos-test-health@google.com', 75 ['chromeos-lab-infrastructure@google.com'], 76 [], 77 'Long Failing Tests', 78 '2/2 tests have been failing for at least %d days.\n' 79 'They are the following:\n\ntest1\ntest2' 80 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG) 81 82 # We use an OrderedDict to gurantee that the elements would be out of 83 # order if we did a simple traversal. 84 failures = ['test2', 'test1'] 85 all_tests = set(failures) 86 87 self.mox.ReplayAll() 88 complete_failures.email_about_test_failure(failures, all_tests) 89 90 91 def test_email_count_of_total_number_of_tests(self): 92 """Test that the email report displays total number of tests.""" 93 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60 94 95 mail.send( 96 'chromeos-test-health@google.com', 97 ['chromeos-lab-infrastructure@google.com'], 98 [], 99 'Long Failing Tests', 100 '1/2 tests have been failing for at least %d days.\n' 101 'They are the following:\n\ntest' 102 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG) 103 104 failures = ['test'] 105 all_tests = set(failures) | {'not_failure'} 106 107 self.mox.ReplayAll() 108 complete_failures.email_about_test_failure(failures, all_tests) 109 110 111class IsValidTestNameTests(test.TestCase): 112 """Tests the is_valid_test_name function.""" 113 114 def test_returns_true_for_valid_test_name(self): 115 """Test that a valid test name returns True.""" 116 name = 'TestName.TestName' 117 self.assertTrue(complete_failures.is_valid_test_name(name)) 118 119 120 def test_returns_false_if_name_has_slash_in_it(self): 121 """Test that a name with a slash in it returns False.""" 122 name = 'path/to/test' 123 self.assertFalse(complete_failures.is_valid_test_name(name)) 124 125 126 def test_returns_false_for_try_new_image_entries(self): 127 """Test that a name that starts with try_new_image returns False.""" 128 name = 'try_new_image-blah' 129 self.assertFalse(complete_failures.is_valid_test_name(name)) 130 131 132class PrepareLastPassesTests(test.TestCase): 133 """Tests the prepare_last_passes function.""" 134 135 def setUp(self): 136 super(PrepareLastPassesTests, self).setUp() 137 138 def tearDown(self): 139 super(PrepareLastPassesTests, self).tearDown() 140 141 def test_does_not_return_invalid_test_names(self): 142 """Tests that tests with invalid test names are not returned.""" 143 results = complete_failures.prepare_last_passes(['invalid_test/name']) 144 145 self.assertEqual(results, {}) 146 147 148class GetRecentlyRanTestNamesTests(mox.MoxTestBase, test.TestCase): 149 """Tests the get_recently_ran_test_names function.""" 150 151 def setUp(self): 152 super(GetRecentlyRanTestNamesTests, self).setUp() 153 self.mox.StubOutWithMock(MockDatetime, 'today') 154 self.datetime = datetime.datetime 155 datetime.datetime = MockDatetime 156 setup_test_environment.set_up() 157 self._orig_cutoff = complete_failures._DAYS_NOT_RUNNING_CUTOFF 158 159 160 def tearDown(self): 161 datetime.datetime = self.datetime 162 complete_failures._DAYS_NOT_RUNNING_CUTOFF = self._orig_cutoff 163 setup_test_environment.tear_down() 164 super(GetRecentlyRanTestNamesTests, self).tearDown() 165 166 167 def test_return_all_recently_ran_tests(self): 168 """Test that the function does as it says it does.""" 169 job = models.Job(job_idx=1) 170 kernel = models.Kernel(kernel_idx=1) 171 machine = models.Machine(machine_idx=1) 172 success_status = models.Status(status_idx=GOOD_STATUS_IDX) 173 174 recent = models.Test(job=job, status=success_status, 175 kernel=kernel, machine=machine, 176 test='recent', 177 started_time=self.datetime(2012, 1, 1)) 178 recent.save() 179 old = models.Test(job=job, status=success_status, 180 kernel=kernel, machine=machine, 181 test='old', 182 started_time=self.datetime(2011, 1, 2)) 183 old.save() 184 185 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 4)) 186 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60 187 188 self.mox.ReplayAll() 189 results = complete_failures.get_recently_ran_test_names() 190 191 self.assertEqual(set(results), set(['recent'])) 192 193 194 def test_returns_no_duplicate_names(self): 195 """Test that each test name appears only once.""" 196 job = models.Job(job_idx=1) 197 kernel = models.Kernel(kernel_idx=1) 198 machine = models.Machine(machine_idx=1) 199 success_status = models.Status(status_idx=GOOD_STATUS_IDX) 200 201 test = models.Test(job=job, status=success_status, 202 kernel=kernel, machine=machine, 203 test='test', 204 started_time=self.datetime(2012, 1, 1)) 205 test.save() 206 duplicate = models.Test(job=job, status=success_status, 207 kernel=kernel, machine=machine, 208 test='test', 209 started_time=self.datetime(2012, 1, 2)) 210 duplicate.save() 211 212 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 3)) 213 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60 214 215 self.mox.ReplayAll() 216 results = complete_failures.get_recently_ran_test_names() 217 218 self.assertEqual(len(results), 1) 219 220 221class GetTestsToAnalyzeTests(mox.MoxTestBase): 222 """Tests the get_tests_to_analyze function.""" 223 224 def test_returns_recent_test_names(self): 225 """Test should return all the test names in the database.""" 226 227 recent_tests = {'passing_test', 'failing_test'} 228 last_passes = {'passing_test': datetime.datetime(2012, 1 ,1), 229 'old_passing_test': datetime.datetime(2011, 1, 1)} 230 231 results = complete_failures.get_tests_to_analyze(recent_tests, 232 last_passes) 233 234 self.assertEqual(results, 235 {'passing_test': datetime.datetime(2012, 1, 1), 236 'failing_test': datetime.datetime.min}) 237 238 239 def test_returns_failing_tests_with_min_datetime(self): 240 """Test that never-passed tests are paired with datetime.min.""" 241 242 recent_tests = {'test'} 243 last_passes = {} 244 245 self.mox.ReplayAll() 246 results = complete_failures.get_tests_to_analyze(recent_tests, 247 last_passes) 248 249 self.assertEqual(results, {'test': datetime.datetime.min}) 250 251 252class FilterOutGoodTestsTests(mox.MoxTestBase): 253 """Tests the filter_our_good_tests function.""" 254 255 def setUp(self): 256 super(FilterOutGoodTestsTests, self).setUp() 257 self.mox.StubOutWithMock(MockDatetime, 'today') 258 self.datetime = datetime.datetime 259 datetime.datetime = MockDatetime 260 self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG 261 262 263 def tearDown(self): 264 datetime.datetime = self.datetime 265 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long 266 super(FilterOutGoodTestsTests, self).tearDown() 267 268 269 def test_remove_all_tests_that_have_passed_recently_enough(self): 270 """Test that all recently passing tests are not returned.""" 271 272 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 10 273 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 21)) 274 275 self.mox.ReplayAll() 276 result = complete_failures.filter_out_good_tests( 277 {'test': self.datetime(2012, 1, 20)}) 278 279 self.assertEqual(result, []) 280 281 282 def test_keep_all_tests_that_have_not_passed_recently_enough(self): 283 """Test that the tests that have not recently passed are returned.""" 284 285 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 10 286 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 21)) 287 288 self.mox.ReplayAll() 289 result = complete_failures.filter_out_good_tests( 290 {'test': self.datetime(2012, 1, 10)}) 291 292 self.assertEqual(result, ['test']) 293 294 295if __name__ == '__main__': 296 unittest.main() 297