blob: da50ab6b40a4f30b43a616b922a47657ee59f176 [file] [log] [blame]
Fang Deng0454e632014-04-07 15:39:47 -07001#!/usr/bin/python
2# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Prashanth Ba7be2072014-07-15 15:03:21 -07006import datetime as datetime_base
7from datetime import datetime
Fang Deng0454e632014-04-07 15:39:47 -07008import mock
Prashanth Ba7be2072014-07-15 15:03:21 -07009import time
Fang Deng0454e632014-04-07 15:39:47 -070010import unittest
11
12import common
13
14from autotest_lib.server.cros.dynamic_suite import constants
15from autotest_lib.site_utils import run_suite
Prashanth Ba7be2072014-07-15 15:03:21 -070016from autotest_lib.site_utils import diagnosis_utils
Fang Deng0454e632014-04-07 15:39:47 -070017
18
19class ResultCollectorUnittest(unittest.TestCase):
20 """Runsuite unittest"""
21
Fang Dengf8503532014-06-12 18:21:55 -070022 JOB_MAX_RUNTIME_MINS = 10
23
Fang Deng0454e632014-04-07 15:39:47 -070024 def setUp(self):
25 """Set up test."""
26 self.afe = mock.MagicMock()
27 self.tko = mock.MagicMock()
28
29
30 def _build_view(self, test_idx, test_name, subdir, status, afe_job_id,
31 job_name='fake_job_name', reason='fake reason',
32 job_keyvals=None, test_started_time=None,
Fang Dengf8503532014-06-12 18:21:55 -070033 test_finished_time=None, invalidates_test_idx=None,
34 job_started_time=None, job_finished_time=None):
Fang Deng0454e632014-04-07 15:39:47 -070035 """Build a test view using the given fields.
36
37 @param test_idx: An integer representing test_idx.
38 @param test_name: A string, e.g. 'dummy_Pass'
39 @param subdir: A string representing the subdir field of the test view.
40 e.g. 'dummy_Pass'.
41 @param status: A string representing the test status.
42 e.g. 'FAIL', 'PASS'
43 @param afe_job_id: An integer representing the afe job id.
44 @param job_name: A string representing the job name.
45 @param reason: A string representing the reason field of the test view.
46 @param job_keyvals: A dictionary stroing the job keyvals.
47 @param test_started_time: A string, e.g. '2014-04-12 12:35:33'
48 @param test_finished_time: A string, e.g. '2014-04-12 12:35:33'
Fang Dengaeab6172014-05-07 17:17:04 -070049 @param invalidates_test_idx: An integer, representing the idx of the
50 test that has been retried.
Fang Dengf8503532014-06-12 18:21:55 -070051 @param job_started_time: A string, e.g. '2014-04-12 12:35:33'
52 @param job_finished_time: A string, e.g. '2014-04-12 12:35:33'
Fang Deng0454e632014-04-07 15:39:47 -070053
54 @reutrn: A dictionary representing a test view.
55
56 """
57 if job_keyvals is None:
58 job_keyvals = {}
59 return {'test_idx': test_idx, 'test_name': test_name, 'subdir':subdir,
60 'status': status, 'afe_job_id': afe_job_id,
61 'job_name': job_name, 'reason': reason,
62 'job_keyvals': job_keyvals,
63 'test_started_time': test_started_time,
Fang Dengaeab6172014-05-07 17:17:04 -070064 'test_finished_time': test_finished_time,
Fang Dengf8503532014-06-12 18:21:55 -070065 'invalidates_test_idx': invalidates_test_idx,
66 'job_started_time': job_started_time,
67 'job_finished_time': job_finished_time}
Fang Deng0454e632014-04-07 15:39:47 -070068
69
70 def _mock_tko_get_detailed_test_views(self, test_views):
71 """Mock tko method get_detailed_test_views call.
72
73 @param test_views: A list of test views that will be returned
74 by get_detailed_test_views.
75 """
76 return_values = {}
77 for v in test_views:
78 views_of_job = return_values.setdefault(
79 ('get_detailed_test_views', v['afe_job_id']), [])
80 views_of_job.append(v)
81
82 def side_effect(*args, **kwargs):
83 """Maps args and kwargs to the mocked return values."""
84 key = (kwargs['call'], kwargs['afe_job_id'])
85 return return_values[key]
86
87 self.tko.run = mock.MagicMock(side_effect=side_effect)
88
89
90 def _mock_afe_get_jobs(self, suite_job_id, child_job_ids):
91 """Mock afe get_jobs call.
92
93 @param suite_job_id: The afe job id of the suite job.
94 @param child_job_ids: A list of job ids of the child jobs.
95
96 """
Fang Dengf8503532014-06-12 18:21:55 -070097 suite_job = mock.MagicMock()
98 suite_job.id = suite_job_id
99 suite_job.max_runtime_mins = 10
100 suite_job.parent_job = None
101
Fang Deng0454e632014-04-07 15:39:47 -0700102 return_values = {suite_job_id: []}
103 for job_id in child_job_ids:
104 new_job = mock.MagicMock()
105 new_job.id = job_id
Fang Dengf8503532014-06-12 18:21:55 -0700106 new_job.max_runtime_mins = self.JOB_MAX_RUNTIME_MINS
107 new_job.parent_job = suite_job
Fang Deng0454e632014-04-07 15:39:47 -0700108 return_values[suite_job_id].append(new_job)
109
110 def side_effect(*args, **kwargs):
111 """Maps args and kwargs to the mocked return values."""
Fang Dengf8503532014-06-12 18:21:55 -0700112 if kwargs.get('id') == suite_job_id:
113 return [suite_job]
Fang Deng0454e632014-04-07 15:39:47 -0700114 return return_values[kwargs['parent_job_id']]
115
116 self.afe.get_jobs = mock.MagicMock(side_effect=side_effect)
117
118
119 def testFetchSuiteTestView(self):
120 """Test that it fetches the correct suite test views."""
121 suite_job_id = 100
Fang Dengaeab6172014-05-07 17:17:04 -0700122 suite_name = 'dummy'
123 build = 'R23-1.1.1.1'
Fang Deng0454e632014-04-07 15:39:47 -0700124 server_job_view = self._build_view(
125 10, 'SERVER_JOB', '----', 'GOOD', suite_job_id)
126 test_to_ignore = self._build_view(
127 11, 'dummy_Pass', '101-user/host/dummy_Pass',
128 'GOOD', suite_job_id)
129 test_to_include = self._build_view(
130 12, 'dummy_Pass.bluetooth', None, 'TEST_NA', suite_job_id)
Fang Dengf8503532014-06-12 18:21:55 -0700131 self._mock_afe_get_jobs(suite_job_id, [])
Fang Deng0454e632014-04-07 15:39:47 -0700132 self._mock_tko_get_detailed_test_views(
133 [server_job_view, test_to_ignore, test_to_include])
134 collector = run_suite.ResultCollector(
135 'fake_server', self.afe, self.tko,
MK Ryu977a9752014-10-21 11:58:09 -0700136 build='fake/build', board='fake', suite_name='dummy',
Fang Deng0454e632014-04-07 15:39:47 -0700137 suite_job_id=suite_job_id)
138 suite_views = collector._fetch_relevant_test_views_of_suite()
139 suite_views = sorted(suite_views, key=lambda view: view['test_idx'])
Shuqian Zhaoc085abb2016-02-24 11:27:26 -0800140 # Verify that SERVER_JOB is renamed to 'Suite job'
Fang Dengaeab6172014-05-07 17:17:04 -0700141 self.assertEqual(suite_views[0].get_testname(),
Shuqian Zhaoc085abb2016-02-24 11:27:26 -0800142 run_suite.TestView.SUITE_JOB)
Fang Deng0454e632014-04-07 15:39:47 -0700143 # Verify that the test with a subidr is not included.
Fang Dengaeab6172014-05-07 17:17:04 -0700144 self.assertEqual(suite_views[0]['test_idx'], 10)
145 self.assertEqual(suite_views[1]['test_idx'], 12)
Fang Deng0454e632014-04-07 15:39:47 -0700146
147
148 def testFetchTestViewOfChildJobs(self):
149 """Test that it fetches the correct child test views."""
150 build = 'lumpy-release/R36-5788.0.0'
MK Ryu977a9752014-10-21 11:58:09 -0700151 board = 'lumpy'
Fang Deng0454e632014-04-07 15:39:47 -0700152 suite_name = 'my_suite'
153 suite_job_id = 100
Fang Dengaeab6172014-05-07 17:17:04 -0700154 invalid_job_id = 101
155 invalid_job_name = '%s/%s/test_Pass' % (build, suite_name)
156 good_job_id = 102
Fang Deng0454e632014-04-07 15:39:47 -0700157 good_job_name = '%s/%s/test_Pass' % (build, suite_name)
Fang Dengaeab6172014-05-07 17:17:04 -0700158 bad_job_id = 103
Fang Deng0454e632014-04-07 15:39:47 -0700159 bad_job_name = '%s/%s/test_ServerJobFail' % (build, suite_name)
160
Fang Dengaeab6172014-05-07 17:17:04 -0700161 invalid_test = self._build_view(
162 19, 'test_Pass_Old', 'fake/subdir',
163 'FAIL', invalid_job_id, invalid_job_name)
Fang Deng0454e632014-04-07 15:39:47 -0700164 good_job_server_job = self._build_view(
165 20, 'SERVER_JOB', '----', 'GOOD', good_job_id, good_job_name)
166 good_job_test = self._build_view(
Fang Dengaeab6172014-05-07 17:17:04 -0700167 21, 'test_Pass', 'fake/subdir', 'GOOD',
168 good_job_id, good_job_name,
169 job_keyvals={'retry_original_job_id': invalid_job_id})
Fang Deng0454e632014-04-07 15:39:47 -0700170 bad_job_server_job = self._build_view(
171 22, 'SERVER_JOB', '----', 'FAIL', bad_job_id, bad_job_name)
172 bad_job_test = self._build_view(
173 23, 'test_ServerJobFail', 'fake/subdir', 'GOOD',
174 bad_job_id, bad_job_name)
175 self._mock_tko_get_detailed_test_views(
176 [good_job_server_job, good_job_test,
Fang Dengaeab6172014-05-07 17:17:04 -0700177 bad_job_server_job, bad_job_test, invalid_test])
Fang Deng0454e632014-04-07 15:39:47 -0700178 self._mock_afe_get_jobs(suite_job_id, [good_job_id, bad_job_id])
179 collector = run_suite.ResultCollector(
180 'fake_server', self.afe, self.tko,
MK Ryu977a9752014-10-21 11:58:09 -0700181 build, board, suite_name, suite_job_id)
Fang Dengaeab6172014-05-07 17:17:04 -0700182 child_views, retry_counts = collector._fetch_test_views_of_child_jobs()
183 # child_views should contain tests 21, 22, 23
Fang Deng0454e632014-04-07 15:39:47 -0700184 child_views = sorted(child_views, key=lambda view: view['test_idx'])
185 # Verify that the SERVER_JOB has been renamed properly
Fang Dengaeab6172014-05-07 17:17:04 -0700186 self.assertEqual(child_views[1].get_testname(),
187 'test_ServerJobFail_SERVER_JOB')
188 # Verify that failed SERVER_JOB and actual invalid tests are included,
189 expected = [good_job_test['test_idx'], bad_job_server_job['test_idx'],
190 bad_job_test['test_idx']]
191 child_view_ids = [v['test_idx'] for v in child_views]
192 self.assertEqual(child_view_ids, expected)
193 self.afe.get_jobs.assert_called_once_with(
194 parent_job_id=suite_job_id)
195 # Verify the retry_counts is calculated correctly
196 self.assertEqual(len(retry_counts), 1)
197 self.assertEqual(retry_counts[21], 1)
Fang Deng0454e632014-04-07 15:39:47 -0700198
199
200 def testGenerateLinks(self):
201 """Test that it generates correct web and buildbot links."""
202 suite_job_id = 100
Fang Dengaeab6172014-05-07 17:17:04 -0700203 suite_name = 'my_suite'
204 build = 'lumpy-release/R36-5788.0.0'
MK Ryu977a9752014-10-21 11:58:09 -0700205 board = 'lumpy'
Fang Dengf8503532014-06-12 18:21:55 -0700206 fake_job = mock.MagicMock()
207 fake_job.parent = suite_job_id
Fang Dengaeab6172014-05-07 17:17:04 -0700208 suite_job_view = run_suite.TestView(
209 self._build_view(
Shuqian Zhaoc085abb2016-02-24 11:27:26 -0800210 20, 'Suite job', '----', 'GOOD', suite_job_id),
Simran Basi01984f52015-10-12 15:36:45 -0700211 fake_job, suite_name, build, 'chromeos-test')
Fang Dengaeab6172014-05-07 17:17:04 -0700212 good_test = run_suite.TestView(
213 self._build_view(
214 21, 'test_Pass', 'fake/subdir', 'GOOD', 101),
Simran Basi01984f52015-10-12 15:36:45 -0700215 fake_job, suite_name, build, 'chromeos-test')
Fang Dengaeab6172014-05-07 17:17:04 -0700216 bad_test = run_suite.TestView(
217 self._build_view(
218 23, 'test_Fail', 'fake/subdir', 'FAIL', 102),
Simran Basi01984f52015-10-12 15:36:45 -0700219 fake_job, suite_name, build, 'chromeos-test')
Fang Deng0454e632014-04-07 15:39:47 -0700220
221 collector = run_suite.ResultCollector(
222 'fake_server', self.afe, self.tko,
Simran Basi01984f52015-10-12 15:36:45 -0700223 build, board, suite_name, suite_job_id, user='chromeos-test')
Fang Deng0454e632014-04-07 15:39:47 -0700224 collector._suite_views = [suite_job_view]
225 collector._test_views = [suite_job_view, good_test, bad_test]
226 collector._max_testname_width = max(
Fang Dengaeab6172014-05-07 17:17:04 -0700227 [len(v.get_testname()) for v in collector._test_views]) + 3
Fang Deng0454e632014-04-07 15:39:47 -0700228 collector._generate_web_and_buildbot_links()
Allen Lidc2c69a2016-09-14 19:05:47 -0700229 URL_PATTERN = run_suite._URL_PATTERN
Fang Deng0454e632014-04-07 15:39:47 -0700230 # expected_web_links is list of (anchor, url) tuples we
231 # are expecting.
232 expected_web_links = [
Allen Li34613242016-09-02 11:52:34 -0700233 (v.get_testname(),
Fang Deng0454e632014-04-07 15:39:47 -0700234 URL_PATTERN % ('fake_server',
Simran Basi01984f52015-10-12 15:36:45 -0700235 '%s-%s' % (v['afe_job_id'], 'chromeos-test')))
Fang Deng0454e632014-04-07 15:39:47 -0700236 for v in collector._test_views]
237 # Verify web links are generated correctly.
238 for i in range(len(collector._web_links)):
239 expect = expected_web_links[i]
240 self.assertEqual(collector._web_links[i].anchor, expect[0])
241 self.assertEqual(collector._web_links[i].url, expect[1])
242
243 expected_buildbot_links = [
Allen Li34613242016-09-02 11:52:34 -0700244 (v.get_testname(),
Fang Deng0454e632014-04-07 15:39:47 -0700245 URL_PATTERN % ('fake_server',
Simran Basi01984f52015-10-12 15:36:45 -0700246 '%s-%s' % (v['afe_job_id'], 'chromeos-test')))
Fang Deng0454e632014-04-07 15:39:47 -0700247 for v in collector._test_views if v['status'] != 'GOOD']
248 # Verify buildbot links are generated correctly.
249 for i in range(len(collector._buildbot_links)):
250 expect = expected_buildbot_links[i]
251 self.assertEqual(collector._buildbot_links[i].anchor, expect[0])
252 self.assertEqual(collector._buildbot_links[i].url, expect[1])
Fang Dengaeab6172014-05-07 17:17:04 -0700253 self.assertEqual(collector._buildbot_links[i].retry_count, 0)
Simran Basi7203d4e2015-02-03 15:50:18 -0800254 # Assert that a wmatrix retry dashboard link is created.
255 self.assertNotEqual(
256 collector._buildbot_links[i].GenerateWmatrixRetryLink(),'')
Fang Deng0454e632014-04-07 15:39:47 -0700257
258
Fang Deng5a43be62014-05-07 17:17:04 -0700259 def _end_to_end_test_helper(
260 self, include_bad_test=False, include_warn_test=False,
261 include_experimental_bad_test=False, include_timeout_test=False,
Fang Dengaeab6172014-05-07 17:17:04 -0700262 include_self_aborted_test=False,
Fang Dengf8503532014-06-12 18:21:55 -0700263 include_aborted_by_suite_test=False,
Fang Dengaeab6172014-05-07 17:17:04 -0700264 include_good_retry=False, include_bad_retry=False,
Fang Dengf8503532014-06-12 18:21:55 -0700265 suite_job_timed_out=False, suite_job_status='GOOD'):
Fang Deng0454e632014-04-07 15:39:47 -0700266 """A helper method for testing ResultCollector end-to-end.
267
268 This method mocks the retrieving of required test views,
269 and call ResultCollector.run() to collect the results.
270
Fang Dengaeab6172014-05-07 17:17:04 -0700271 @param include_bad_test:
272 If True, include a view of a test which has status 'FAIL'.
273 @param include_warn_test:
274 If True, include a view of a test which has status 'WARN'
Fang Deng0454e632014-04-07 15:39:47 -0700275 @param include_experimental_bad_test:
276 If True, include a view of an experimental test
277 which has status 'FAIL'.
Fang Dengaeab6172014-05-07 17:17:04 -0700278 @param include_timeout_test:
279 If True, include a view of a test which was aborted before
280 started.
281 @param include_self_aborted_test:
282 If True, include a view of test which was aborted after
Fang Dengf8503532014-06-12 18:21:55 -0700283 started and hit hits own timeout.
284 @param include_self_aborted_by_suite_test:
285 If True, include a view of test which was aborted after
286 started but has not hit its own timeout.
Fang Dengaeab6172014-05-07 17:17:04 -0700287 @param include_good_retry:
288 If True, include a test that passed after retry.
289 @param include_bad_retry:
290 If True, include a test that failed after retry.
Fang Dengf8503532014-06-12 18:21:55 -0700291 @param suite_job_status: One of 'GOOD' 'FAIL' 'ABORT' 'RUNNING'
Fang Deng0454e632014-04-07 15:39:47 -0700292
Fang Dengaeab6172014-05-07 17:17:04 -0700293 @returns: A ResultCollector instance.
Fang Deng0454e632014-04-07 15:39:47 -0700294 """
295 suite_job_id = 100
296 good_job_id = 101
297 bad_job_id = 102
Fang Deng5a43be62014-05-07 17:17:04 -0700298 warn_job_id = 102
299 experimental_bad_job_id = 102
300 timeout_job_id = 100
301 self_aborted_job_id = 104
Fang Dengf8503532014-06-12 18:21:55 -0700302 aborted_by_suite_job_id = 105
303 good_retry_job_id = 106
304 bad_retry_job_id = 107
Fang Dengaeab6172014-05-07 17:17:04 -0700305 invalid_job_id_1 = 90
306 invalid_job_id_2 = 91
307 suite_name = 'dummy'
308 build = 'lumpy-release/R27-3888.0.0'
Fang Deng0454e632014-04-07 15:39:47 -0700309 suite_job_keyvals = {
310 constants.DOWNLOAD_STARTED_TIME: '2014-04-29 13:14:20',
311 constants.PAYLOAD_FINISHED_TIME: '2014-04-29 13:14:25',
312 constants.ARTIFACT_FINISHED_TIME: '2014-04-29 13:14:30'}
313
Fang Dengf8503532014-06-12 18:21:55 -0700314 suite_job_started_time = '2014-04-29 13:14:37'
315 if suite_job_timed_out:
316 suite_job_keyvals['aborted_by'] = 'test_user'
317 suite_job_finished_time = '2014-04-29 13:25:37'
318 suite_job_status = 'ABORT'
319 else:
320 suite_job_finished_time = '2014-04-29 13:23:37'
321
Fang Deng0454e632014-04-07 15:39:47 -0700322 server_job_view = self._build_view(
Fang Deng5a43be62014-05-07 17:17:04 -0700323 10, 'SERVER_JOB', '----', suite_job_status, suite_job_id,
Fang Deng0454e632014-04-07 15:39:47 -0700324 'lumpy-release/R27-3888.0.0-test_suites/control.dummy',
325 '', suite_job_keyvals, '2014-04-29 13:14:37',
Fang Dengf8503532014-06-12 18:21:55 -0700326 '2014-04-29 13:20:27', job_started_time=suite_job_started_time,
327 job_finished_time=suite_job_finished_time)
Fang Deng0454e632014-04-07 15:39:47 -0700328 good_test = self._build_view(
329 11, 'dummy_Pass', '101-user/host/dummy_Pass', 'GOOD',
Fang Dengaeab6172014-05-07 17:17:04 -0700330 good_job_id, 'lumpy-release/R27-3888.0.0/dummy/dummy_Pass',
Fang Deng0454e632014-04-07 15:39:47 -0700331 '', {}, '2014-04-29 13:15:35', '2014-04-29 13:15:36')
332 bad_test = self._build_view(
333 12, 'dummy_Fail.Fail', '102-user/host/dummy_Fail.Fail', 'FAIL',
Fang Dengaeab6172014-05-07 17:17:04 -0700334 bad_job_id, 'lumpy-release/R27-3888.0.0/dummy/dummy_Fail.Fail',
Fang Deng0454e632014-04-07 15:39:47 -0700335 'always fail', {}, '2014-04-29 13:16:00',
336 '2014-04-29 13:16:02')
337 warn_test = self._build_view(
Fang Deng5a43be62014-05-07 17:17:04 -0700338 13, 'dummy_Fail.Warn', '102-user/host/dummy_Fail.Warn', 'WARN',
Fang Dengaeab6172014-05-07 17:17:04 -0700339 warn_job_id, 'lumpy-release/R27-3888.0.0/dummy/dummy_Fail.Warn',
Fang Deng0454e632014-04-07 15:39:47 -0700340 'always warn', {}, '2014-04-29 13:16:00',
341 '2014-04-29 13:16:02')
342 experimental_bad_test = self._build_view(
Fang Deng5a43be62014-05-07 17:17:04 -0700343 14, 'experimental_dummy_Fail.Fail',
Fang Deng0454e632014-04-07 15:39:47 -0700344 '102-user/host/dummy_Fail.Fail', 'FAIL',
345 experimental_bad_job_id,
Fang Dengaeab6172014-05-07 17:17:04 -0700346 'lumpy-release/R27-3888.0.0/dummy/experimental_dummy_Fail.Fail',
Fang Deng0454e632014-04-07 15:39:47 -0700347 'always fail', {'experimental': 'True'}, '2014-04-29 13:16:06',
348 '2014-04-29 13:16:07')
Fang Deng5a43be62014-05-07 17:17:04 -0700349 timeout_test = self._build_view(
350 15, 'dummy_Timeout', '', 'ABORT',
Fang Dengaeab6172014-05-07 17:17:04 -0700351 timeout_job_id,
352 'lumpy-release/R27-3888.0.0/dummy/dummy_Timeout',
Fang Deng5a43be62014-05-07 17:17:04 -0700353 'child job did not run', {}, '2014-04-29 13:15:37',
354 '2014-04-29 13:15:38')
355 self_aborted_test = self._build_view(
356 16, 'dummy_Abort', '104-user/host/dummy_Abort', 'ABORT',
Fang Dengaeab6172014-05-07 17:17:04 -0700357 self_aborted_job_id,
358 'lumpy-release/R27-3888.0.0/dummy/dummy_Abort',
Fang Dengf8503532014-06-12 18:21:55 -0700359 'child job aborted', {'aborted_by': 'test_user'},
360 '2014-04-29 13:15:39', '2014-04-29 13:15:40',
361 job_started_time='2014-04-29 13:15:39',
362 job_finished_time='2014-04-29 13:25:40')
363 aborted_by_suite = self._build_view(
364 17, 'dummy_AbortBySuite', '105-user/host/dummy_AbortBySuite',
365 'RUNNING', aborted_by_suite_job_id,
366 'lumpy-release/R27-3888.0.0/dummy/dummy_Abort',
367 'aborted by suite', {'aborted_by': 'test_user'},
368 '2014-04-29 13:15:39', '2014-04-29 13:15:40',
369 job_started_time='2014-04-29 13:15:39',
370 job_finished_time='2014-04-29 13:15:40')
Fang Dengaeab6172014-05-07 17:17:04 -0700371 good_retry = self._build_view(
Fang Dengf8503532014-06-12 18:21:55 -0700372 18, 'dummy_RetryPass', '106-user/host/dummy_RetryPass', 'GOOD',
Fang Dengaeab6172014-05-07 17:17:04 -0700373 good_retry_job_id,
374 'lumpy-release/R27-3888.0.0/dummy/dummy_RetryPass',
375 '', {'retry_original_job_id': invalid_job_id_1},
376 '2014-04-29 13:15:37',
377 '2014-04-29 13:15:38', invalidates_test_idx=1)
378 bad_retry = self._build_view(
Fang Dengf8503532014-06-12 18:21:55 -0700379 19, 'dummy_RetryFail', '107-user/host/dummy_RetryFail', 'FAIL',
Fang Dengaeab6172014-05-07 17:17:04 -0700380 bad_retry_job_id,
381 'lumpy-release/R27-3888.0.0/dummy/dummy_RetryFail',
382 'retry failed', {'retry_original_job_id': invalid_job_id_2},
383 '2014-04-29 13:15:39', '2014-04-29 13:15:40',
384 invalidates_test_idx=2)
385 invalid_test_1 = self._build_view(
386 1, 'dummy_RetryPass', '90-user/host/dummy_RetryPass', 'GOOD',
387 invalid_job_id_1,
388 'lumpy-release/R27-3888.0.0/dummy/dummy_RetryPass',
389 'original test failed', {}, '2014-04-29 13:10:00',
390 '2014-04-29 13:10:01')
391 invalid_test_2 = self._build_view(
392 2, 'dummy_RetryFail', '91-user/host/dummy_RetryFail', 'FAIL',
393 invalid_job_id_2,
394 'lumpy-release/R27-3888.0.0/dummy/dummy_RetryFail',
395 'original test failed', {},
396 '2014-04-29 13:10:03', '2014-04-29 13:10:04')
397
Fang Deng0454e632014-04-07 15:39:47 -0700398 test_views = [server_job_view, good_test]
Fang Deng5a43be62014-05-07 17:17:04 -0700399 child_jobs = set([good_job_id])
Fang Deng0454e632014-04-07 15:39:47 -0700400 if include_bad_test:
401 test_views.append(bad_test)
Fang Deng5a43be62014-05-07 17:17:04 -0700402 child_jobs.add(bad_job_id)
Fang Deng0454e632014-04-07 15:39:47 -0700403 if include_warn_test:
404 test_views.append(warn_test)
Fang Deng5a43be62014-05-07 17:17:04 -0700405 child_jobs.add(warn_job_id)
Fang Deng0454e632014-04-07 15:39:47 -0700406 if include_experimental_bad_test:
407 test_views.append(experimental_bad_test)
Fang Deng5a43be62014-05-07 17:17:04 -0700408 child_jobs.add(experimental_bad_job_id)
409 if include_timeout_test:
410 test_views.append(timeout_test)
Fang Deng5a43be62014-05-07 17:17:04 -0700411 if include_self_aborted_test:
412 test_views.append(self_aborted_test)
413 child_jobs.add(self_aborted_job_id)
Fang Dengaeab6172014-05-07 17:17:04 -0700414 if include_good_retry:
415 test_views.extend([good_retry, invalid_test_1])
416 child_jobs.add(good_retry_job_id)
417 if include_bad_retry:
418 test_views.extend([bad_retry, invalid_test_2])
419 child_jobs.add(bad_retry_job_id)
Fang Dengf8503532014-06-12 18:21:55 -0700420 if include_aborted_by_suite_test:
421 test_views.append(aborted_by_suite)
422 child_jobs.add(aborted_by_suite_job_id)
Fang Deng0454e632014-04-07 15:39:47 -0700423 self._mock_tko_get_detailed_test_views(test_views)
424 self._mock_afe_get_jobs(suite_job_id, child_jobs)
425 collector = run_suite.ResultCollector(
426 'fake_server', self.afe, self.tko,
MK Ryu977a9752014-10-21 11:58:09 -0700427 'lumpy-release/R36-5788.0.0', 'lumpy', 'dummy', suite_job_id)
Fang Deng0454e632014-04-07 15:39:47 -0700428 collector.run()
429 return collector
430
431
432 def testEndToEndSuitePass(self):
433 """Test it returns code OK when all test pass."""
434 collector = self._end_to_end_test_helper()
435 self.assertEqual(collector.return_code, run_suite.RETURN_CODES.OK)
436
437
438 def testEndToEndExperimentalTestFails(self):
439 """Test that it returns code OK when only experimental test fails."""
440 collector = self._end_to_end_test_helper(
441 include_experimental_bad_test=True)
442 self.assertEqual(collector.return_code, run_suite.RETURN_CODES.OK)
443
444
445 def testEndToEndSuiteWarn(self):
446 """Test it returns code WARNING when there is a test that warns."""
447 collector = self._end_to_end_test_helper(include_warn_test=True)
448 self.assertEqual(collector.return_code, run_suite.RETURN_CODES.WARNING)
449
450
451 def testEndToEndSuiteFail(self):
452 """Test it returns code ERROR when there is a test that fails."""
453 # Test that it returns ERROR when there is test that fails.
454 collector = self._end_to_end_test_helper(include_bad_test=True)
455 self.assertEqual(collector.return_code, run_suite.RETURN_CODES.ERROR)
456
457 # Test that it returns ERROR when both experimental and non-experimental
458 # test fail.
459 collector = self._end_to_end_test_helper(
460 include_bad_test=True, include_warn_test=True,
461 include_experimental_bad_test=True)
462 self.assertEqual(collector.return_code, run_suite.RETURN_CODES.ERROR)
463
Fang Dengaeab6172014-05-07 17:17:04 -0700464 collector = self._end_to_end_test_helper(include_self_aborted_test=True)
465 self.assertEqual(collector.return_code, run_suite.RETURN_CODES.ERROR)
466
Fang Deng0454e632014-04-07 15:39:47 -0700467
Fang Deng5a43be62014-05-07 17:17:04 -0700468 def testEndToEndSuiteJobFail(self):
469 """Test it returns code SUITE_FAILURE when only the suite job failed."""
470 collector = self._end_to_end_test_helper(suite_job_status='ABORT')
471 self.assertEqual(
472 collector.return_code, run_suite.RETURN_CODES.INFRA_FAILURE)
473
474 collector = self._end_to_end_test_helper(suite_job_status='ERROR')
475 self.assertEqual(
476 collector.return_code, run_suite.RETURN_CODES.INFRA_FAILURE)
477
478
Fang Dengaeab6172014-05-07 17:17:04 -0700479 def testEndToEndRetry(self):
480 """Test it returns correct code when a test was retried."""
481 collector = self._end_to_end_test_helper(include_good_retry=True)
482 self.assertEqual(
483 collector.return_code, run_suite.RETURN_CODES.WARNING)
484
485 collector = self._end_to_end_test_helper(include_good_retry=True,
486 include_self_aborted_test=True)
487 self.assertEqual(
488 collector.return_code, run_suite.RETURN_CODES.ERROR)
489
490 collector = self._end_to_end_test_helper(include_good_retry=True,
491 include_bad_test=True)
492 self.assertEqual(
493 collector.return_code, run_suite.RETURN_CODES.ERROR)
494
495 collector = self._end_to_end_test_helper(include_bad_retry=True)
496 self.assertEqual(
497 collector.return_code, run_suite.RETURN_CODES.ERROR)
498
499
Fang Deng5a43be62014-05-07 17:17:04 -0700500 def testEndToEndSuiteTimeout(self):
501 """Test it returns correct code when a child job timed out."""
Fang Dengf8503532014-06-12 18:21:55 -0700502 # a child job timed out before started, none failed.
Fang Deng5a43be62014-05-07 17:17:04 -0700503 collector = self._end_to_end_test_helper(include_timeout_test=True)
504 self.assertEqual(
505 collector.return_code, run_suite.RETURN_CODES.SUITE_TIMEOUT)
506
Fang Dengf8503532014-06-12 18:21:55 -0700507 # a child job timed out before started, and one test failed.
Fang Deng5a43be62014-05-07 17:17:04 -0700508 collector = self._end_to_end_test_helper(
509 include_bad_test=True, include_timeout_test=True)
510 self.assertEqual(collector.return_code, run_suite.RETURN_CODES.ERROR)
511
Fang Dengf8503532014-06-12 18:21:55 -0700512 # a child job timed out before started, and one test warned.
Fang Deng5a43be62014-05-07 17:17:04 -0700513 collector = self._end_to_end_test_helper(
514 include_warn_test=True, include_timeout_test=True)
Fang Dengaeab6172014-05-07 17:17:04 -0700515 self.assertEqual(collector.return_code,
516 run_suite.RETURN_CODES.SUITE_TIMEOUT)
517
Fang Dengf8503532014-06-12 18:21:55 -0700518 # a child job timed out before started, and one test was retried.
Fang Dengaeab6172014-05-07 17:17:04 -0700519 collector = self._end_to_end_test_helper(include_good_retry=True,
520 include_timeout_test=True)
Fang Deng5a43be62014-05-07 17:17:04 -0700521 self.assertEqual(
522 collector.return_code, run_suite.RETURN_CODES.SUITE_TIMEOUT)
523
Fang Dengf8503532014-06-12 18:21:55 -0700524 # a child jot was aborted because suite timed out.
525 collector = self._end_to_end_test_helper(
526 include_aborted_by_suite_test=True)
527 self.assertEqual(
528 collector.return_code, run_suite.RETURN_CODES.OK)
529
530 # suite job timed out.
531 collector = self._end_to_end_test_helper(suite_job_timed_out=True)
532 self.assertEqual(
533 collector.return_code, run_suite.RETURN_CODES.SUITE_TIMEOUT)
534
Fang Deng5a43be62014-05-07 17:17:04 -0700535
Ningning Xiabd911bd2016-04-19 14:06:03 -0700536class LogLinkUnittests(unittest.TestCase):
537 """Test the LogLink"""
538
539 def testGenerateBuildbotLinks(self):
540 """Test LogLink GenerateBuildbotLinks"""
541 log_link_a = run_suite.LogLink('mock_anchor', 'mock_server',
542 'mock_job_string',
543 bug_info=('mock_bug_id', 1),
544 reason='mock_reason',
545 retry_count=1,
546 testname='mock_testname')
547 # Generate a bug link and a log link when bug_info is present
Allen Lie082ced2016-09-14 15:19:20 -0700548 self.assertTrue(len(list(log_link_a.GenerateBuildbotLinks())) == 2)
Ningning Xiabd911bd2016-04-19 14:06:03 -0700549
550 log_link_b = run_suite.LogLink('mock_anchor', 'mock_server',
551 'mock_job_string_b',
552 reason='mock_reason',
553 retry_count=1,
554 testname='mock_testname')
555 # Generate a log link when there is no bug_info
Allen Lie082ced2016-09-14 15:19:20 -0700556 self.assertTrue(len(list(log_link_b.GenerateBuildbotLinks())) == 1)
Ningning Xiabd911bd2016-04-19 14:06:03 -0700557
558
Prashanth Ba7be2072014-07-15 15:03:21 -0700559class SimpleTimerUnittests(unittest.TestCase):
560 """Test the simple timer."""
561
562 def testPoll(self):
563 """Test polling the timer."""
564 interval_hours = 0.0001
565 t = diagnosis_utils.SimpleTimer(interval_hours=interval_hours)
566 deadline = t.deadline
567 self.assertTrue(deadline is not None and
568 t.interval_hours == interval_hours)
569 min_deadline = (datetime.now() +
570 datetime_base.timedelta(hours=interval_hours))
571 time.sleep(interval_hours * 3600)
572 self.assertTrue(t.poll())
573 self.assertTrue(t.deadline >= min_deadline)
574
575
576 def testBadInterval(self):
577 """Test a bad interval."""
578 t = diagnosis_utils.SimpleTimer(interval_hours=-1)
579 self.assertTrue(t.deadline is None and t.poll() == False)
580 t._reset()
581 self.assertTrue(t.deadline is None and t.poll() == False)
582
583
Allen Li910c6042016-10-24 17:52:25 -0700584class ArgumentParserUnittests(unittest.TestCase):
585 """Tests for argument parser."""
586
587 @unittest.expectedFailure
588 def test_crbug_658013(self):
589 """crbug.com/658013
590
591 Expected failure due to http://bugs.python.org/issue9334
592 """
593 parser = run_suite.make_parser()
594 args = [
595 '--board', 'heli',
596 '--build', 'trybot-heli-paladin/R56-8918.0.0-b1601',
597 '--suite_name', 'test_that_wrapper',
598 '--pool', 'suites',
599 '--max_runtime_mins', '20',
600 '--suite_args', '-b heli -i trybot-heli-paladin/R56-8918.0.0-b1601 :lab: suite:bvt-inline',
601 ]
602
603 def error_handler(msg): # pylint: disable=missing-docstring
604 self.fail('Argument parsing failed: ' + msg)
605
606 parser.error = error_handler
607 got = parser.parse_args(args)
608 self.assertEqual(
609 got.board, 'heli')
610 self.assertEqual(
611 got.build, 'trybot-heli-paladin/R56-8918.0.0-b1601')
612 self.assertEqual(
613 got.suite_args,
614 '-b heli -i trybot-heli-paladin/R56-8918.0.0-b1601 :lab: suite:bvt-inline')
615
616 def test_crbug_658013b(self):
617 """crbug.com/658013
618
619 Unambiguous behavior.
620 """
621 parser = run_suite.make_parser()
622 args = [
623 '--board=heli',
624 '--build=trybot-heli-paladin/R56-8918.0.0-b1601',
625 '--suite_name=test_that_wrapper',
626 '--pool=suites',
627 '--max_runtime_mins=20',
628 '--suite_args=-b heli -i trybot-heli-paladin/R56-8918.0.0-b1601 :lab: suite:bvt-inline',
629 ]
630
631 def error_handler(msg): # pylint: disable=missing-docstring
632 self.fail('Argument parsing failed: ' + msg)
633
634 parser.error = error_handler
635 got = parser.parse_args(args)
636 self.assertEqual(
637 got.board, 'heli')
638 self.assertEqual(
639 got.build, 'trybot-heli-paladin/R56-8918.0.0-b1601')
640 self.assertEqual(
641 got.suite_args,
642 '-b heli -i trybot-heli-paladin/R56-8918.0.0-b1601 :lab: suite:bvt-inline')
643
644
Fang Deng0454e632014-04-07 15:39:47 -0700645if __name__ == '__main__':
646 unittest.main()