blob: a5e0035bc4f89cc1e9114ccd7eddb0c9716b7b75 [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
Keyar Hooda3564712013-07-29 17:15:50 -07007import collections, datetime, unittest
Keyar Hood1a3c8dd2013-05-29 17:41:50 -07008
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
Keyar Hood788e4982013-06-18 14:34:28 -070023
Keyar Hoode7b11842013-06-25 15:41:59 -070024# 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
Keyar Hood3d807682013-07-01 15:13:05 -070030class StoreResultsTests(mox.MoxTestBase):
31 """Test that entries are properly stored in the storage object."""
32
33 def setUp(self):
34 super(StoreResultsTests, self).setUp()
35 self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
36
37
38 def tearDown(self):
39 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long
40 super(StoreResultsTests, self).tearDown()
41
42
43 def test_add_failing_test(self):
44 """Test adding a failing test to storage."""
45 # We will want to keep all the datetime logic intact and so we need to
46 # keep a reference to the unmocked datetime.
47 self.datetime = datetime.datetime
48 self.mox.StubOutWithMock(datetime, 'datetime')
49 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 1))
50 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
51
52 storage = {}
53
54 self.mox.ReplayAll()
55 complete_failures.store_results(
56 {'test': datetime.datetime.min}, storage)
57
58 self.assertEqual(storage['test'], self.datetime(2012, 1, 1))
59
60
61 def test_remove_test_if_it_has_succeeded_recently_enough(self):
62 """Test that we remove a passing test from the storage object."""
63 storage = {'test': datetime.datetime(2012, 1, 1)}
64 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
65 today = datetime.datetime(2012, 4, 10)
66 safe_date = datetime.datetime(2012, 4, 9)
67
68 self.mox.StubOutWithMock(datetime, 'datetime')
69 datetime.datetime.today().AndReturn(today)
70
71 self.mox.ReplayAll()
72 complete_failures.store_results({'test': safe_date}, storage)
73
74 self.assertTrue('test' not in storage)
75
76
77 def test_no_crashing_on_test_that_has_never_failed_for_too_long(self):
78 """Test that we do not crash for tests that have always passed."""
79 storage = {}
80 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
81 today = datetime.datetime(2012, 4, 10)
82 safe_date = datetime.datetime(2012, 4, 9)
83
84 self.mox.StubOutWithMock(datetime, 'datetime')
85 datetime.datetime.today().AndReturn(today)
86
87 self.mox.ReplayAll()
88 complete_failures.store_results({'test': safe_date}, storage)
89
90 self.assertTrue('test' not in storage)
91
92
93 def test_do_not_delete_if_still_failing(self):
94 """Test that an old failing test is not removed from storage."""
95 # We will want to keep all the datetime logic intact and so we need to
96 # keep a reference to the unmocked datetime.
97 self.datetime = datetime.datetime
98 today = datetime.datetime(2012, 1, 1)
99 self.mox.StubOutWithMock(datetime, 'datetime')
100 datetime.datetime.today().AndReturn(today)
101
102 storage = {'test': datetime.datetime.min}
103
104 # The ReplayAll is required or else a mox object sneaks its way into
105 # the storage object somehow.
106 self.mox.ReplayAll()
107 complete_failures.store_results(
108 {'test': datetime.datetime.min}, storage)
109
110 self.assertTrue('test' in storage)
111
112
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700113class EmailAboutTestFailureTests(mox.MoxTestBase):
114 """
Keyar Hood3d807682013-07-01 15:13:05 -0700115 Tests that emails are sent about failed tests.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700116
Keyar Hood3d807682013-07-01 15:13:05 -0700117 This currently means an email is sent about all the entries of the
118 storage object.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700119
120 """
121 def setUp(self):
122 super(EmailAboutTestFailureTests, self).setUp()
123
124 # We need to mock out the send function in all tests or else the
125 # emails will be sent out during tests.
126 self.mox.StubOutWithMock(mail, 'send')
127
Keyar Hoode7b11842013-06-25 15:41:59 -0700128 self._orig_too_long = complete_failures._DAYS_TO_BE_FAILING_TOO_LONG
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700129
130
131 def tearDown(self):
Keyar Hoode7b11842013-06-25 15:41:59 -0700132 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = self._orig_too_long
Keyar Hood788e4982013-06-18 14:34:28 -0700133 super(EmailAboutTestFailureTests, self).tearDown()
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700134
135
Keyar Hood3d807682013-07-01 15:13:05 -0700136 def test_email_sent_about_all_entries_in_storage(self):
137 """Test that the email report mentions all the entries in storage."""
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700138 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
139
140 mail.send(
141 'chromeos-test-health@google.com',
142 ['chromeos-lab-infrastructure@google.com'],
143 [],
144 'Long Failing Tests',
Keyar Hooda3564712013-07-29 17:15:50 -0700145 '1/1 tests have been failing for at least %d days.\n'
146 'They are the following:\n\ntest'
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700147 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
148
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700149 storage = {'test': datetime.datetime.min}
Keyar Hooda3564712013-07-29 17:15:50 -0700150 all_tests = set(storage.keys())
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700151
152 # The ReplayAll is required or else a mox object sneaks its way into
153 # the storage object somehow.
154 self.mox.ReplayAll()
Keyar Hooda3564712013-07-29 17:15:50 -0700155 complete_failures.email_about_test_failure(storage, all_tests)
156
157
158 def test_email_has_test_names_sorted_alphabetically(self):
159 """Test that the email report has entries sorted alphabetically."""
160 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
161
162 mail.send(
163 'chromeos-test-health@google.com',
164 ['chromeos-lab-infrastructure@google.com'],
165 [],
166 'Long Failing Tests',
167 '2/2 tests have been failing for at least %d days.\n'
168 'They are the following:\n\ntest1\ntest2'
169 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
170
171 # We use an OrderedDict to gurantee that the elements would be out of
172 # order if we did a simple traversal.
173 storage = collections.OrderedDict((('test2', datetime.datetime.min),
174 ('test1', datetime.datetime.min)))
175 all_tests = set(storage.keys())
176
177 # The ReplayAll is required or else a mox object sneaks its way into
178 # the storage object somehow.
179 self.mox.ReplayAll()
180 complete_failures.email_about_test_failure(storage, all_tests)
181
182
183 def test_email_count_of_total_number_of_tests(self):
184 """Test that the email report displays total number of tests."""
185 complete_failures._DAYS_TO_BE_FAILING_TOO_LONG = 60
186
187 mail.send(
188 'chromeos-test-health@google.com',
189 ['chromeos-lab-infrastructure@google.com'],
190 [],
191 'Long Failing Tests',
192 '1/2 tests have been failing for at least %d days.\n'
193 'They are the following:\n\ntest'
194 % complete_failures._DAYS_TO_BE_FAILING_TOO_LONG)
195
196 storage = {'test': datetime.datetime.min}
197 all_tests = set(storage.keys()) | {'not_failure'}
198
199 # The ReplayAll is required or else a mox object sneaks its way into
200 # the storage object somehow.
201 self.mox.ReplayAll()
202 complete_failures.email_about_test_failure(storage, all_tests)
Keyar Hood788e4982013-06-18 14:34:28 -0700203
204
Keyar Hood005539d2013-06-24 14:27:57 -0700205class IsValidTestNameTests(test.TestCase):
206 """Tests the is_valid_test_name function."""
207
208 def test_returns_true_for_valid_test_name(self):
209 """Test that a valid test name returns True."""
210 name = 'TestName.TestName'
211 self.assertTrue(complete_failures.is_valid_test_name(name))
212
213
214 def test_returns_false_if_name_has_slash_in_it(self):
215 """Test that a name with a slash in it returns False."""
216 name = 'path/to/test'
217 self.assertFalse(complete_failures.is_valid_test_name(name))
218
219
220 def test_returns_false_for_try_new_image_entries(self):
221 """Test that a name that starts with try_new_image returns False."""
222 name = 'try_new_image-blah'
223 self.assertFalse(complete_failures.is_valid_test_name(name))
224
225
Keyar Hood0f26dba2013-07-18 17:49:32 -0700226class PrepareLastPassesTests(test.TestCase):
227 """Tests the prepare_last_passes function."""
Keyar Hood788e4982013-06-18 14:34:28 -0700228
229 def setUp(self):
Keyar Hood0f26dba2013-07-18 17:49:32 -0700230 super(PrepareLastPassesTests, self).setUp()
Keyar Hood788e4982013-06-18 14:34:28 -0700231
232 def tearDown(self):
Keyar Hood0f26dba2013-07-18 17:49:32 -0700233 super(PrepareLastPassesTests, self).tearDown()
Keyar Hood788e4982013-06-18 14:34:28 -0700234
Keyar Hood005539d2013-06-24 14:27:57 -0700235 def test_does_not_return_invalid_test_names(self):
236 """Tests that tests with invalid test names are not returned."""
Keyar Hood0f26dba2013-07-18 17:49:32 -0700237 results = complete_failures.prepare_last_passes(['invalid_test/name'])
Keyar Hood005539d2013-06-24 14:27:57 -0700238
Keyar Hood0f26dba2013-07-18 17:49:32 -0700239 self.assertEqual(results, {})
Keyar Hood005539d2013-06-24 14:27:57 -0700240
Keyar Hood005539d2013-06-24 14:27:57 -0700241
242
Keyar Hoode7b11842013-06-25 15:41:59 -0700243class GetRecentlyRanTestNamesTests(mox.MoxTestBase, test.TestCase):
244 """Tests the get_recently_ran_test_names function."""
Keyar Hood788e4982013-06-18 14:34:28 -0700245
246 def setUp(self):
Keyar Hoode7b11842013-06-25 15:41:59 -0700247 super(GetRecentlyRanTestNamesTests, self).setUp()
248 self.mox.StubOutWithMock(MockDatetime, 'today')
249 self.datetime = datetime.datetime
250 datetime.datetime = MockDatetime
Keyar Hood788e4982013-06-18 14:34:28 -0700251 setup_test_environment.set_up()
Keyar Hoode7b11842013-06-25 15:41:59 -0700252 self._orig_cutoff = complete_failures._DAYS_NOT_RUNNING_CUTOFF
Keyar Hood788e4982013-06-18 14:34:28 -0700253
254
255 def tearDown(self):
Keyar Hoode7b11842013-06-25 15:41:59 -0700256 datetime.datetime = self.datetime
257 complete_failures._DAYS_NOT_RUNNING_CUTOFF = self._orig_cutoff
Keyar Hood788e4982013-06-18 14:34:28 -0700258 setup_test_environment.tear_down()
Keyar Hoode7b11842013-06-25 15:41:59 -0700259 super(GetRecentlyRanTestNamesTests, self).tearDown()
Keyar Hood788e4982013-06-18 14:34:28 -0700260
261
Keyar Hoode7b11842013-06-25 15:41:59 -0700262 def test_return_all_recently_ran_tests(self):
Keyar Hood788e4982013-06-18 14:34:28 -0700263 """Test that the function does as it says it does."""
Keyar Hood005539d2013-06-24 14:27:57 -0700264 job = models.Job(job_idx=1)
265 kernel = models.Kernel(kernel_idx=1)
266 machine = models.Machine(machine_idx=1)
267 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700268
Keyar Hoode7b11842013-06-25 15:41:59 -0700269 recent = models.Test(job=job, status=success_status,
270 kernel=kernel, machine=machine,
271 test='recent',
272 started_time=self.datetime(2012, 1, 1))
273 recent.save()
274 old = models.Test(job=job, status=success_status,
275 kernel=kernel, machine=machine,
276 test='old',
277 started_time=self.datetime(2011, 1, 2))
278 old.save()
Keyar Hood788e4982013-06-18 14:34:28 -0700279
Keyar Hoode7b11842013-06-25 15:41:59 -0700280 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 4))
281 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60
Keyar Hood788e4982013-06-18 14:34:28 -0700282
Keyar Hoode7b11842013-06-25 15:41:59 -0700283 self.mox.ReplayAll()
284 results = complete_failures.get_recently_ran_test_names()
285
286 self.assertEqual(set(results), set(['recent']))
Keyar Hood788e4982013-06-18 14:34:28 -0700287
288
289 def test_returns_no_duplicate_names(self):
290 """Test that each test name appears only once."""
Keyar Hood005539d2013-06-24 14:27:57 -0700291 job = models.Job(job_idx=1)
292 kernel = models.Kernel(kernel_idx=1)
293 machine = models.Machine(machine_idx=1)
294 success_status = models.Status(status_idx=GOOD_STATUS_IDX)
Keyar Hood788e4982013-06-18 14:34:28 -0700295
Keyar Hood005539d2013-06-24 14:27:57 -0700296 test = models.Test(job=job, status=success_status,
297 kernel=kernel, machine=machine,
298 test='test',
Keyar Hoode7b11842013-06-25 15:41:59 -0700299 started_time=self.datetime(2012, 1, 1))
Keyar Hood788e4982013-06-18 14:34:28 -0700300 test.save()
Keyar Hood005539d2013-06-24 14:27:57 -0700301 duplicate = models.Test(job=job, status=success_status,
302 kernel=kernel, machine=machine,
303 test='test',
Keyar Hoode7b11842013-06-25 15:41:59 -0700304 started_time=self.datetime(2012, 1, 2))
Keyar Hood788e4982013-06-18 14:34:28 -0700305 duplicate.save()
306
Keyar Hoode7b11842013-06-25 15:41:59 -0700307 datetime.datetime.today().AndReturn(self.datetime(2012, 1, 3))
308 complete_failures._DAYS_NOT_RUNNING_CUTOFF = 60
309
310 self.mox.ReplayAll()
311 results = complete_failures.get_recently_ran_test_names()
Keyar Hood788e4982013-06-18 14:34:28 -0700312
313 self.assertEqual(len(results), 1)
314
315
316class GetTestsToAnalyzeTests(mox.MoxTestBase):
317 """Tests the get_tests_to_analyze function."""
318
Keyar Hoode7b11842013-06-25 15:41:59 -0700319 def test_returns_recent_test_names(self):
Keyar Hood788e4982013-06-18 14:34:28 -0700320 """Test should return all the test names in the database."""
Keyar Hood788e4982013-06-18 14:34:28 -0700321
Keyar Hooda3564712013-07-29 17:15:50 -0700322 recent_tests = {'passing_test', 'failing_test'}
323 last_passes = {'passing_test': datetime.datetime(2012, 1 ,1),
324 'old_passing_test': datetime.datetime(2011, 1, 1)}
325
326 results = complete_failures.get_tests_to_analyze(recent_tests,
327 last_passes)
Keyar Hood788e4982013-06-18 14:34:28 -0700328
Keyar Hoode7b11842013-06-25 15:41:59 -0700329 self.assertEqual(results,
330 {'passing_test': datetime.datetime(2012, 1, 1),
331 'failing_test': datetime.datetime.min})
Keyar Hood788e4982013-06-18 14:34:28 -0700332
333
334 def test_returns_failing_tests_with_min_datetime(self):
335 """Test that never-passed tests are paired with datetime.min."""
Keyar Hood788e4982013-06-18 14:34:28 -0700336
Keyar Hooda3564712013-07-29 17:15:50 -0700337 recent_tests = {'test'}
338 last_passes = {}
Keyar Hood788e4982013-06-18 14:34:28 -0700339
340 self.mox.ReplayAll()
Keyar Hooda3564712013-07-29 17:15:50 -0700341 results = complete_failures.get_tests_to_analyze(recent_tests,
342 last_passes)
Keyar Hood788e4982013-06-18 14:34:28 -0700343
344 self.assertEqual(results, {'test': datetime.datetime.min})
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700345
346
347if __name__ == '__main__':
348 unittest.main()