blob: 1c28a9bd29da22414b401bdaf1be8a0e64bcc7ef [file] [log] [blame]
Keyar Hood1a3c8dd2013-05-29 17:41:50 -07001#!/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
Keyar Hood788e4982013-06-18 14:34:28 -070011import 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
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070017from autotest_lib.client.common_lib import mail
Keyar Hood788e4982013-06-18 14:34:28 -070018from autotest_lib.frontend.tko import models
19from django import test
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070020
21
Keyar Hood788e4982013-06-18 14:34:28 -070022GOOD_STATUS_IDX = 6
23FAIL_STATUS_IDX = 4
24
Keyar Hoode7b11842013-06-25 15:41:59 -070025# See complte_failurs_functional_tests.py for why we need this.
26class MockDatetime(datetime.datetime):
27 """Used to mock out parts of datetime.datetime."""
28 pass
29
30
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070031class EmailAboutTestFailureTests(mox.MoxTestBase):
32 """
33 Test the core logic of the comlete_failures.py script.
34
35 The core logic is to send emails only if we have not yet done so for a
36 given test before and to take actions if the test has been failing for
37 long enough.
38
39 """
40 def setUp(self):
41 super(EmailAboutTestFailureTests, self).setUp()
42
43 # We need to mock out the send function in all tests or else the
44 # emails will be sent out during tests.
45 self.mox.StubOutWithMock(mail, 'send')
46
Keyar Hoode7b11842013-06-25 15:41:59 -070047 self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070048
49
50 def tearDown(self):
Keyar Hoode7b11842013-06-25 15:41:59 -070051 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long
Keyar Hood788e4982013-06-18 14:34:28 -070052 super(EmailAboutTestFailureTests, self).tearDown()
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070053
54
Keyar Hood4d5f9d12013-06-28 15:36:45 -070055 def test_deal_with_failing_test(self):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070056 """
57 Test adding a failing test to the storage.
58
Keyar Hood4d5f9d12013-06-28 15:36:45 -070059 We expect the email sending code to be called.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070060
61 """
62 # We will want to keep all the datetime logic intact and so we need to
63 # keep a reference to the unmocked datetime.
64 self.datetime = datetime.datetime
65 self.mox.StubOutWithMock(datetime, 'datetime')
66 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 1))
67 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
68
69 mail.send(
70 'chromeos-test-health@google.com',
71 ['chromeos-lab-infrastructure@google.com'],
72 [],
73 'Long Failing Tests',
74 'The following tests have been failing for at '
75 'least %i days:\n\ntest'
76 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
77
78 storage = {}
79
80 # The ReplayAll is required or else a mox object sneaks its way into
81 # the storage object somehow.
82 self.mox.ReplayAll()
83 complete_failures.email_about_test_failure(
84 {'test': datetime.datetime.min}, storage)
85
86 self.assertEqual(storage['test'], self.datetime(2012, 1, 1))
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070087
88
89 def test_remove_test_if_it_has_succeeded_recently_enough(self):
90 """Test that we remove a passing test from the storage object."""
91 storage = {'test': datetime.datetime(2012, 1, 1)}
92 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
93 today = datetime.datetime(2012, 4, 10)
94 safe_date = datetime.datetime(2012, 4, 9)
95
96 self.mox.StubOutWithMock(datetime, 'datetime')
97 datetime.datetime.today().AndReturn(today)
98
99 self.mox.ReplayAll()
100 complete_failures.email_about_test_failure({'test': safe_date}, storage)
101
102 self.assertTrue('test' not in storage)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700103
104
105 def test_no_crashing_on_test_that_has_never_failed_for_too_long(self):
106 """Test that we do not crash for tests that have always passed."""
107 storage = {}
108 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
109 today = datetime.datetime(2012,4,10)
110 safe_date = datetime.datetime(2012,4,9)
111
112 self.mox.StubOutWithMock(datetime, 'datetime')
113 datetime.datetime.today().AndReturn(today)
114
115 self.mox.ReplayAll()
116 complete_failures.email_about_test_failure({'test': safe_date}, storage)
117
118 self.assertTrue('test' not in storage)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700119
120
Keyar Hood4d5f9d12013-06-28 15:36:45 -0700121 def test_send_email_if_test_already_in_storage(self):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700122 """Test only send emails on newly problematic tests."""
123 storage = {'test': datetime.datetime(2012, 1, 1)}
124 self.datetime = datetime.datetime
125 self.mox.StubOutWithMock(datetime, 'datetime')
126 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 1))
127
Keyar Hood4d5f9d12013-06-28 15:36:45 -0700128 mail.send(
129 'chromeos-test-health@google.com',
130 ['chromeos-lab-infrastructure@google.com'],
131 [],
132 'Long Failing Tests',
133 'The following tests have been failing for at '
134 'least %i days:\n\ntest'
135 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
136
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700137 self.mox.ReplayAll()
138 complete_failures.email_about_test_failure(
139 {'test': datetime.datetime.min}, storage)
140
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700141
142 def test_do_not_delete_if_still_failing(self):
143 """Test that an old failing test is not removed from storage."""
144 # We will want to keep all the datetime logic intact and so we need to
145 # keep a reference to the unmocked datetime.
146 self.datetime = datetime.datetime
147 today = datetime.datetime(2012, 1, 1)
148 self.mox.StubOutWithMock(datetime, 'datetime')
149 datetime.datetime.today().AndReturn(today)
150
151 storage = {'test': datetime.datetime.min}
152
Keyar Hood4d5f9d12013-06-28 15:36:45 -0700153 mail.send(
154 'chromeos-test-health@google.com',
155 ['chromeos-lab-infrastructure@google.com'],
156 [],
157 'Long Failing Tests',
158 'The following tests have been failing for at '
159 'least %i days:\n\ntest'
160 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
161
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700162 # The ReplayAll is required or else a mox object sneaks its way into
163 # the storage object somehow.
164 self.mox.ReplayAll()
165 complete_failures.email_about_test_failure(
166 {'test': datetime.datetime.min}, storage)
167
168 self.assertTrue('test' in storage)
Keyar Hood788e4982013-06-18 14:34:28 -0700169
170
Keyar Hood005539d2013-06-24 14:27:57 -0700171class IsValidTestNameTests(test.TestCase):
172 """Tests the is_valid_test_name function."""
173
174 def test_returns_true_for_valid_test_name(self):
175 """Test that a valid test name returns True."""
176 name = 'TestName.TestName'
177 self.assertTrue(complete_failures.is_valid_test_name(name))
178
179
180 def test_returns_false_if_name_has_slash_in_it(self):
181 """Test that a name with a slash in it returns False."""
182 name = 'path/to/test'
183 self.assertFalse(complete_failures.is_valid_test_name(name))
184
185
186 def test_returns_false_for_try_new_image_entries(self):
187 """Test that a name that starts with try_new_image returns False."""
188 name = 'try_new_image-blah'
189 self.assertFalse(complete_failures.is_valid_test_name(name))
190
191
Keyar Hood788e4982013-06-18 14:34:28 -0700192class GetLastPassTimesTests(mox.MoxTestBase, test.TestCase):
193 """Tests the get_last_pass_times function."""
194
195 def setUp(self):
196 super(GetLastPassTimesTests, self).setUp()
197 setup_test_environment.set_up()
198
199
200 def tearDown(self):
201 setup_test_environment.tear_down()
202 super(GetLastPassTimesTests, self).tearDown()
203
204
205 def test_return_most_recent_pass(self):
206 """The last time a test passed should be returned."""
207 # To add a test entry to the database, Django the test object to
208 # be instantiated with various other model instances. We give these
209 # instances dummy id values.
Keyar Hood005539d2013-06-24 14:27:57 -0700210 job = models.Job(job_idx=1)
211 kernel = models.Kernel(kernel_idx=1)
212 machine = models.Machine(machine_idx=1)
213 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700214
Keyar Hood005539d2013-06-24 14:27:57 -0700215 early_pass = models.Test(job=job, status=success_status,
216 kernel=kernel, machine=machine,
217 test='test',
218 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700219 early_pass.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700220 late_pass = models.Test(job=job, status=success_status,
221 kernel=kernel, machine=machine,
222 test='test',
223 started_time=datetime.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700224 late_pass.save()
225
226 results = complete_failures.get_last_pass_times()
227
228 self.assertEquals(results, {'test': datetime.datetime(2012, 1, 2)})
229
230
231 def test_only_return_passing_tests(self):
232 """Tests that only tests that have passed at some point are returned."""
Keyar Hood005539d2013-06-24 14:27:57 -0700233 job = models.Job(job_idx=1)
234 kernel = models.Kernel(kernel_idx=1)
235 machine = models.Machine(machine_idx=1)
236 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
237 fail_status = models.Status(status_idx=FAIL_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700238
Keyar Hood005539d2013-06-24 14:27:57 -0700239 passing_test = models.Test(job=job, status=success_status,
240 kernel=kernel, machine=machine,
241 test='passing_test',
242 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700243 passing_test.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700244 failing_test = models.Test(job=job, status=fail_status,
245 kernel=kernel, machine=machine,
246 test='failing_test',
247 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700248 failing_test.save()
249
250 results = complete_failures.get_last_pass_times()
251
252 self.assertEquals(results,
253 {'passing_test': datetime.datetime(2012, 1, 1)})
254
255
256 def test_return_all_passing_tests(self):
257 """This function returns all tests that passed at least once."""
Keyar Hood005539d2013-06-24 14:27:57 -0700258 job = models.Job(job_idx=1)
259 kernel = models.Kernel(kernel_idx=1)
260 machine = models.Machine(machine_idx=1)
261 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700262
Keyar Hood005539d2013-06-24 14:27:57 -0700263 test1 = models.Test(job=job, status=success_status,
264 kernel=kernel, machine=machine,
265 test='test1',
266 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700267 test1.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700268 test2 = models.Test(job=job, status=success_status,
269 kernel=kernel, machine=machine,
270 test='test2',
271 started_time=datetime.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700272 test2.save()
273
274 results = complete_failures.get_last_pass_times()
275
276 self.assertEquals(results, {'test1': datetime.datetime(2012, 1, 1),
277 'test2': datetime.datetime(2012, 1, 2)})
278
279
Keyar Hood005539d2013-06-24 14:27:57 -0700280 def test_does_not_return_invalid_test_names(self):
281 """Tests that tests with invalid test names are not returned."""
282 job = models.Job(job_idx=1)
283 kernel = models.Kernel(kernel_idx=1)
284 machine = models.Machine(machine_idx=1)
285 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
286 fail_status = models.Status(status_idx=FAIL_STATUS_IDX)
287
288 invalid_test = models.Test(job=job, status=success_status,
289 kernel=kernel, machine=machine,
290 test='invalid_test/name',
291 started_time=datetime.datetime(2012, 1, 1))
292 invalid_test.save()
293
294 results = complete_failures.get_last_pass_times()
295
296 self.assertTrue(not results)
297
298
Keyar Hoode7b11842013-06-25 15:41:59 -0700299class GetRecentlyRanTestNamesTests(mox.MoxTestBase, test.TestCase):
300 """Tests the get_recently_ran_test_names function."""
Keyar Hood788e4982013-06-18 14:34:28 -0700301
302 def setUp(self):
Keyar Hoode7b11842013-06-25 15:41:59 -0700303 super(GetRecentlyRanTestNamesTests, self).setUp()
304 self.mox.StubOutWithMock(MockDatetime, 'today')
305 self.datetime = datetime.datetime
306 datetime.datetime = MockDatetime
Keyar Hood788e4982013-06-18 14:34:28 -0700307 setup_test_environment.set_up()
Keyar Hoode7b11842013-06-25 15:41:59 -0700308 self._orig_cutoff = complete_failures._DAYS_NOT_RUNNING_CUTOFF
Keyar Hood788e4982013-06-18 14:34:28 -0700309
310
311 def tearDown(self):
Keyar Hoode7b11842013-06-25 15:41:59 -0700312 datetime.datetime = self.datetime
313 complete_failures._DAYS_NOT_RUNNING_CUTOFF = self._orig_cutoff
Keyar Hood788e4982013-06-18 14:34:28 -0700314 setup_test_environment.tear_down()
Keyar Hoode7b11842013-06-25 15:41:59 -0700315 super(GetRecentlyRanTestNamesTests, self).tearDown()
Keyar Hood788e4982013-06-18 14:34:28 -0700316
317
Keyar Hoode7b11842013-06-25 15:41:59 -0700318 def test_return_all_recently_ran_tests(self):
Keyar Hood788e4982013-06-18 14:34:28 -0700319 """Test that the function does as it says it does."""
Keyar Hood005539d2013-06-24 14:27:57 -0700320 job = models.Job(job_idx=1)
321 kernel = models.Kernel(kernel_idx=1)
322 machine = models.Machine(machine_idx=1)
323 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700324
Keyar Hoode7b11842013-06-25 15:41:59 -0700325 recent = models.Test(job=job, status=success_status,
326 kernel=kernel, machine=machine,
327 test='recent',
328 started_time=self.datetime(2012, 1, 1))
329 recent.save()
330 old = models.Test(job=job, status=success_status,
331 kernel=kernel, machine=machine,
332 test='old',
333 started_time=self.datetime(2011, 1, 2))
334 old.save()
Keyar Hood788e4982013-06-18 14:34:28 -0700335
Keyar Hoode7b11842013-06-25 15:41:59 -0700336 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 4))
337 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60
Keyar Hood788e4982013-06-18 14:34:28 -0700338
Keyar Hoode7b11842013-06-25 15:41:59 -0700339 self.mox.ReplayAll()
340 results = complete_failures.get_recently_ran_test_names()
341
342 self.assertEqual(set(results), set(['recent']))
Keyar Hood788e4982013-06-18 14:34:28 -0700343
344
345 def test_returns_no_duplicate_names(self):
346 """Test that each test name appears only once."""
Keyar Hood005539d2013-06-24 14:27:57 -0700347 job = models.Job(job_idx=1)
348 kernel = models.Kernel(kernel_idx=1)
349 machine = models.Machine(machine_idx=1)
350 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700351
Keyar Hood005539d2013-06-24 14:27:57 -0700352 test = models.Test(job=job, status=success_status,
353 kernel=kernel, machine=machine,
354 test='test',
Keyar Hoode7b11842013-06-25 15:41:59 -0700355 started_time=self.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700356 test.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700357 duplicate = models.Test(job=job, status=success_status,
358 kernel=kernel, machine=machine,
359 test='test',
Keyar Hoode7b11842013-06-25 15:41:59 -0700360 started_time=self.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700361 duplicate.save()
362
Keyar Hoode7b11842013-06-25 15:41:59 -0700363 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 3))
364 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60
365
366 self.mox.ReplayAll()
367 results = complete_failures.get_recently_ran_test_names()
Keyar Hood788e4982013-06-18 14:34:28 -0700368
369 self.assertEqual(len(results), 1)
370
371
Keyar Hood005539d2013-06-24 14:27:57 -0700372 def test_does_not_return_invalid_test_names(self):
373 """Tests that only tests with invalid test names are not returned."""
374 job = models.Job(job_idx=1)
375 kernel = models.Kernel(kernel_idx=1)
376 machine = models.Machine(machine_idx=1)
377 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
378
379 invalid_test = models.Test(job=job, status=success_status,
380 kernel=kernel, machine=machine,
381 test='invalid_test/name',
Keyar Hoode7b11842013-06-25 15:41:59 -0700382 started_time=self.datetime(2012, 1, 1))
Keyar Hood005539d2013-06-24 14:27:57 -0700383 invalid_test.save()
384
Keyar Hoode7b11842013-06-25 15:41:59 -0700385 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 2))
386 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60
387
388 self.mox.ReplayAll()
389 results = complete_failures.get_recently_ran_test_names()
Keyar Hood005539d2013-06-24 14:27:57 -0700390
391 self.assertTrue(not results)
392
393
Keyar Hood788e4982013-06-18 14:34:28 -0700394class GetTestsToAnalyzeTests(mox.MoxTestBase):
395 """Tests the get_tests_to_analyze function."""
396
Keyar Hoode7b11842013-06-25 15:41:59 -0700397 def test_returns_recent_test_names(self):
Keyar Hood788e4982013-06-18 14:34:28 -0700398 """Test should return all the test names in the database."""
399 self.mox.StubOutWithMock(complete_failures, 'get_last_pass_times')
Keyar Hoode7b11842013-06-25 15:41:59 -0700400 self.mox.StubOutWithMock(complete_failures,
401 'get_recently_ran_test_names')
Keyar Hood788e4982013-06-18 14:34:28 -0700402
403 complete_failures.get_last_pass_times().AndReturn({'passing_test':
Keyar Hoode7b11842013-06-25 15:41:59 -0700404 datetime.datetime(2012, 1 ,1),
405 'old_passing_test': datetime.datetime(2011, 1, 1)})
406 complete_failures.get_recently_ran_test_names().AndReturn(
407 {'passing_test',
408 'failing_test'})
Keyar Hood788e4982013-06-18 14:34:28 -0700409 self.mox.ReplayAll()
410 results = complete_failures.get_tests_to_analyze()
411
Keyar Hoode7b11842013-06-25 15:41:59 -0700412 self.assertEqual(results,
413 {'passing_test': datetime.datetime(2012, 1, 1),
414 'failing_test': datetime.datetime.min})
Keyar Hood788e4982013-06-18 14:34:28 -0700415
416
417 def test_returns_failing_tests_with_min_datetime(self):
418 """Test that never-passed tests are paired with datetime.min."""
419 self.mox.StubOutWithMock(complete_failures, 'get_last_pass_times')
Keyar Hoode7b11842013-06-25 15:41:59 -0700420 self.mox.StubOutWithMock(complete_failures,
421 'get_recently_ran_test_names')
Keyar Hood788e4982013-06-18 14:34:28 -0700422
423 complete_failures.get_last_pass_times().AndReturn({})
Keyar Hoode7b11842013-06-25 15:41:59 -0700424 complete_failures.get_recently_ran_test_names().AndReturn({'test'})
Keyar Hood788e4982013-06-18 14:34:28 -0700425
426 self.mox.ReplayAll()
427 results = complete_failures.get_tests_to_analyze()
428
429 self.assertEqual(results, {'test': datetime.datetime.min})
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700430
431
432if __name__ == '__main__':
433 unittest.main()