blob: a08344c3911feb1a9b2e4663399ec72be6e19188 [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 Hood1a3c8dd2013-05-29 17:41:50 -070025class EmailAboutTestFailureTests(mox.MoxTestBase):
26 """
27 Test the core logic of the comlete_failures.py script.
28
29 The core logic is to send emails only if we have not yet done so for a
30 given test before and to take actions if the test has been failing for
31 long enough.
32
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._orignal_too_late = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
42
43
44 def tearDown(self):
45 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orignal_too_late
Keyar Hood788e4982013-06-18 14:34:28 -070046 super(EmailAboutTestFailureTests, self).tearDown()
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070047
48
49 def test_deal_with_new_failing_test(self):
50 """
51 Test adding a failing test to the storage.
52
53 We expect the email sending code to be called if it is added.
54
55 """
56 # We will want to keep all the datetime logic intact and so we need to
57 # keep a reference to the unmocked datetime.
58 self.datetime = datetime.datetime
59 self.mox.StubOutWithMock(datetime, 'datetime')
60 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 1))
61 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
62
63 mail.send(
64 'chromeos-test-health@google.com',
65 ['chromeos-lab-infrastructure@google.com'],
66 [],
67 'Long Failing Tests',
68 'The following tests have been failing for at '
69 'least %i days:\n\ntest'
70 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
71
72 storage = {}
73
74 # The ReplayAll is required or else a mox object sneaks its way into
75 # the storage object somehow.
76 self.mox.ReplayAll()
77 complete_failures.email_about_test_failure(
78 {'test': datetime.datetime.min}, storage)
79
80 self.assertEqual(storage['test'], self.datetime(2012, 1, 1))
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070081
82
83 def test_remove_test_if_it_has_succeeded_recently_enough(self):
84 """Test that we remove a passing test from the storage object."""
85 storage = {'test': datetime.datetime(2012, 1, 1)}
86 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
87 today = datetime.datetime(2012, 4, 10)
88 safe_date = datetime.datetime(2012, 4, 9)
89
90 self.mox.StubOutWithMock(datetime, 'datetime')
91 datetime.datetime.today().AndReturn(today)
92
93 self.mox.ReplayAll()
94 complete_failures.email_about_test_failure({'test': safe_date}, storage)
95
96 self.assertTrue('test' not in storage)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070097
98
99 def test_no_crashing_on_test_that_has_never_failed_for_too_long(self):
100 """Test that we do not crash for tests that have always passed."""
101 storage = {}
102 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
103 today = datetime.datetime(2012,4,10)
104 safe_date = datetime.datetime(2012,4,9)
105
106 self.mox.StubOutWithMock(datetime, 'datetime')
107 datetime.datetime.today().AndReturn(today)
108
109 self.mox.ReplayAll()
110 complete_failures.email_about_test_failure({'test': safe_date}, storage)
111
112 self.assertTrue('test' not in storage)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700113
114
115 def test_do_not_send_email_if_test_already_in_storage(self):
116 """Test only send emails on newly problematic tests."""
117 storage = {'test': datetime.datetime(2012, 1, 1)}
118 self.datetime = datetime.datetime
119 self.mox.StubOutWithMock(datetime, 'datetime')
120 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 1))
121
122 self.mox.ReplayAll()
123 complete_failures.email_about_test_failure(
124 {'test': datetime.datetime.min}, storage)
125
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700126
127 def test_do_not_delete_if_still_failing(self):
128 """Test that an old failing test is not removed from storage."""
129 # We will want to keep all the datetime logic intact and so we need to
130 # keep a reference to the unmocked datetime.
131 self.datetime = datetime.datetime
132 today = datetime.datetime(2012, 1, 1)
133 self.mox.StubOutWithMock(datetime, 'datetime')
134 datetime.datetime.today().AndReturn(today)
135
136 storage = {'test': datetime.datetime.min}
137
138 # The ReplayAll is required or else a mox object sneaks its way into
139 # the storage object somehow.
140 self.mox.ReplayAll()
141 complete_failures.email_about_test_failure(
142 {'test': datetime.datetime.min}, storage)
143
144 self.assertTrue('test' in storage)
Keyar Hood788e4982013-06-18 14:34:28 -0700145
146
Keyar Hood005539d2013-06-24 14:27:57 -0700147class IsValidTestNameTests(test.TestCase):
148 """Tests the is_valid_test_name function."""
149
150 def test_returns_true_for_valid_test_name(self):
151 """Test that a valid test name returns True."""
152 name = 'TestName.TestName'
153 self.assertTrue(complete_failures.is_valid_test_name(name))
154
155
156 def test_returns_false_if_name_has_slash_in_it(self):
157 """Test that a name with a slash in it returns False."""
158 name = 'path/to/test'
159 self.assertFalse(complete_failures.is_valid_test_name(name))
160
161
162 def test_returns_false_for_try_new_image_entries(self):
163 """Test that a name that starts with try_new_image returns False."""
164 name = 'try_new_image-blah'
165 self.assertFalse(complete_failures.is_valid_test_name(name))
166
167
Keyar Hood788e4982013-06-18 14:34:28 -0700168class GetLastPassTimesTests(mox.MoxTestBase, test.TestCase):
169 """Tests the get_last_pass_times function."""
170
171 def setUp(self):
172 super(GetLastPassTimesTests, self).setUp()
173 setup_test_environment.set_up()
174
175
176 def tearDown(self):
177 setup_test_environment.tear_down()
178 super(GetLastPassTimesTests, self).tearDown()
179
180
181 def test_return_most_recent_pass(self):
182 """The last time a test passed should be returned."""
183 # To add a test entry to the database, Django the test object to
184 # be instantiated with various other model instances. We give these
185 # instances dummy id values.
Keyar Hood005539d2013-06-24 14:27:57 -0700186 job = models.Job(job_idx=1)
187 kernel = models.Kernel(kernel_idx=1)
188 machine = models.Machine(machine_idx=1)
189 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700190
Keyar Hood005539d2013-06-24 14:27:57 -0700191 early_pass = models.Test(job=job, status=success_status,
192 kernel=kernel, machine=machine,
193 test='test',
194 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700195 early_pass.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700196 late_pass = models.Test(job=job, status=success_status,
197 kernel=kernel, machine=machine,
198 test='test',
199 started_time=datetime.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700200 late_pass.save()
201
202 results = complete_failures.get_last_pass_times()
203
204 self.assertEquals(results, {'test': datetime.datetime(2012, 1, 2)})
205
206
207 def test_only_return_passing_tests(self):
208 """Tests that only tests that have passed at some point are returned."""
Keyar Hood005539d2013-06-24 14:27:57 -0700209 job = models.Job(job_idx=1)
210 kernel = models.Kernel(kernel_idx=1)
211 machine = models.Machine(machine_idx=1)
212 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
213 fail_status = models.Status(status_idx=FAIL_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700214
Keyar Hood005539d2013-06-24 14:27:57 -0700215 passing_test = models.Test(job=job, status=success_status,
216 kernel=kernel, machine=machine,
217 test='passing_test',
218 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700219 passing_test.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700220 failing_test = models.Test(job=job, status=fail_status,
221 kernel=kernel, machine=machine,
222 test='failing_test',
223 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700224 failing_test.save()
225
226 results = complete_failures.get_last_pass_times()
227
228 self.assertEquals(results,
229 {'passing_test': datetime.datetime(2012, 1, 1)})
230
231
232 def test_return_all_passing_tests(self):
233 """This function returns all tests that passed at least once."""
Keyar Hood005539d2013-06-24 14:27:57 -0700234 job = models.Job(job_idx=1)
235 kernel = models.Kernel(kernel_idx=1)
236 machine = models.Machine(machine_idx=1)
237 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700238
Keyar Hood005539d2013-06-24 14:27:57 -0700239 test1 = models.Test(job=job, status=success_status,
240 kernel=kernel, machine=machine,
241 test='test1',
242 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700243 test1.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700244 test2 = models.Test(job=job, status=success_status,
245 kernel=kernel, machine=machine,
246 test='test2',
247 started_time=datetime.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700248 test2.save()
249
250 results = complete_failures.get_last_pass_times()
251
252 self.assertEquals(results, {'test1': datetime.datetime(2012, 1, 1),
253 'test2': datetime.datetime(2012, 1, 2)})
254
255
Keyar Hood005539d2013-06-24 14:27:57 -0700256 def test_does_not_return_invalid_test_names(self):
257 """Tests that tests with invalid test names are not returned."""
258 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)
262 fail_status = models.Status(status_idx=FAIL_STATUS_IDX)
263
264 invalid_test = models.Test(job=job, status=success_status,
265 kernel=kernel, machine=machine,
266 test='invalid_test/name',
267 started_time=datetime.datetime(2012, 1, 1))
268 invalid_test.save()
269
270 results = complete_failures.get_last_pass_times()
271
272 self.assertTrue(not results)
273
274
Keyar Hood788e4982013-06-18 14:34:28 -0700275class GetAllTestNamesTests(mox.MoxTestBase, test.TestCase):
276 """Tests the get_all_test_names function."""
277
278 def setUp(self):
279 super(GetAllTestNamesTests, self).setUp()
280 setup_test_environment.set_up()
281
282
283 def tearDown(self):
284 setup_test_environment.tear_down()
285 super(GetAllTestNamesTests, self).tearDown()
286
287
288 def test_return_all_tests(self):
289 """Test that the function does as it says it does."""
Keyar Hood005539d2013-06-24 14:27:57 -0700290 job = models.Job(job_idx=1)
291 kernel = models.Kernel(kernel_idx=1)
292 machine = models.Machine(machine_idx=1)
293 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700294
Keyar Hood005539d2013-06-24 14:27:57 -0700295 test1 = models.Test(job=job, status=success_status,
296 kernel=kernel, machine=machine,
297 test='test1',
298 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700299 test1.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700300 test2 = models.Test(job=job, status=success_status,
301 kernel=kernel, machine=machine,
302 test='test2',
303 started_time=datetime.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700304 test2.save()
305
306 results = complete_failures.get_all_test_names()
307
308 self.assertEqual(set(results), set(['test1', 'test2']))
309
310
311 def test_returns_no_duplicate_names(self):
312 """Test that each test name appears only once."""
Keyar Hood005539d2013-06-24 14:27:57 -0700313 job = models.Job(job_idx=1)
314 kernel = models.Kernel(kernel_idx=1)
315 machine = models.Machine(machine_idx=1)
316 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700317
Keyar Hood005539d2013-06-24 14:27:57 -0700318 test = models.Test(job=job, status=success_status,
319 kernel=kernel, machine=machine,
320 test='test',
321 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700322 test.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700323 duplicate = models.Test(job=job, status=success_status,
324 kernel=kernel, machine=machine,
325 test='test',
326 started_time=datetime.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700327 duplicate.save()
328
329 results = complete_failures.get_all_test_names()
330
331 self.assertEqual(len(results), 1)
332
333
Keyar Hood005539d2013-06-24 14:27:57 -0700334 def test_does_not_return_invalid_test_names(self):
335 """Tests that only tests with invalid test names are not returned."""
336 job = models.Job(job_idx=1)
337 kernel = models.Kernel(kernel_idx=1)
338 machine = models.Machine(machine_idx=1)
339 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
340
341 invalid_test = models.Test(job=job, status=success_status,
342 kernel=kernel, machine=machine,
343 test='invalid_test/name',
344 started_time=datetime.datetime(2012, 1, 1))
345 invalid_test.save()
346
347 results = complete_failures.get_all_test_names()
348
349 self.assertTrue(not results)
350
351
Keyar Hood788e4982013-06-18 14:34:28 -0700352class GetTestsToAnalyzeTests(mox.MoxTestBase):
353 """Tests the get_tests_to_analyze function."""
354
355 def test_returns_all_test_names(self):
356 """Test should return all the test names in the database."""
357 self.mox.StubOutWithMock(complete_failures, 'get_last_pass_times')
358 self.mox.StubOutWithMock(complete_failures, 'get_all_test_names')
359
360 complete_failures.get_last_pass_times().AndReturn({'passing_test':
361 datetime.datetime(2012, 1 ,1)})
362 complete_failures.get_all_test_names().AndReturn(['passing_test',
363 'failing_test'])
364 self.mox.ReplayAll()
365 results = complete_failures.get_tests_to_analyze()
366
367 self.assertEqual(set(results.keys()),
368 set(['passing_test', 'failing_test']))
369
370
371 def test_returns_failing_tests_with_min_datetime(self):
372 """Test that never-passed tests are paired with datetime.min."""
373 self.mox.StubOutWithMock(complete_failures, 'get_last_pass_times')
374 self.mox.StubOutWithMock(complete_failures, 'get_all_test_names')
375
376 complete_failures.get_last_pass_times().AndReturn({})
377 complete_failures.get_all_test_names().AndReturn(['test'])
378
379 self.mox.ReplayAll()
380 results = complete_failures.get_tests_to_analyze()
381
382 self.assertEqual(results, {'test': datetime.datetime.min})
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700383
384
385if __name__ == '__main__':
386 unittest.main()