blob: 044f06ef2a347f1a80cb947765afa92c20c24bf4 [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
147class GetLastPassTimesTests(mox.MoxTestBase, test.TestCase):
148 """Tests the get_last_pass_times function."""
149
150 def setUp(self):
151 super(GetLastPassTimesTests, self).setUp()
152 setup_test_environment.set_up()
153
154
155 def tearDown(self):
156 setup_test_environment.tear_down()
157 super(GetLastPassTimesTests, self).tearDown()
158
159
160 def test_return_most_recent_pass(self):
161 """The last time a test passed should be returned."""
162 # To add a test entry to the database, Django the test object to
163 # be instantiated with various other model instances. We give these
164 # instances dummy id values.
165 job = models.Job(job_idx = 1)
166 kernel = models.Kernel(kernel_idx = 1)
167 machine = models.Machine(machine_idx = 1)
168 success_status = models.Status(status_idx = GOOD_STATUS_IDX)
169
170 early_pass = models.Test(job = job, status = success_status,
171 kernel = kernel, machine = machine,
172 test = 'test',
173 started_time = datetime.datetime(2012, 1, 1))
174 early_pass.save()
175 late_pass = models.Test(job = job, status = success_status,
176 kernel = kernel, machine = machine,
177 test = 'test',
178 started_time = datetime.datetime(2012, 1, 2))
179 late_pass.save()
180
181 results = complete_failures.get_last_pass_times()
182
183 self.assertEquals(results, {'test': datetime.datetime(2012, 1, 2)})
184
185
186 def test_only_return_passing_tests(self):
187 """Tests that only tests that have passed at some point are returned."""
188 job = models.Job(job_idx = 1)
189 kernel = models.Kernel(kernel_idx = 1)
190 machine = models.Machine(machine_idx = 1)
191 success_status = models.Status(status_idx = GOOD_STATUS_IDX)
192 fail_status = models.Status(status_idx = FAIL_STATUS_IDX)
193
194 passing_test = models.Test(job = job, status = success_status,
195 kernel = kernel, machine = machine,
196 test = 'passing_test',
197 started_time = datetime.datetime(2012, 1, 1))
198 passing_test.save()
199 failing_test = models.Test(job = job, status = fail_status,
200 kernel = kernel, machine = machine,
201 test = 'failing_test',
202 started_time = datetime.datetime(2012, 1, 1))
203 failing_test.save()
204
205 results = complete_failures.get_last_pass_times()
206
207 self.assertEquals(results,
208 {'passing_test': datetime.datetime(2012, 1, 1)})
209
210
211 def test_return_all_passing_tests(self):
212 """This function returns all tests that passed at least once."""
213 job = models.Job(job_idx = 1)
214 kernel = models.Kernel(kernel_idx = 1)
215 machine = models.Machine(machine_idx = 1)
216 success_status = models.Status(status_idx = GOOD_STATUS_IDX)
217
218 test1 = models.Test(job = job, status = success_status,
219 kernel = kernel, machine = machine,
220 test = 'test1',
221 started_time = datetime.datetime(2012, 1, 1))
222 test1.save()
223 test2 = models.Test(job = job, status = success_status,
224 kernel = kernel, machine = machine,
225 test = 'test2',
226 started_time = datetime.datetime(2012, 1, 2))
227 test2.save()
228
229 results = complete_failures.get_last_pass_times()
230
231 self.assertEquals(results, {'test1': datetime.datetime(2012, 1, 1),
232 'test2': datetime.datetime(2012, 1, 2)})
233
234
235class GetAllTestNamesTests(mox.MoxTestBase, test.TestCase):
236 """Tests the get_all_test_names function."""
237
238 def setUp(self):
239 super(GetAllTestNamesTests, self).setUp()
240 setup_test_environment.set_up()
241
242
243 def tearDown(self):
244 setup_test_environment.tear_down()
245 super(GetAllTestNamesTests, self).tearDown()
246
247
248 def test_return_all_tests(self):
249 """Test that the function does as it says it does."""
250 job = models.Job(job_idx = 1)
251 kernel = models.Kernel(kernel_idx = 1)
252 machine = models.Machine(machine_idx = 1)
253 success_status = models.Status(status_idx = GOOD_STATUS_IDX)
254
255 test1 = models.Test(job = job, status = success_status,
256 kernel = kernel, machine = machine,
257 test = 'test1',
258 started_time = datetime.datetime(2012, 1, 1))
259 test1.save()
260 test2 = models.Test(job = job, status = success_status,
261 kernel = kernel, machine = machine,
262 test = 'test2',
263 started_time = datetime.datetime(2012, 1, 2))
264 test2.save()
265
266 results = complete_failures.get_all_test_names()
267
268 self.assertEqual(set(results), set(['test1', 'test2']))
269
270
271 def test_returns_no_duplicate_names(self):
272 """Test that each test name appears only once."""
273 job = models.Job(job_idx = 1)
274 kernel = models.Kernel(kernel_idx = 1)
275 machine = models.Machine(machine_idx = 1)
276 success_status = models.Status(status_idx = GOOD_STATUS_IDX)
277
278 test = models.Test(job = job, status = success_status,
279 kernel = kernel, machine = machine,
280 test = 'test',
281 started_time = datetime.datetime(2012, 1, 1))
282 test.save()
283 duplicate = models.Test(job = job, status = success_status,
284 kernel = kernel, machine = machine,
285 test = 'test',
286 started_time = datetime.datetime(2012, 1, 2))
287 duplicate.save()
288
289 results = complete_failures.get_all_test_names()
290
291 self.assertEqual(len(results), 1)
292
293
294class GetTestsToAnalyzeTests(mox.MoxTestBase):
295 """Tests the get_tests_to_analyze function."""
296
297 def test_returns_all_test_names(self):
298 """Test should return all the test names in the database."""
299 self.mox.StubOutWithMock(complete_failures, 'get_last_pass_times')
300 self.mox.StubOutWithMock(complete_failures, 'get_all_test_names')
301
302 complete_failures.get_last_pass_times().AndReturn({'passing_test':
303 datetime.datetime(2012, 1 ,1)})
304 complete_failures.get_all_test_names().AndReturn(['passing_test',
305 'failing_test'])
306 self.mox.ReplayAll()
307 results = complete_failures.get_tests_to_analyze()
308
309 self.assertEqual(set(results.keys()),
310 set(['passing_test', 'failing_test']))
311
312
313 def test_returns_failing_tests_with_min_datetime(self):
314 """Test that never-passed tests are paired with datetime.min."""
315 self.mox.StubOutWithMock(complete_failures, 'get_last_pass_times')
316 self.mox.StubOutWithMock(complete_failures, 'get_all_test_names')
317
318 complete_failures.get_last_pass_times().AndReturn({})
319 complete_failures.get_all_test_names().AndReturn(['test'])
320
321 self.mox.ReplayAll()
322 results = complete_failures.get_tests_to_analyze()
323
324 self.assertEqual(results, {'test': datetime.datetime.min})
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700325
326
327if __name__ == '__main__':
328 unittest.main()