blob: 382bfd70286637096d0313d06d066455e7908497 [file] [log] [blame]
Xixuan Wu0c76d5b2017-08-30 16:40:17 -07001# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module for task unittests."""
Xixuan Wu5ff0fac2019-01-07 10:06:35 -08006# pylint: disable=g-missing-super-call
Xixuan Wu0c76d5b2017-08-30 16:40:17 -07007
Xinan Lindf0698a2020-02-05 22:38:11 -08008import json
Xixuan Wu0c76d5b2017-08-30 16:40:17 -07009import re
10import unittest
11
Xixuan Wu5451a662017-10-17 10:57:40 -070012import build_lib
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070013import config_reader
14import mock
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070015import task
Xixuan Wu51bb7102019-03-18 14:51:44 -070016import task_config_reader
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070017import tot_manager
18
Xinan Lindf0698a2020-02-05 22:38:11 -080019from chromite.api.gen.test_platform.suite_scheduler import analytics_pb2
20from chromite.api.gen.chromiumos import branch_pb2
21
Sean McAllister66bf7e92021-07-16 18:46:04 +000022from google.protobuf import json_format
Xinan Lindf0698a2020-02-05 22:38:11 -080023
24FAKE_UUID = 'c78e0bf3-4142-11ea-bc66-88e9fe4c5349'
25
26
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070027class FakeTotMilestoneManager(tot_manager.TotMilestoneManager):
28 """Mock class for tot_manager.TotMilestoneManager."""
29
30 def __init__(self, is_sanity):
31 self.is_sanity = is_sanity
32 self.storage_client = None
33 self.tot = self._tot_milestone()
34
35
Xixuan Wu8d2f2862018-08-28 16:48:04 -070036class FakeLabConfig(object):
Xixuan Wuf4a4c882019-03-15 14:48:26 -070037 """Mock class for config_reader.LabConfig."""
38
39 def get_cros_model_map(self):
Xinan Lin6668e0f2020-05-29 10:02:57 -070040 return {'coral': ['robo', 'nasher', 'lava'], 'hatch': ['akemi', 'kled']}
Xixuan Wuf4a4c882019-03-15 14:48:26 -070041
Garry Wang6ca42dd2022-02-28 21:54:19 -080042 def get_android_model_map(self):
43 return {'pixel3': ['pixel3xl'], 'pixel4': ['pixel4a']}
44
Xixuan Wuf4a4c882019-03-15 14:48:26 -070045
Xinan Lindf0698a2020-02-05 22:38:11 -080046class FakeBigqueryRestClient(object):
47
48 def __init__(self, rest_client, project=None, dataset=None, table=None):
49 """Initialize the mock class."""
50 self.table = table
51 self.rows = []
52
53 def insert(self, rows):
54 self.rows = rows
55 return True
56
57
Xixuan Wu5451a662017-10-17 10:57:40 -070058class TaskBaseTestCase(unittest.TestCase):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070059
60 BOARD = 'fake_board'
Xixuan Wu8d2f2862018-08-28 16:48:04 -070061 UNIBUILD_BOARD = 'coral'
Po-Hsien Wang6d589732018-05-15 17:19:34 -070062 EXCLUDE_BOARD = 'exclude_fake_board'
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070063 NAME = 'fake_name'
Brigit Rossbach6a8eea12021-01-11 09:35:31 -070064 ANALYTICS_NAME='fake_name'
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070065 SUITE = 'fake_suite'
66 POOL = 'fake_pool'
67 PLATFORM = '6182.0.0-rc2'
68 MILESTONE = '30'
69 NUM = 1
70 PRIORITY = 50
71 TIMEOUT = 600
72
73 def setUp(self):
74 tot_patcher = mock.patch('tot_manager.TotMilestoneManager')
75 self._mock_tot = tot_patcher.start()
76 self.addCleanup(tot_patcher.stop)
77 # Can't pass in False since storage_client is not mocked.
78 self._mock_tot.return_value = FakeTotMilestoneManager(True)
Xinan Lindf0698a2020-02-05 22:38:11 -080079
80 _mock_bq_client = mock.patch('rest_client.BigqueryRestClient')
81 mock_bq_client = _mock_bq_client.start()
82 self.addCleanup(_mock_bq_client.stop)
83 self.mock_bq_client = FakeBigqueryRestClient(
84 None, project='proj', dataset='dataset', table='foo')
85 mock_bq_client.return_value = self.mock_bq_client
86
Xinan Lin6668e0f2020-05-29 10:02:57 -070087 self.task = task.Task(
88 task_config_reader.TaskInfo(
89 name=self.NAME,
90 suite=self.SUITE,
91 branch_specs=[],
Brigit Rossbach6a8eea12021-01-11 09:35:31 -070092 analytics_name=self.ANALYTICS_NAME,
Xinan Lin6668e0f2020-05-29 10:02:57 -070093 pool=self.POOL,
94 num=self.NUM,
95 boards=','.join(
96 [self.BOARD, self.UNIBUILD_BOARD]),
97 exclude_boards=self.EXCLUDE_BOARD,
98 priority=self.PRIORITY,
99 timeout=self.TIMEOUT))
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700100
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700101 self._empty_configs = config_reader.Configs(
Brigit Rossbachbb080912020-11-18 13:52:17 -0700102 lab_config=config_reader.LabConfig(None))
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700103 self._fake_configs = config_reader.Configs(
Xinan Lin576022d2020-01-21 18:12:47 -0800104 lab_config=FakeLabConfig())
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700105
Xinan Lin028f9582019-12-11 10:55:33 -0800106 self.firmware_dict = {
107 ('cros', 'fake_board'): ('fake_board-release/R30-6182.0.0-rc2'),
108 ('firmware', 'fake_board'): ('firmware-fake_board-12345.67.'
109 'A-firmwarebranch/RFoo-1.0.0-b1234567'
110 '/fake_board')
111 }
112
Xixuan Wu5451a662017-10-17 10:57:40 -0700113
114class TaskTestCase(TaskBaseTestCase):
115
116 def setUp(self):
117 super(TaskTestCase, self).setUp()
118
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700119 mock_push = mock.patch('task.Task._push_suite')
120 self._mock_push = mock_push.start()
121 self.addCleanup(mock_push.stop)
122
Xinan Lin6668e0f2020-05-29 10:02:57 -0700123 def tearDown(self):
124 self.task.boards = ','.join([self.BOARD, self.UNIBUILD_BOARD])
125 self.task.models = None
126
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700127 def testSetSpecCompareInfoEqual(self):
128 """Test compare info setting for specs that equals to a milestone."""
129 self.task.branch_specs = re.split(r'\s*,\s*', '==tot-2')
130 self.task._set_spec_compare_info()
131 self.assertTrue(self.task._version_equal_constraint)
132 self.assertFalse(self.task._version_gte_constraint)
133 self.assertFalse(self.task._version_lte_constraint)
134 self.assertEqual(self.task._bare_branches, [])
135
136 def testSetSpecCompareInfoLess(self):
137 """Test compare info setting for specs that is less than a milestone."""
138 self.task.branch_specs = re.split(r'\s*,\s*', '<=tot')
139 self.task._set_spec_compare_info()
140 self.assertFalse(self.task._version_equal_constraint)
141 self.assertFalse(self.task._version_gte_constraint)
142 self.assertTrue(self.task._version_lte_constraint)
143 self.assertEqual(self.task._bare_branches, [])
144
145 def testSetSpecCompareInfoGreater(self):
146 """Test compare info setting for specs that is greater than a milestone."""
147 self.task.branch_specs = re.split(r'\s*,\s*', '>=tot-2')
148 self.task._set_spec_compare_info()
149 self.assertFalse(self.task._version_equal_constraint)
150 self.assertTrue(self.task._version_gte_constraint)
151 self.assertFalse(self.task._version_lte_constraint)
152 self.assertEqual(self.task._bare_branches, [])
153
154 def testFitsSpecEqual(self):
155 """Test milestone check for specs that equals to a milestone."""
156 self.task.branch_specs = re.split(r'\s*,\s*', '==tot-1')
157 self.task._set_spec_compare_info()
158 self.assertFalse(self.task._fits_spec('40'))
159 self.assertTrue(self.task._fits_spec('39'))
160 self.assertFalse(self.task._fits_spec('38'))
161
162 def testFitsSpecLess(self):
163 """Test milestone check for specs that is less than a milestone."""
164 self.task.branch_specs = re.split(r'\s*,\s*', '<=tot-1')
165 self.task._set_spec_compare_info()
166 self.assertFalse(self.task._fits_spec('40'))
167 self.assertTrue(self.task._fits_spec('39'))
168 self.assertTrue(self.task._fits_spec('38'))
169
170 def testFitsSpecGreater(self):
171 """Test milestone check for specs that is greater than a milestone."""
172 self.task.branch_specs = re.split(r'\s*,\s*', '>=tot-2')
173 self.task._set_spec_compare_info()
174 self.assertTrue(self.task._fits_spec('39'))
175 self.assertTrue(self.task._fits_spec('38'))
176 self.assertFalse(self.task._fits_spec('37'))
177
Xinan Lin028f9582019-12-11 10:55:33 -0800178 def testGetFirmwareSuccessfully(self):
179 """Test get firmware from firmware_dict successfully."""
Xinan Lin39dcca82019-07-26 18:55:51 -0700180
181 firmware = self.task._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800182 'cros', self.BOARD, self.firmware_dict, None)
Xinan Lin39dcca82019-07-26 18:55:51 -0700183 self.assertEqual(
184 firmware,
185 '{0}-release/R{1}-{2}'.format(self.BOARD,
186 self.MILESTONE,
187 self.PLATFORM))
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700188 firmware = self.task._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800189 'firmware', self.BOARD, self.firmware_dict, None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700190 self.assertEqual(
191 firmware,
Xinan Lin39dcca82019-07-26 18:55:51 -0700192 'firmware-{0}-12345.67.A-firmwarebranch/'
193 'RFoo-1.0.0-b1234567/{0}'.format(self.BOARD))
194
Xinan Lin028f9582019-12-11 10:55:33 -0800195 def testGetNonExistentBoard(self):
196 """Test get firmware for non-existent board from firmware_dict."""
197 self.assertIsNone(self.task._get_firmware_build(
198 'firmware', 'non-existent', self.firmware_dict, None))
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700199
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700200 def testScheduleCrosSuccessfully(self):
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700201 """test schedule cros builds successfully."""
202 branch_builds = {(self.BOARD, None, 'release', '56'): '0000.00.00'}
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700203 self.task._schedule_cros_builds(branch_builds,
Xinan Lin028f9582019-12-11 10:55:33 -0800204 self.firmware_dict,
205 self._fake_configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700206 self.assertEqual(self._mock_push.call_count, 1)
207
208 def testScheduleCrosNonvalidBoard(self):
209 """Test schedule no cros builds due to non-allowed board."""
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700210 branch_builds = {('%s_2' % self.BOARD, None, 'release', '56'): '0000.00.00'}
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700211 self.task._schedule_cros_builds(branch_builds,
Xinan Lin028f9582019-12-11 10:55:33 -0800212 self.firmware_dict,
213 self._empty_configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700214 self.assertEqual(self._mock_push.call_count, 0)
215
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700216 def testScheduleCrosExcludeBoard(self):
217 """Test schedule no cros builds due to board is excluded."""
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700218 branch_builds = {(self.EXCLUDE_BOARD, None, 'release', '56'): '0000.00.00'}
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700219 self.task._schedule_cros_builds(branch_builds,
Xinan Lin028f9582019-12-11 10:55:33 -0800220 self.firmware_dict,
221 self._empty_configs)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700222 self.assertEqual(self._mock_push.call_count, 0)
223
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700224 def testScheduleCrosSuccessfullyWithSpecifiedModel(self):
225 """Test schedule unibuild with specific model."""
226 self.task.board = self.UNIBUILD_BOARD
227 branch_builds = {(self.UNIBUILD_BOARD, 'robo', 'release', '56'):
228 '0000.00.00',
229 (self.UNIBUILD_BOARD, 'lava', 'release', '56'):
230 '0000.00.00'}
231 self.task._schedule_cros_builds(branch_builds,
Xinan Lin028f9582019-12-11 10:55:33 -0800232 self.firmware_dict,
233 self._fake_configs)
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700234 self.assertEqual(self._mock_push.call_count, 2)
235
Xinan Lin6668e0f2020-05-29 10:02:57 -0700236 def testScheduleCrosSuccessfullyForBoardWithSuffix(self):
237 """Test schedule board with suffix and specific model."""
238 branch_builds = {
239 ('hatch-kernelnext', None, 'release', '56'): '0000.00.00'
240 }
241 self.task.boards = 'hatch-kernelnext'
242 self.task.models = 'hatch_akemi'
243 self.task._schedule_cros_builds(branch_builds, self.firmware_dict,
244 self._fake_configs)
245 self.assertEqual(self._mock_push.call_count, 1)
246
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700247 def testScheduleCrosNonValidSpec(self):
248 """Test schedule no cros builds due to non-allowed branch milestone."""
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700249 branch_builds = {(self.BOARD, None, 'release', '56'): '0000.00.00'}
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700250 # Only run tasks whose milestone = tot (R40)
251 self.task.branch_specs = re.split(r'\s*,\s*', '==tot')
252 self.task._set_spec_compare_info()
253 self.task._schedule_cros_builds(branch_builds,
Xinan Lin028f9582019-12-11 10:55:33 -0800254 self.firmware_dict,
255 self._empty_configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700256 self.assertEqual(self._mock_push.call_count, 0)
257
258 def testScheduleCrosSuccessfullyWithValidFirmware(self):
259 """Test schedule cros builds successfully with valid firmware."""
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700260 branch_builds = {(self.BOARD, None, 'release', '56'): '0000.00.00'}
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700261 self.task.firmware_ro_build_spec = 'firmware'
262 self.task._schedule_cros_builds(branch_builds,
Xinan Lin028f9582019-12-11 10:55:33 -0800263 self.firmware_dict,
264 self._fake_configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700265 self.assertEqual(self._mock_push.call_count, 1)
266
267 def testScheduleCrosWithNonValidFirmware(self):
268 """Test schedule no cros builds due to non-existent firmware."""
Xinan Lin028f9582019-12-11 10:55:33 -0800269 branch_builds = {('foo-board', None, 'release', '56'): '0000.00.00'}
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700270 self.task.firmware_ro_build_spec = 'firmware'
271 self.task._schedule_cros_builds(branch_builds,
Xinan Lin028f9582019-12-11 10:55:33 -0800272 self.firmware_dict,
273 self._empty_configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700274 self.assertEqual(self._mock_push.call_count, 0)
275
276 def testScheduleLaunchControlWithFullBranches(self):
277 """Test schedule all launch control builds successfully."""
278 lc_builds = {self.BOARD: ['git_nyc-mr2-release/shamu-userdebug/3844975',
279 'git_nyc-mr1-release/shamu-userdebug/3783920']}
280 lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release'
281 self.task.launch_control_branches = [
282 t.lstrip() for t in lc_branches.split(',')]
283 self.task._schedule_launch_control_builds(lc_builds)
284 self.assertEqual(self._mock_push.call_count, 2)
285
286 def testScheduleLaunchControlWithPartlyBranches(self):
287 """Test schedule part of launch control builds due to branch check."""
288 lc_builds = {self.BOARD: ['git_nyc-mr2-release/shamu-userdebug/3844975',
289 'git_nyc-mr1-release/shamu-userdebug/3783920']}
290 lc_branches = 'git_nyc-mr1-release'
291 self.task.launch_control_branches = [
292 t.lstrip() for t in lc_branches.split(',')]
293 self.task._schedule_launch_control_builds(lc_builds)
294 self.assertEqual(self._mock_push.call_count, 1)
295
296 def testScheduleLaunchControlWithNoBranches(self):
297 """Test schedule none of launch control builds due to branch check."""
298 lc_builds = {self.BOARD: ['git_nyc-mr2-release/shamu-userdebug/3844975',
299 'git_nyc-mr1-release/shamu-userdebug/3783920']}
300 self.task.launch_control_branches = []
301 self.task._schedule_launch_control_builds(lc_builds)
302 self.assertEqual(self._mock_push.call_count, 0)
303
304 def testScheduleLaunchControlNonvalidBoard(self):
305 """Test schedule none of launch control builds due to board check."""
306 lc_builds = {'%s_2' % self.BOARD:
307 ['git_nyc-mr2-release/shamu-userdebug/3844975',
308 'git_nyc-mr1-release/shamu-userdebug/3783920']}
309 lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release'
310 self.task.launch_control_branches = [
311 t.lstrip() for t in lc_branches.split(',')]
312 self.task._schedule_launch_control_builds(lc_builds)
313 self.assertEqual(self._mock_push.call_count, 0)
Xixuan Wu5451a662017-10-17 10:57:40 -0700314
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700315 def testScheduleLaunchControlExcludeBoard(self):
316 """Test schedule none of launch control builds due to board check."""
317 lc_builds = {self.EXCLUDE_BOARD:
318 ['git_nyc-mr2-release/shamu-userdebug/3844975',
319 'git_nyc-mr1-release/shamu-userdebug/3783920']}
320 lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release'
321 self.task.launch_control_branches = [
322 t.lstrip() for t in lc_branches.split(',')]
323 self.task._schedule_launch_control_builds(lc_builds)
324 self.assertEqual(self._mock_push.call_count, 0)
325
Xixuan Wu5451a662017-10-17 10:57:40 -0700326
327class TaskPushTestCase(TaskBaseTestCase):
328
329 def setUp(self):
330 super(TaskPushTestCase, self).setUp()
331 mock_push = mock.patch('task_executor.push')
332 self._mock_push = mock_push.start()
333 self.addCleanup(mock_push.stop)
334
335 def testScheduleCrOSIsPushSuccessfully(self):
336 """Test IS_PUSHED is changed if some CrOS suites are scheduled."""
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700337 branch_builds = {(self.BOARD, None, 'release', '56'): '0000.00.00'}
Xixuan Wu5451a662017-10-17 10:57:40 -0700338 self.task.os_type = build_lib.OS_TYPE_CROS
339 self.assertTrue(self.task.schedule(
Xinan Lin028f9582019-12-11 10:55:33 -0800340 [], (branch_builds, []), self.firmware_dict, self._fake_configs))
Xixuan Wu5451a662017-10-17 10:57:40 -0700341
342 def testScheduleLaunchControlIsPushSuccessfully(self):
343 """Test IS_PUSHED is changed if some launch control suites are scheduled."""
344 lc_builds = {self.BOARD: ['git_nyc-mr2-release/shamu-userdebug/3844975',
345 'git_nyc-mr1-release/shamu-userdebug/3783920']}
346 lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release'
347 self.task.launch_control_branches = [
348 t.lstrip() for t in lc_branches.split(',')]
349 self.task.os_type = build_lib.OS_TYPES_LAUNCH_CONTROL
350 self.assertTrue(self.task.schedule(
Xinan Lin028f9582019-12-11 10:55:33 -0800351 lc_builds, ([], []), self.firmware_dict,
352 self._empty_configs))
Xixuan Wu5451a662017-10-17 10:57:40 -0700353
354 def testScheduleCrosIsPushInvalidBoard(self):
355 """Test schedule no cros builds due to non-allowed board."""
Craig Bergstrom58263d32018-04-26 14:11:35 -0600356 branch_builds = (
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700357 {('%s_2' % self.BOARD, None, 'release', '56'): '0000.00.00'}, {},
Craig Bergstrom58263d32018-04-26 14:11:35 -0600358 )
Xixuan Wu5451a662017-10-17 10:57:40 -0700359 self.task.os_type = build_lib.OS_TYPE_CROS
360 self.assertFalse(self.task.schedule(
Xinan Lin028f9582019-12-11 10:55:33 -0800361 [], branch_builds, self.firmware_dict, self._empty_configs))
Xixuan Wu5451a662017-10-17 10:57:40 -0700362
363 def testScheduleLaunchControlIsPushInvalidBoard(self):
364 """Test schedule none of launch control builds due to board check."""
365 lc_builds = {'%s_2' % self.BOARD:
366 ['git_nyc-mr2-release/shamu-userdebug/3844975',
367 'git_nyc-mr1-release/shamu-userdebug/3783920']}
368 lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release'
369 self.task.launch_control_branches = [
370 t.lstrip() for t in lc_branches.split(',')]
371 self.task.os_type = build_lib.OS_TYPES_LAUNCH_CONTROL
372 self.task._schedule_launch_control_builds(lc_builds)
373 self.assertFalse(self.task.schedule(
Xinan Lin028f9582019-12-11 10:55:33 -0800374 lc_builds, ([], []), self.firmware_dict,
375 self._empty_configs))
Xinan Lindf0698a2020-02-05 22:38:11 -0800376
377
378class TaskJobSectionTestCase(TaskBaseTestCase):
379
380 def setUp(self):
381 super(TaskJobSectionTestCase, self).setUp()
382 mock_push = mock.patch('task_executor.push')
383 self._mock_push = mock_push.start()
384 self.addCleanup(mock_push.stop)
385
386 def testJobSectionName(self):
387 """Test the job section name if recorded."""
388 self.task.schedule(
389 [], ([], []), self.firmware_dict, self._fake_configs)
390 self.assertEqual(self._mock_push.call_count, 0)
391 job_sec = json_format.Parse(
392 json.dumps(self.mock_bq_client.rows[0]['json']),
393 analytics_pb2.ScheduleJobSection())
394 self.assertEqual(job_sec.job_name, 'fake_name')
395
396 def testAddBoard(self):
397 """Test the configured board is recorded."""
398 new_task = task.Task(task_config_reader.TaskInfo(
399 name=self.NAME,
400 suite=self.SUITE,
401 branch_specs=[],
402 boards='coral'))
403 new_task.schedule(
404 [], ([], []), self.firmware_dict, self._fake_configs)
405 job_sec = json_format.Parse(
406 json.dumps(self.mock_bq_client.rows[0]['json']),
407 analytics_pb2.ScheduleJobSection())
408 self.assertEqual(len(job_sec.build_targets), 1)
409 self.assertEqual(job_sec.build_targets[0].name, 'coral')
410
411 def testAddModel(self):
412 """Test the configured model is recorded."""
413 new_task = task.Task(task_config_reader.TaskInfo(
414 name=self.NAME,
415 suite=self.SUITE,
416 branch_specs=[],
417 boards='coral',
418 exclude_models='coral_robo,coral_nasher'))
419 new_task.schedule(
420 [], ([], []), self.firmware_dict, self._fake_configs)
421 job_sec = json_format.Parse(
422 json.dumps(self.mock_bq_client.rows[0]['json']),
423 analytics_pb2.ScheduleJobSection())
424 # lava is the only coral model that was not excluded.
425 self.assertEqual(len(job_sec.models), 1)
426 self.assertEqual(job_sec.models[0].value, 'lava')
427
428 def testAddMatchedBuild(self):
429 """Test the matched build is recorded."""
430 branch_builds = {('coral', '', 'release', '40'):
431 '0000.00.00',
432 ('hatch', '', 'release', '37'):
433 '0000.00.00'}
434 new_task = task.Task(task_config_reader.TaskInfo(
435 name=self.NAME,
436 suite=self.SUITE,
437 branch_specs=['>=tot-2'],
438 boards='coral,hatch',
439 os_type=build_lib.OS_TYPE_CROS))
440 new_task.schedule(
441 [], (branch_builds, []), self.firmware_dict, self._fake_configs)
442 job_sec = json_format.Parse(
443 json.dumps(self.mock_bq_client.rows[0]['json']),
444 analytics_pb2.ScheduleJobSection())
445 self.assertEqual(len(job_sec.matched_builds), 1)
446 build = job_sec.matched_builds[0].release_build
447 # hatch should not be recorded as its build is too old.
448 self.assertEqual(build.build_target.name, 'coral')
449 self.assertEqual(build.milestone, 40)
450 self.assertEqual(build.chrome_os_version, '0000.00.00')
451 self.assertEqual(build.type, branch_pb2.Branch.RELEASE)
452
453 def testAddMatchedRelaxBuild(self):
454 """Test the matched relax build is recorded."""
455 relax_builds = {('coral', '', 'release', '40'):
456 '0000.00.00',
457 ('hatch', '', 'release', '37'):
458 '0000.00.00'}
459 new_task = task.Task(task_config_reader.TaskInfo(
460 name=self.NAME,
461 suite=self.SUITE,
462 branch_specs=['>=tot-2'],
463 boards='coral,hatch',
464 only_hwtest_sanity_required=True,
465 os_type=build_lib.OS_TYPE_CROS))
466 new_task.schedule(
467 [], ([], relax_builds), self.firmware_dict, self._fake_configs)
468 job_sec = json_format.Parse(
469 json.dumps(self.mock_bq_client.rows[0]['json']),
470 analytics_pb2.ScheduleJobSection())
471 self.assertEqual(len(job_sec.matched_builds), 1)
472 build = job_sec.matched_builds[0].relax_build
473 # hatch should not be recorded as its build is too old.
474 self.assertEqual(build.build_target.name, 'coral')
475 self.assertEqual(build.milestone, 40)
476 self.assertEqual(build.chrome_os_version, '0000.00.00')
477 self.assertEqual(build.type, branch_pb2.Branch.RELEASE)
478
479 def testAddMatchedFirmwareBuild(self):
480 """Test the matched firmware build is recorded."""
481 branch_builds = {('fake_board', '', 'release', '40'):
482 '0000.00.00'}
483 new_task = task.Task(task_config_reader.TaskInfo(
484 name=self.NAME,
485 suite=self.SUITE,
486 branch_specs=['>=tot-2'],
487 boards='fake_board',
488 firmware_rw_build_spec='firmware',
489 os_type=build_lib.OS_TYPE_CROS))
490 new_task.schedule(
491 [], (branch_builds, []), self.firmware_dict, self._fake_configs)
492 job_sec = json_format.Parse(
493 json.dumps(self.mock_bq_client.rows[0]['json']),
494 analytics_pb2.ScheduleJobSection())
495 self.assertEqual(len(job_sec.matched_builds), 2)
496 build = job_sec.matched_builds[1].firmware_rw_build
497 self.assertEqual(build.build_target.name, 'fake_board')
498 self.assertEqual(build.type, branch_pb2.Branch.FIRMWARE)
499 self.assertEqual(build.artifact.path,
500 self.firmware_dict['firmware', 'fake_board'])
501
502 def testScheduleJobSuccessfully(self):
503 """Test the scheduled job is recorded."""
504 branch_builds = {('coral', 'lava', 'release', '40'):
505 '0000.00.00'}
506 new_task = task.Task(task_config_reader.TaskInfo(
507 name=self.NAME,
508 suite=self.SUITE,
509 branch_specs=['>=tot-2'],
510 boards='coral',
511 os_type=build_lib.OS_TYPE_CROS))
512 with mock.patch('uuid.uuid1', return_value=FAKE_UUID):
513 new_task.schedule(
514 [], (branch_builds, []), self.firmware_dict, self._fake_configs)
515 job_sec = json_format.Parse(
516 json.dumps(self.mock_bq_client.rows[0]['json']),
517 analytics_pb2.ScheduleJobSection())
518 self.assertEqual(len(job_sec.schedule_jobs), 1)
519 job = job_sec.schedule_jobs[0]
520 self.assertEqual(job.build_target.name, 'coral')
521 self.assertEqual(job.model.value, 'lava')
522 self.assertEqual(job.queued_task_id, FAKE_UUID)
523
524 def testScheduleJobFailedforFirmwareRO(self):
525 """Test the dropped job due to firmware absence is recorded."""
526 branch_builds = {('coral', 'lava', 'release', '40'):
527 '0000.00.00'}
528 new_task = task.Task(task_config_reader.TaskInfo(
529 name=self.NAME,
530 suite=self.SUITE,
531 branch_specs=['>=tot-2'],
532 boards='coral',
533 firmware_ro_build_spec='firmware',
Brigit Rossbachbb080912020-11-18 13:52:17 -0700534 firmware_ro_version=None,
Xinan Lindf0698a2020-02-05 22:38:11 -0800535 os_type=build_lib.OS_TYPE_CROS))
536 new_task.schedule(
537 [], (branch_builds, []), {}, self._fake_configs)
538 job_sec = json_format.Parse(
539 json.dumps(self.mock_bq_client.rows[0]['json']),
540 analytics_pb2.ScheduleJobSection())
541 self.assertEqual(len(job_sec.schedule_jobs), 1)
542 job = job_sec.schedule_jobs[0]
543 self.assertEqual(job.build_target.name, 'coral')
544 self.assertEqual(job.model.value, 'lava')
Sean McAllisterb9289cd2021-06-04 09:40:29 -0600545 self.assertIn('No RO firmware build to run', job.justification)
546
547 def testScheduleJobFailedforFirmwareRW(self):
548 """Test the dropped job due to firmware absence is recorded."""
549 branch_builds = {('coral', 'lava', 'release', '40'):
550 '0000.00.00'}
551 new_task = task.Task(task_config_reader.TaskInfo(
552 name=self.NAME,
553 suite=self.SUITE,
554 branch_specs=['>=tot-2'],
555 boards='coral',
556 firmware_rw_build_spec='firmware',
557 firmware_rw_version=None,
558 os_type=build_lib.OS_TYPE_CROS))
559 new_task.schedule(
560 [], (branch_builds, []), {}, self._fake_configs)
561 job_sec = json_format.Parse(
562 json.dumps(self.mock_bq_client.rows[0]['json']),
563 analytics_pb2.ScheduleJobSection())
564 self.assertEqual(len(job_sec.schedule_jobs), 1)
565 job = job_sec.schedule_jobs[0]
566 self.assertEqual(job.build_target.name, 'coral')
567 self.assertEqual(job.model.value, 'lava')
568 self.assertIn('No RW firmware build to run', job.justification)
Xinan Lindf0698a2020-02-05 22:38:11 -0800569
570 def testScheduleJobFailedforBranchNotMaching(self):
571 """Test the dropped job due to branch spec is recorded."""
572 branch_builds = {('coral', 'lava', 'release', '38'):
573 '0000.00.00'}
574 new_task = task.Task(task_config_reader.TaskInfo(
575 name=self.NAME,
576 suite=self.SUITE,
577 branch_specs=['>=tot-1'],
578 boards='coral',
579 os_type=build_lib.OS_TYPE_CROS))
580 new_task.schedule(
581 [], (branch_builds, []), {}, self._fake_configs)
582 job_sec = json_format.Parse(
583 json.dumps(self.mock_bq_client.rows[0]['json']),
584 analytics_pb2.ScheduleJobSection())
585 self.assertEqual(len(job_sec.schedule_jobs), 1)
586 job = job_sec.schedule_jobs[0]
587 self.assertEqual(job.build_target.name, 'coral')
588 self.assertEqual(job.model.value, 'lava')
589 self.assertIn('branch_build spec', job.justification)
590
591 def testScheduleJobFailedforAnyModel(self):
592 """Test the dropped job due to any_model is recorded."""
593 branch_builds = {('coral', None, 'release', '38'):
594 '0000.00.00'}
595 new_task = task.Task(task_config_reader.TaskInfo(
596 name=self.NAME,
597 suite=self.SUITE,
598 branch_specs=['>=tot-2'],
599 boards='coral',
600 any_model=True,
601 exclude_models='coral_nasher',
602 os_type=build_lib.OS_TYPE_CROS))
603 new_task.schedule(
604 [], (branch_builds, []), {}, self._fake_configs)
605 job_sec = json_format.Parse(
606 json.dumps(self.mock_bq_client.rows[0]['json']),
607 analytics_pb2.ScheduleJobSection())
608 jobs = job_sec.schedule_jobs
Xinan Lin0673a182020-04-14 15:09:35 -0700609 # 'nasher' is excluded, hence there should be two jobs for the rest models
610 # 'robo', 'lava'.
611 self.assertEqual(2, len(jobs))
612 # The first job should be scheduled with empty model, as any_model is True.
613 self.assertEqual(jobs[0].build_target.name, 'coral')
614 self.assertEqual(jobs[0].model.value, '')
615 # The second job should be skipped and the justification is recorded.
Xinan Lindf0698a2020-02-05 22:38:11 -0800616 self.assertEqual(jobs[1].build_target.name, 'coral')
617 self.assertEqual(jobs[1].model.value, 'lava')
618 self.assertIn('Skip model lava as any_model enabled', jobs[1].justification)
Xinan Lin6668e0f2020-05-29 10:02:57 -0700619
620 def testScheduleJobForBoardWithSuffix(self):
621 """Test record a schedule job with board plus suffix and model specified."""
622 branch_builds = {('hatch-kernelnext', None, 'release', '38'): '0000.00.00'}
623 new_task = task.Task(
624 task_config_reader.TaskInfo(
625 name=self.NAME,
626 suite=self.SUITE,
627 branch_specs=['>=tot-2'],
628 boards='hatch-kernelnext',
629 models='hatch_akemi',
630 os_type=build_lib.OS_TYPE_CROS))
631 new_task.schedule([], (branch_builds, []), {}, self._fake_configs)
632 job_sec = json_format.Parse(
633 json.dumps(self.mock_bq_client.rows[0]['json']),
634 analytics_pb2.ScheduleJobSection())
635 jobs = job_sec.schedule_jobs
636 # Only the specified model should appear in the scheduled jobs.
637 self.assertEqual(1, len(jobs))
638 self.assertEqual(jobs[0].build_target.name, 'hatch-kernelnext')
639 self.assertEqual(jobs[0].model.value, 'akemi')
Garry Wangdce77572021-07-18 19:33:35 -0700640
641
642class TaskBaseMultiDUTsTestCase(unittest.TestCase):
643
644 NAME = 'fake_name'
645 ANALYTICS_NAME='fake_name'
646 SUITE = 'fake_suite'
647 POOL = 'fake_pool'
648 PLATFORM = '6182.0.0-rc2'
649 MILESTONE = '30'
650 NUM = 1
651 PRIORITY = 50
652 TIMEOUT = 600
653 BUILD_DICT = {
654 ('coral', 'robo', 'release', '55'): '0000.00.00',
655 ('coral', 'robo', 'release', '56'): '0000.00.00',
656 ('coral', 'nasher', 'release', '55'): '0000.00.00',
657 ('coral', 'nasher', 'release', '56'): '0000.00.00',
658 ('coral', 'lava', 'release', '55'): '0000.00.00',
659 ('coral', 'lava', 'release', '56'): '0000.00.00',
660 ('hatch', 'akemi', 'release', '55'): '0000.00.00',
661 ('hatch', 'akemi', 'release', '56'): '0000.00.00',
662 ('hatch', 'kled', 'release', '55'): '0000.00.00',
663 ('hatch', 'kled', 'release', '56'): '0000.00.00',
664 }
Garry Wang4e29b512021-08-26 19:32:59 -0700665 RECENT_BUILD_DICT = {
Garry Wangdce77572021-07-18 19:33:35 -0700666 ('coral', None, 'release', '55'): '0000.00.00',
667 ('coral', None, 'release', '56'): '0000.00.00',
668 ('hatch', None, 'release', '55'): '0000.00.00',
669 ('hatch', None, 'release', '56'): '0000.00.00',
670 }
671
672 def setUp(self, multi_dut_boards=None, multi_dut_models=None):
673 tot_patcher = mock.patch('tot_manager.TotMilestoneManager')
674 self._mock_tot = tot_patcher.start()
675 self.addCleanup(tot_patcher.stop)
676 # Can't pass in False since storage_client is not mocked.
677 self._mock_tot.return_value = FakeTotMilestoneManager(True)
678
679 _mock_bq_client = mock.patch('rest_client.BigqueryRestClient')
680 mock_bq_client = _mock_bq_client.start()
681 self.addCleanup(_mock_bq_client.stop)
682 self.mock_bq_client = FakeBigqueryRestClient(
683 None, project='proj', dataset='dataset', table='foo')
684 mock_bq_client.return_value = self.mock_bq_client
685
686 self.task = task.Task(
687 task_config_reader.TaskInfo(
688 name=self.NAME,
689 suite=self.SUITE,
690 branch_specs=[],
691 analytics_name=self.ANALYTICS_NAME,
692 pool=self.POOL,
693 num=self.NUM,
694 multi_dut_boards=multi_dut_boards,
695 multi_dut_models=multi_dut_models,
696 priority=self.PRIORITY,
697 timeout=self.TIMEOUT))
698
699 self._empty_configs = config_reader.Configs(
700 lab_config=config_reader.LabConfig(None))
701 self._fake_configs = config_reader.Configs(
702 lab_config=FakeLabConfig())
703
704
705class TaskMultiDUTsByBoardTestCase(TaskBaseMultiDUTsTestCase):
706
707 def setUp(self):
708 super(TaskMultiDUTsByBoardTestCase, self).setUp(
709 multi_dut_boards="coral;hatch, hatch;coral")
710
711 mock_push = mock.patch('task.Task._push_suite')
712 self._mock_push = mock_push.start()
713 self.addCleanup(mock_push.stop)
714
715 def testMultiDUTsTaskAttributes(self):
716 """Test correct multi-duts related attributes is set when
717 multi_dut_boards presents in configs.
718 """
719 self.assertEqual(self.task.is_multi_dut_testing, True)
720 self.assertEqual(self.task.multi_dut_boards,
721 [['coral', 'hatch'], ['hatch', 'coral']])
722
723 def testGetMultiDUTsBuildTargetsDictWithAnyModel(self):
724 """Test the correct build targets dict can be generated with
725 any_model set to True.
726 """
727 self.task.any_model = True
728 d = self.task._get_multi_duts_build_targets_dict(self._fake_configs)
729 expected_dict = {
730 'coral': [
731 [
732 task.BuildTarget('coral', None, None),
733 task.BuildTarget('hatch', None, None),
734 ],
735 ],
736 'hatch': [
737 [
738 task.BuildTarget('hatch', None, None),
739 task.BuildTarget('coral', None, None),
740 ],
741 ]
742 }
743 self.assertEqual(d, expected_dict)
744
745 def testGetMultiDUTsBuildTargetsDictWithoutAnyModel(self):
746 """Test the correct build targets dict can be generated with
747 any_model set to False.
748 """
749 self.task.any_model = False
750 d = self.task._get_multi_duts_build_targets_dict(self._fake_configs)
751 expected_dict = {
752 'coral': [
753 [
754 task.BuildTarget('coral', 'robo', None),
755 task.BuildTarget('hatch', None, None),
756 ],
757 [
758 task.BuildTarget('coral', 'nasher', None),
759 task.BuildTarget('hatch', None, None),
760 ],
761 [
762 task.BuildTarget('coral', 'lava', None),
763 task.BuildTarget('hatch', None, None),
764 ],
765 ],
766 'hatch': [
767 [
768 task.BuildTarget('hatch', 'akemi', None),
769 task.BuildTarget('coral', None, None),
770 ],
771 [
772 task.BuildTarget('hatch', 'kled', None),
773 task.BuildTarget('coral', None, None),
774 ],
775 ]
776 }
777 self.assertEqual(d, expected_dict)
778
779 def testScheduleMultiDUTsCrosWithAnyModel(self):
780 """Test schedule multi DUTs cros build with any_model set to True."""
781 self.task.any_model = True
782 self.task._schedule_multi_duts_cros_builds(
Garry Wang4e29b512021-08-26 19:32:59 -0700783 self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs)
Garry Wangdce77572021-07-18 19:33:35 -0700784 self.assertEqual(self._mock_push.call_count, 4)
785
786 def testScheduleMultiDUTsCrosWithoutAnyModel(self):
787 """Test schedule multi DUTs cros build with any_model set to False."""
788 self.task.any_model = False
789 self.task._schedule_multi_duts_cros_builds(
Garry Wang4e29b512021-08-26 19:32:59 -0700790 self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs)
Garry Wangdce77572021-07-18 19:33:35 -0700791 self.assertEqual(self._mock_push.call_count, 10)
792
793 def testScheduleMultiDUTsWithMissingSecondaryBuild(self):
794 """Test schedule multi DUTs cros build when missing secondary build
Garry Wang4e29b512021-08-26 19:32:59 -0700795 info in recent dict, the expectation is no suite will be pushed.
Garry Wangdce77572021-07-18 19:33:35 -0700796 """
797 self.task._schedule_multi_duts_cros_builds(
798 self.BUILD_DICT, {}, self._fake_configs)
799 assert not self._mock_push.called
800
801
802class TaskMultiDUTsByModelTestCase(TaskBaseMultiDUTsTestCase):
803
804 def setUp(self):
805 super(TaskMultiDUTsByModelTestCase, self).setUp(
806 multi_dut_models='robo;akemi, kled;lava')
807
808 mock_push = mock.patch('task.Task._push_suite')
809 self._mock_push = mock_push.start()
810 self.addCleanup(mock_push.stop)
811
812 def testMultiDUTsTaskAttributes(self):
813 """Test correct multi-duts related attributes is set when
814 multi_dut_models presents in configs.
815 """
816 self.assertEqual(self.task.is_multi_dut_testing, True)
817 self.assertEqual(self.task.multi_dut_models,
818 [['robo', 'akemi'], ['kled', 'lava']])
819
820 def testGetMultiDUTsBuildTargetsDict(self):
821 """Test the correct build targets dict can be generated."""
822 d = self.task._get_multi_duts_build_targets_dict(self._fake_configs)
823 expected_dict = {
824 'coral': [
825 [
826 task.BuildTarget('coral', 'robo', None),
827 task.BuildTarget('hatch', 'akemi', None),
828 ],
829 ],
830 'hatch': [
831 [
832 task.BuildTarget('hatch', 'kled', None),
833 task.BuildTarget('coral', 'lava', None),
834 ],
835 ]
836 }
837 self.assertEqual(d, expected_dict)
838
839 def testScheduleMultiDUTsCrosWithoutAnyModel(self):
840 """Test schedule multi DUTs cros build."""
841 self.task._schedule_multi_duts_cros_builds(
Garry Wang4e29b512021-08-26 19:32:59 -0700842 self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs)
Garry Wangdce77572021-07-18 19:33:35 -0700843 self.assertEqual(self._mock_push.call_count, 4)
Garry Wang6ca42dd2022-02-28 21:54:19 -0800844
845
846class TaskMultiDUTsWithAndroidByBoardTestCase(TaskBaseMultiDUTsTestCase):
847
848 def setUp(self):
849 super(TaskMultiDUTsWithAndroidByBoardTestCase, self).setUp(
850 multi_dut_boards='coral;pixel3, hatch;pixel4')
851
852 mock_push = mock.patch('task.Task._push_suite')
853 self._mock_push = mock_push.start()
854 self.addCleanup(mock_push.stop)
855
856 def testGetMultiDUTsBuildTargetsDictWithAndroidByBoard(self):
857 """Test the correct build targets dict can be generated with
858 Android devices when specified by board.
859 """
860 self.task.any_model = True
861 d = self.task._get_multi_duts_build_targets_dict(self._fake_configs)
862 expected_dict = {
863 'coral': [
864 [
865 task.BuildTarget('coral', None, None),
866 task.BuildTarget('pixel3', None, None),
867 ],
868 ],
869 'hatch': [
870 [
871 task.BuildTarget('hatch', None, None),
872 task.BuildTarget('pixel4', None, None),
873 ],
874 ]
875 }
876 self.assertEqual(d, expected_dict)
877
878 def testScheduleMultiDUTsWithAndroidByBoard(self):
879 """Test schedule multi DUTs cros build with Android by board."""
880 self.task.any_model = True
881 self.task._schedule_multi_duts_cros_builds(
882 self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs)
883 self.assertEqual(self._mock_push.call_count, 4)
884
885
886class TaskMultiDUTsWithAndroidByModelTestCase(TaskBaseMultiDUTsTestCase):
887
888 def setUp(self):
889 super(TaskMultiDUTsWithAndroidByModelTestCase, self).setUp(
890 multi_dut_models='robo;pixel3xl, kled;pixel4a')
891
892 mock_push = mock.patch('task.Task._push_suite')
893 self._mock_push = mock_push.start()
894 self.addCleanup(mock_push.stop)
895
896 def testGetMultiDUTsBuildTargetsDictWithAndroidByModel(self):
897 """Test the correct build targets dict can be generated with
898 Android devices when specified by model.
899 """
900 d = self.task._get_multi_duts_build_targets_dict(self._fake_configs)
901 expected_dict = {
902 'coral': [
903 [
904 task.BuildTarget('coral', 'robo', None),
905 task.BuildTarget('pixel3', 'pixel3xl', None),
906 ],
907 ],
908 'hatch': [
909 [
910 task.BuildTarget('hatch', 'kled', None),
911 task.BuildTarget('pixel4', 'pixel4a', None),
912 ],
913 ]
914 }
915 self.assertEqual(d, expected_dict)
916
917 def testScheduleMultiDUTsWithAndroidByModel(self):
918 """Test schedule multi DUTs cros build with Android by model."""
919 self.task.any_model = True
920 self.task._schedule_multi_duts_cros_builds(
921 self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs)
922 self.assertEqual(self._mock_push.call_count, 4)