blob: 0d90f9cdf40022f9f85b2a0895a460775e70ad26 [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 Hood3d807682013-07-01 15:13:05 -070031class StoreResultsTests(mox.MoxTestBase):
32 """Test that entries are properly stored in the storage object."""
33
34 def setUp(self):
35 super(StoreResultsTests, self).setUp()
36 self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
37
38
39 def tearDown(self):
40 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long
41 super(StoreResultsTests, self).tearDown()
42
43
44 def test_add_failing_test(self):
45 """Test adding a failing test to storage."""
46 # We will want to keep all the datetime logic intact and so we need to
47 # keep a reference to the unmocked datetime.
48 self.datetime = datetime.datetime
49 self.mox.StubOutWithMock(datetime, 'datetime')
50 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 1))
51 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
52
53 storage = {}
54
55 self.mox.ReplayAll()
56 complete_failures.store_results(
57 {'test': datetime.datetime.min}, storage)
58
59 self.assertEqual(storage['test'], self.datetime(2012, 1, 1))
60
61
62 def test_remove_test_if_it_has_succeeded_recently_enough(self):
63 """Test that we remove a passing test from the storage object."""
64 storage = {'test': datetime.datetime(2012, 1, 1)}
65 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
66 today = datetime.datetime(2012, 4, 10)
67 safe_date = datetime.datetime(2012, 4, 9)
68
69 self.mox.StubOutWithMock(datetime, 'datetime')
70 datetime.datetime.today().AndReturn(today)
71
72 self.mox.ReplayAll()
73 complete_failures.store_results({'test': safe_date}, storage)
74
75 self.assertTrue('test' not in storage)
76
77
78 def test_no_crashing_on_test_that_has_never_failed_for_too_long(self):
79 """Test that we do not crash for tests that have always passed."""
80 storage = {}
81 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
82 today = datetime.datetime(2012, 4, 10)
83 safe_date = datetime.datetime(2012, 4, 9)
84
85 self.mox.StubOutWithMock(datetime, 'datetime')
86 datetime.datetime.today().AndReturn(today)
87
88 self.mox.ReplayAll()
89 complete_failures.store_results({'test': safe_date}, storage)
90
91 self.assertTrue('test' not in storage)
92
93
94 def test_do_not_delete_if_still_failing(self):
95 """Test that an old failing test is not removed from storage."""
96 # We will want to keep all the datetime logic intact and so we need to
97 # keep a reference to the unmocked datetime.
98 self.datetime = datetime.datetime
99 today = datetime.datetime(2012, 1, 1)
100 self.mox.StubOutWithMock(datetime, 'datetime')
101 datetime.datetime.today().AndReturn(today)
102
103 storage = {'test': datetime.datetime.min}
104
105 # The ReplayAll is required or else a mox object sneaks its way into
106 # the storage object somehow.
107 self.mox.ReplayAll()
108 complete_failures.store_results(
109 {'test': datetime.datetime.min}, storage)
110
111 self.assertTrue('test' in storage)
112
113
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700114class EmailAboutTestFailureTests(mox.MoxTestBase):
115 """
Keyar Hood3d807682013-07-01 15:13:05 -0700116 Tests that emails are sent about failed tests.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700117
Keyar Hood3d807682013-07-01 15:13:05 -0700118 This currently means an email is sent about all the entries of the
119 storage object.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700120
121 """
122 def setUp(self):
123 super(EmailAboutTestFailureTests, self).setUp()
124
125 # We need to mock out the send function in all tests or else the
126 # emails will be sent out during tests.
127 self.mox.StubOutWithMock(mail, 'send')
128
Keyar Hoode7b11842013-06-25 15:41:59 -0700129 self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700130
131
132 def tearDown(self):
Keyar Hoode7b11842013-06-25 15:41:59 -0700133 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long
Keyar Hood788e4982013-06-18 14:34:28 -0700134 super(EmailAboutTestFailureTests, self).tearDown()
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700135
136
Keyar Hood3d807682013-07-01 15:13:05 -0700137 def test_email_sent_about_all_entries_in_storage(self):
138 """Test that the email report mentions all the entries in storage."""
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700139 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
140
141 mail.send(
142 'chromeos-test-health@google.com',
143 ['chromeos-lab-infrastructure@google.com'],
144 [],
145 'Long Failing Tests',
146 'The following tests have been failing for at '
147 'least %i days:\n\ntest'
148 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
149
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700150 storage = {'test': datetime.datetime.min}
151
152 # The ReplayAll is required or else a mox object sneaks its way into
153 # the storage object somehow.
154 self.mox.ReplayAll()
Keyar Hood3d807682013-07-01 15:13:05 -0700155 complete_failures.email_about_test_failure(storage)
Keyar Hood788e4982013-06-18 14:34:28 -0700156
157
Keyar Hood005539d2013-06-24 14:27:57 -0700158class IsValidTestNameTests(test.TestCase):
159 """Tests the is_valid_test_name function."""
160
161 def test_returns_true_for_valid_test_name(self):
162 """Test that a valid test name returns True."""
163 name = 'TestName.TestName'
164 self.assertTrue(complete_failures.is_valid_test_name(name))
165
166
167 def test_returns_false_if_name_has_slash_in_it(self):
168 """Test that a name with a slash in it returns False."""
169 name = 'path/to/test'
170 self.assertFalse(complete_failures.is_valid_test_name(name))
171
172
173 def test_returns_false_for_try_new_image_entries(self):
174 """Test that a name that starts with try_new_image returns False."""
175 name = 'try_new_image-blah'
176 self.assertFalse(complete_failures.is_valid_test_name(name))
177
178
Keyar Hood788e4982013-06-18 14:34:28 -0700179class GetLastPassTimesTests(mox.MoxTestBase, test.TestCase):
180 """Tests the get_last_pass_times function."""
181
182 def setUp(self):
183 super(GetLastPassTimesTests, self).setUp()
184 setup_test_environment.set_up()
185
186
187 def tearDown(self):
188 setup_test_environment.tear_down()
189 super(GetLastPassTimesTests, self).tearDown()
190
191
192 def test_return_most_recent_pass(self):
193 """The last time a test passed should be returned."""
194 # To add a test entry to the database, Django the test object to
195 # be instantiated with various other model instances. We give these
196 # instances dummy id values.
Keyar Hood005539d2013-06-24 14:27:57 -0700197 job = models.Job(job_idx=1)
198 kernel = models.Kernel(kernel_idx=1)
199 machine = models.Machine(machine_idx=1)
200 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700201
Keyar Hood005539d2013-06-24 14:27:57 -0700202 early_pass = models.Test(job=job, status=success_status,
203 kernel=kernel, machine=machine,
204 test='test',
205 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700206 early_pass.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700207 late_pass = models.Test(job=job, status=success_status,
208 kernel=kernel, machine=machine,
209 test='test',
210 started_time=datetime.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700211 late_pass.save()
212
213 results = complete_failures.get_last_pass_times()
214
215 self.assertEquals(results, {'test': datetime.datetime(2012, 1, 2)})
216
217
218 def test_only_return_passing_tests(self):
219 """Tests that only tests that have passed at some point are returned."""
Keyar Hood005539d2013-06-24 14:27:57 -0700220 job = models.Job(job_idx=1)
221 kernel = models.Kernel(kernel_idx=1)
222 machine = models.Machine(machine_idx=1)
223 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
224 fail_status = models.Status(status_idx=FAIL_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700225
Keyar Hood005539d2013-06-24 14:27:57 -0700226 passing_test = models.Test(job=job, status=success_status,
227 kernel=kernel, machine=machine,
228 test='passing_test',
229 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700230 passing_test.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700231 failing_test = models.Test(job=job, status=fail_status,
232 kernel=kernel, machine=machine,
233 test='failing_test',
234 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700235 failing_test.save()
236
237 results = complete_failures.get_last_pass_times()
238
239 self.assertEquals(results,
240 {'passing_test': datetime.datetime(2012, 1, 1)})
241
242
243 def test_return_all_passing_tests(self):
244 """This function returns all tests that passed at least once."""
Keyar Hood005539d2013-06-24 14:27:57 -0700245 job = models.Job(job_idx=1)
246 kernel = models.Kernel(kernel_idx=1)
247 machine = models.Machine(machine_idx=1)
248 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700249
Keyar Hood005539d2013-06-24 14:27:57 -0700250 test1 = models.Test(job=job, status=success_status,
251 kernel=kernel, machine=machine,
252 test='test1',
253 started_time=datetime.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700254 test1.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700255 test2 = models.Test(job=job, status=success_status,
256 kernel=kernel, machine=machine,
257 test='test2',
258 started_time=datetime.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700259 test2.save()
260
261 results = complete_failures.get_last_pass_times()
262
263 self.assertEquals(results, {'test1': datetime.datetime(2012, 1, 1),
264 'test2': datetime.datetime(2012, 1, 2)})
265
266
Keyar Hood005539d2013-06-24 14:27:57 -0700267 def test_does_not_return_invalid_test_names(self):
268 """Tests that tests with invalid test names are not returned."""
269 job = models.Job(job_idx=1)
270 kernel = models.Kernel(kernel_idx=1)
271 machine = models.Machine(machine_idx=1)
272 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
273 fail_status = models.Status(status_idx=FAIL_STATUS_IDX)
274
275 invalid_test = models.Test(job=job, status=success_status,
276 kernel=kernel, machine=machine,
277 test='invalid_test/name',
278 started_time=datetime.datetime(2012, 1, 1))
279 invalid_test.save()
280
281 results = complete_failures.get_last_pass_times()
282
283 self.assertTrue(not results)
284
285
Keyar Hoode7b11842013-06-25 15:41:59 -0700286class GetRecentlyRanTestNamesTests(mox.MoxTestBase, test.TestCase):
287 """Tests the get_recently_ran_test_names function."""
Keyar Hood788e4982013-06-18 14:34:28 -0700288
289 def setUp(self):
Keyar Hoode7b11842013-06-25 15:41:59 -0700290 super(GetRecentlyRanTestNamesTests, self).setUp()
291 self.mox.StubOutWithMock(MockDatetime, 'today')
292 self.datetime = datetime.datetime
293 datetime.datetime = MockDatetime
Keyar Hood788e4982013-06-18 14:34:28 -0700294 setup_test_environment.set_up()
Keyar Hoode7b11842013-06-25 15:41:59 -0700295 self._orig_cutoff = complete_failures._DAYS_NOT_RUNNING_CUTOFF
Keyar Hood788e4982013-06-18 14:34:28 -0700296
297
298 def tearDown(self):
Keyar Hoode7b11842013-06-25 15:41:59 -0700299 datetime.datetime = self.datetime
300 complete_failures._DAYS_NOT_RUNNING_CUTOFF = self._orig_cutoff
Keyar Hood788e4982013-06-18 14:34:28 -0700301 setup_test_environment.tear_down()
Keyar Hoode7b11842013-06-25 15:41:59 -0700302 super(GetRecentlyRanTestNamesTests, self).tearDown()
Keyar Hood788e4982013-06-18 14:34:28 -0700303
304
Keyar Hoode7b11842013-06-25 15:41:59 -0700305 def test_return_all_recently_ran_tests(self):
Keyar Hood788e4982013-06-18 14:34:28 -0700306 """Test that the function does as it says it does."""
Keyar Hood005539d2013-06-24 14:27:57 -0700307 job = models.Job(job_idx=1)
308 kernel = models.Kernel(kernel_idx=1)
309 machine = models.Machine(machine_idx=1)
310 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700311
Keyar Hoode7b11842013-06-25 15:41:59 -0700312 recent = models.Test(job=job, status=success_status,
313 kernel=kernel, machine=machine,
314 test='recent',
315 started_time=self.datetime(2012, 1, 1))
316 recent.save()
317 old = models.Test(job=job, status=success_status,
318 kernel=kernel, machine=machine,
319 test='old',
320 started_time=self.datetime(2011, 1, 2))
321 old.save()
Keyar Hood788e4982013-06-18 14:34:28 -0700322
Keyar Hoode7b11842013-06-25 15:41:59 -0700323 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 4))
324 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60
Keyar Hood788e4982013-06-18 14:34:28 -0700325
Keyar Hoode7b11842013-06-25 15:41:59 -0700326 self.mox.ReplayAll()
327 results = complete_failures.get_recently_ran_test_names()
328
329 self.assertEqual(set(results), set(['recent']))
Keyar Hood788e4982013-06-18 14:34:28 -0700330
331
332 def test_returns_no_duplicate_names(self):
333 """Test that each test name appears only once."""
Keyar Hood005539d2013-06-24 14:27:57 -0700334 job = models.Job(job_idx=1)
335 kernel = models.Kernel(kernel_idx=1)
336 machine = models.Machine(machine_idx=1)
337 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700338
Keyar Hood005539d2013-06-24 14:27:57 -0700339 test = models.Test(job=job, status=success_status,
340 kernel=kernel, machine=machine,
341 test='test',
Keyar Hoode7b11842013-06-25 15:41:59 -0700342 started_time=self.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700343 test.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700344 duplicate = models.Test(job=job, status=success_status,
345 kernel=kernel, machine=machine,
346 test='test',
Keyar Hoode7b11842013-06-25 15:41:59 -0700347 started_time=self.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700348 duplicate.save()
349
Keyar Hoode7b11842013-06-25 15:41:59 -0700350 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 3))
351 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60
352
353 self.mox.ReplayAll()
354 results = complete_failures.get_recently_ran_test_names()
Keyar Hood788e4982013-06-18 14:34:28 -0700355
356 self.assertEqual(len(results), 1)
357
358
Keyar Hood005539d2013-06-24 14:27:57 -0700359 def test_does_not_return_invalid_test_names(self):
360 """Tests that only tests with invalid test names are not returned."""
361 job = models.Job(job_idx=1)
362 kernel = models.Kernel(kernel_idx=1)
363 machine = models.Machine(machine_idx=1)
364 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
365
366 invalid_test = models.Test(job=job, status=success_status,
367 kernel=kernel, machine=machine,
368 test='invalid_test/name',
Keyar Hoode7b11842013-06-25 15:41:59 -0700369 started_time=self.datetime(2012, 1, 1))
Keyar Hood005539d2013-06-24 14:27:57 -0700370 invalid_test.save()
371
Keyar Hoode7b11842013-06-25 15:41:59 -0700372 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 2))
373 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60
374
375 self.mox.ReplayAll()
376 results = complete_failures.get_recently_ran_test_names()
Keyar Hood005539d2013-06-24 14:27:57 -0700377
378 self.assertTrue(not results)
379
380
Keyar Hood788e4982013-06-18 14:34:28 -0700381class GetTestsToAnalyzeTests(mox.MoxTestBase):
382 """Tests the get_tests_to_analyze function."""
383
Keyar Hoode7b11842013-06-25 15:41:59 -0700384 def test_returns_recent_test_names(self):
Keyar Hood788e4982013-06-18 14:34:28 -0700385 """Test should return all the test names in the database."""
386 self.mox.StubOutWithMock(complete_failures, 'get_last_pass_times')
Keyar Hoode7b11842013-06-25 15:41:59 -0700387 self.mox.StubOutWithMock(complete_failures,
388 'get_recently_ran_test_names')
Keyar Hood788e4982013-06-18 14:34:28 -0700389
390 complete_failures.get_last_pass_times().AndReturn({'passing_test':
Keyar Hoode7b11842013-06-25 15:41:59 -0700391 datetime.datetime(2012, 1 ,1),
392 'old_passing_test': datetime.datetime(2011, 1, 1)})
393 complete_failures.get_recently_ran_test_names().AndReturn(
394 {'passing_test',
395 'failing_test'})
Keyar Hood788e4982013-06-18 14:34:28 -0700396 self.mox.ReplayAll()
397 results = complete_failures.get_tests_to_analyze()
398
Keyar Hoode7b11842013-06-25 15:41:59 -0700399 self.assertEqual(results,
400 {'passing_test': datetime.datetime(2012, 1, 1),
401 'failing_test': datetime.datetime.min})
Keyar Hood788e4982013-06-18 14:34:28 -0700402
403
404 def test_returns_failing_tests_with_min_datetime(self):
405 """Test that never-passed tests are paired with datetime.min."""
406 self.mox.StubOutWithMock(complete_failures, 'get_last_pass_times')
Keyar Hoode7b11842013-06-25 15:41:59 -0700407 self.mox.StubOutWithMock(complete_failures,
408 'get_recently_ran_test_names')
Keyar Hood788e4982013-06-18 14:34:28 -0700409
410 complete_failures.get_last_pass_times().AndReturn({})
Keyar Hoode7b11842013-06-25 15:41:59 -0700411 complete_failures.get_recently_ran_test_names().AndReturn({'test'})
Keyar Hood788e4982013-06-18 14:34:28 -0700412
413 self.mox.ReplayAll()
414 results = complete_failures.get_tests_to_analyze()
415
416 self.assertEqual(results, {'test': datetime.datetime.min})
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700417
418
419if __name__ == '__main__':
420 unittest.main()