blob: c6b6f3d3df68cae18049b6bb34ced07d71d854a7 [file] [log] [blame]
Aviv Keshetc679faf2019-11-27 17:52:50 -08001#!/usr/bin/env python2
Xinan Lin3ba18a02019-08-13 15:44:55 -07002# Copyright 2019 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
6"""Module for buildbucket unittests."""
7
Xinan Lindf0698a2020-02-05 22:38:11 -08008import json
Xinan Lin3ba18a02019-08-13 15:44:55 -07009import mock
10import os
11import unittest
12
Xinan Lin9e4917d2019-11-04 10:58:47 -080013import constants
Xinan Lin3ba18a02019-08-13 15:44:55 -070014import build_lib
15import buildbucket
16import task_executor
17
18from chromite.api.gen.test_platform import request_pb2
Xinan Lindf0698a2020-02-05 22:38:11 -080019from chromite.api.gen.test_platform.suite_scheduler import analytics_pb2
Xinan Lin9e4917d2019-11-04 10:58:47 -080020from google.appengine.api import taskqueue
Xinan Lin3ba18a02019-08-13 15:44:55 -070021from google.appengine.ext import testbed
22from google.protobuf import json_format
Xinan Lindf0698a2020-02-05 22:38:11 -080023from infra_libs.buildbucket.proto import build_pb2
Xinan Lin3ba18a02019-08-13 15:44:55 -070024
25
26ADDR = u'http://localhost:1'
Xinan Lindf0698a2020-02-05 22:38:11 -080027FAKE_UUID = 'c78e0bf3-4142-11ea-bc66-88e9fe4c5349'
28FAKE_BUILD_ID = 8890493019851395280
Xinan Lin3ba18a02019-08-13 15:44:55 -070029
30
Xinan Lin9e4917d2019-11-04 10:58:47 -080031def _get_suite_params(board='fake_board',
32 model='fake_model',
33 suite='fake_suite',
34 pool='DUT_POOL_SUITES',
35 cros_build='fake_cros_build'):
Xinan Lin3ba18a02019-08-13 15:44:55 -070036 return {
Prathmesh Prabhu2382a182019-09-07 21:18:10 -070037 'suite': suite,
Xinan Lindf0698a2020-02-05 22:38:11 -080038 'task_id': FAKE_UUID,
Prathmesh Prabhu2382a182019-09-07 21:18:10 -070039 'board': board,
40 'model': model,
41 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
Xinan Lin3ba18a02019-08-13 15:44:55 -070042 build_lib.BuildVersionKey.FW_RW_VERSION: 'fake_firmware_rw_build',
43 build_lib.BuildVersionKey.FW_RO_VERSION: 'fake_firmware_ro_build',
44 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: 'fake_android_build',
45 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: 'fake_testbed_build',
46 'num': 1,
Prathmesh Prabhu2382a182019-09-07 21:18:10 -070047 'pool': pool,
Xinan Lin3ba18a02019-08-13 15:44:55 -070048 'priority': 10,
Xinan Lin6e097382019-08-27 18:43:35 -070049 'timeout': 12,
50 'timeout_mins': 4320,
51 'max_runtime_mins': 4320,
Xinan Lin3ba18a02019-08-13 15:44:55 -070052 'no_wait_for_results': True,
53 'test_source_build': 'fake_test_source_build',
54 'job_retry': False,
55 'no_delay': False,
56 'force': False,
57 'run_prod_code': True,
58 'is_skylab': False,
59 }
60
61
62class FakeTestPlatformClient(object):
63
64 def __init__(self, test):
65 self._test = test
66 self.called_with_requests = []
Xinan Lindf0698a2020-02-05 22:38:11 -080067 self.error = None
Xinan Lin3ba18a02019-08-13 15:44:55 -070068
69 def ScheduleBuild(self, req, credentials=None):
70 self.called_with_requests.append(req)
Xinan Lindf0698a2020-02-05 22:38:11 -080071 if self.error:
72 return self.error
73 return build_pb2.Build(id=FAKE_BUILD_ID)
74
75
76class FakeBigqueryRestClient(object):
77
78 def __init__(self, rest_client, project=None, dataset=None, table=None):
79 """Initialize the mock class."""
80 self.table = table
81 self.rows = []
82
83 def insert(self, rows):
84 self.rows = rows
85 return True
Xinan Lin3ba18a02019-08-13 15:44:55 -070086
87
88class TestPlatformClientTestCase(unittest.TestCase):
89
90 def setUp(self):
91 super(TestPlatformClientTestCase, self).setUp()
92 self.fake_client = FakeTestPlatformClient(self)
93 patcher = mock.patch('buildbucket._get_client',
94 return_value=self._get_client(ADDR))
95 patcher.start()
96 self.client = buildbucket.TestPlatformClient(ADDR,
97 'foo-proj',
98 'foo-bucket',
99 'foo-builder')
100 self.addCleanup(patcher.stop)
101 self.testbed = testbed.Testbed()
102 self.testbed.activate()
103 self.addCleanup(self.testbed.deactivate)
104 self.testbed.init_taskqueue_stub(
105 root_path=os.path.join(os.path.dirname(__file__)))
106 self.taskqueue_stub = self.testbed.get_stub(
107 testbed.TASKQUEUE_SERVICE_NAME)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800108 self.queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700109
Xinan Lin9e4917d2019-11-04 10:58:47 -0800110 def testFormTestPlatformMultiRequestSuccessfully(self):
111 suite_kwargs = [_get_suite_params(board=b)
112 for b in ['foo', 'goo']]
113 for suite in suite_kwargs:
114 task_executor.push(task_executor.SUITES_QUEUE,
115 tag='fake_suite', **suite)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700116
Xinan Lin9e4917d2019-11-04 10:58:47 -0800117 tasks = self.queue.lease_tasks_by_tag(3600,
118 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
119
120 self.client.multirequest_run(tasks, 'fake_suite')
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700121 self._assert_bb_client_called()
Xinan Lin9e4917d2019-11-04 10:58:47 -0800122
123 request_map = self._extract_request_map_from_bb_client()
124 request_list = [_struct_pb2_to_request_pb2(v)
125 for v in request_map.values()]
126 request_list.sort(
127 key=lambda req: req.params.software_attributes.build_target.name)
128 for i, req in enumerate(request_list):
129 self.assertEqual(req.params.scheduling.priority, 10)
130 self.assertEqual(req.params.scheduling.managed_pool,
131 request_pb2.Request.Params.Scheduling.MANAGED_POOL_SUITES)
132 self.assertEqual(req.params.time.maximum_duration.seconds, 165600)
Aviv Keshetc679faf2019-11-27 17:52:50 -0800133 gs_url = ('gs://chromeos-image-archive/%s' %
134 suite_kwargs[i]['test_source_build'])
135 self.assertEqual(req.params.metadata.test_metadata_url, gs_url)
136 self.assertEqual(req.params.metadata.debug_symbols_archive_url, gs_url)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800137 self.assertEqual(req.test_plan.suite[0].name, suite_kwargs[i]['suite'])
138 self.assertEqual(req.params.software_attributes.build_target.name,
139 suite_kwargs[i]['board'])
140
141 def testPoolName(self):
142 suite_kwargs = [_get_suite_params(board=b)
143 for b in ['foo', 'goo', 'hoo']]
144 suite_kwargs[0]['pool'] = 'cts'
145 suite_kwargs[1]['pool'] = 'wifi'
146 suite_kwargs[2]['override_qs_account'] = 'dummy@foo.com'
147 for suite in suite_kwargs:
148 task_executor.push(task_executor.SUITES_QUEUE,
149 tag='some_suite', **suite)
150
151 tasks = self.queue.lease_tasks_by_tag(3600,
152 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
153 self.client.multirequest_run(tasks, 'some_suite')
154 self._assert_bb_client_called()
155 request_map = self._extract_request_map_from_bb_client()
156 request_list = [_struct_pb2_to_request_pb2(v)
157 for v in request_map.values()]
158 self.assertEqual(len(request_list), 3)
159 request_list.sort(
160 key=lambda req: req.params.software_attributes.build_target.name)
161 self.assertEqual(request_list[0].params.scheduling.managed_pool,
Alex Zamorzaevc1935602019-08-28 14:37:35 -0700162 request_pb2.Request.Params.Scheduling.MANAGED_POOL_CTS)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800163 self.assertEqual(request_list[1].params.scheduling.unmanaged_pool,
164 suite_kwargs[1]['pool'])
165 self.assertEqual(request_list[2].params.scheduling.quota_account,
166 suite_kwargs[2]['override_qs_account'])
Prathmesh Prabhu88409f62019-08-30 14:32:28 -0700167
Xinan Lin3ba18a02019-08-13 15:44:55 -0700168 def testNoFinalBuild(self):
Xinan Lin9e4917d2019-11-04 10:58:47 -0800169 suite_kwargs = _get_suite_params()
170 suite_kwargs[build_lib.BuildVersionKey.CROS_VERSION] = None
171 suite_kwargs[build_lib.BuildVersionKey.ANDROID_BUILD_VERSION] = None
172 suite_kwargs[build_lib.BuildVersionKey.TESTBED_BUILD_VERSION] = None
173
174 task_executor.push(task_executor.SUITES_QUEUE,
175 tag='fake_suite', **suite_kwargs)
176
177 tasks = self.queue.lease_tasks_by_tag(3600,
178 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
179 executed = self.client.multirequest_run(tasks, 'fake_suite')
180 self.assertEqual(len(executed), 0)
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700181 self._assert_bb_client_not_called()
Xinan Lin3ba18a02019-08-13 15:44:55 -0700182
Xinan Lin9e4917d2019-11-04 10:58:47 -0800183 def testShouldSetPriority(self):
184 suite_kwargs = _get_suite_params()
185 suite_kwargs['priority'] = '30'
Xinan Linfb63d572019-09-24 15:49:04 -0700186
Xinan Lin9e4917d2019-11-04 10:58:47 -0800187 task_executor.push(task_executor.SUITES_QUEUE,
188 tag='fake_suite', **suite_kwargs)
189
190 tasks = self.queue.lease_tasks_by_tag(3600,
191 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
192 self.client.multirequest_run(tasks, 'fake_suite')
193 request_map = self._extract_request_map_from_bb_client()
Xinan Linfb63d572019-09-24 15:49:04 -0700194 self._assert_bb_client_called()
Xinan Lin9e4917d2019-11-04 10:58:47 -0800195 self.assertEqual(len(request_map), 1)
196 req = _struct_pb2_to_request_pb2(request_map['fake_board_fake_model'])
197 self.assertEqual(req.params.scheduling.priority, 30)
198
199 def testRequestWithInvalidTags(self):
200 suite_kwargs = [_get_suite_params(board=b)
201 for b in ['foo', 'foo']]
202
203 # test request having None String Tag
204 suite_kwargs[0]['model'] = 'None'
205
206 # test request having None Tag
207 suite_kwargs[1]['model'] = None
208
209 for suite in suite_kwargs:
210 task_executor.push(task_executor.SUITES_QUEUE,
211 tag='fake_suite', **suite)
212
213 tasks = self.queue.lease_tasks_by_tag(3600,
214 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
215 executed = self.client.multirequest_run(tasks, 'fake_suite')
216 self.assertEqual(len(executed), 2)
217
218 self._assert_bb_client_called()
219 request_map = self._extract_request_map_from_bb_client()
220 self.assertEqual(len(request_map), 2)
Xinan Linfb63d572019-09-24 15:49:04 -0700221 want = {
Xinan Lin9e4917d2019-11-04 10:58:47 -0800222 'suite:fake_suite',
223 'build:fake_cros_build',
Xinan Linfb63d572019-09-24 15:49:04 -0700224 'label-pool:DUT_POOL_SUITES',
Xinan Lin9e4917d2019-11-04 10:58:47 -0800225 'label-board:foo',
Xinan Linfb63d572019-09-24 15:49:04 -0700226 }
Xinan Lin9e4917d2019-11-04 10:58:47 -0800227 want_bb = {
228 'suite:fake_suite',
229 }
230 for req_name in ['foo', 'foo_1']:
231 req = _struct_pb2_to_request_pb2(request_map[req_name])
232 req_tags = set([t for t in req.params.decorations.tags])
233 self.assertEqual(want, req_tags)
234 bb_tags = set([t for t in self._extract_tags_from_bb_client()])
235 self.assertEqual(want_bb, bb_tags)
Xinan Linfb63d572019-09-24 15:49:04 -0700236
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700237 def testRequestTags(self):
Xinan Lin9e4917d2019-11-04 10:58:47 -0800238 suite_kwargs = _get_suite_params(
239 board='fake_board',
240 model='fake_model',
241 pool='DUT_POOL_SUITES',
242 suite='fake_suite',
243 cros_build='fake_build',
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700244 )
Xinan Linc8647112020-02-04 16:45:56 -0800245 suite_kwargs['dimensions'] = 'label-wifi:foo,label-bt:hoo'
Xinan Lin9e4917d2019-11-04 10:58:47 -0800246 task_executor.push(task_executor.SUITES_QUEUE,
247 tag='fake_suite', **suite_kwargs)
248
249 tasks = self.queue.lease_tasks_by_tag(3600,
250 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
251 self.client.multirequest_run(tasks, 'fake_suite')
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700252 self._assert_bb_client_called()
253 want = {
Xinan Lin9e4917d2019-11-04 10:58:47 -0800254 'label-board:fake_board',
255 'label-model:fake_model',
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700256 'label-pool:DUT_POOL_SUITES',
Xinan Lin9e4917d2019-11-04 10:58:47 -0800257 'suite:fake_suite',
258 'build:fake_build',
Xinan Linc8647112020-02-04 16:45:56 -0800259 'label-wifi:foo',
260 'label-bt:hoo'
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700261 }
Xinan Lin9e4917d2019-11-04 10:58:47 -0800262 want_bb = {
263 'suite:fake_suite',
264 }
265 request_map = self._extract_request_map_from_bb_client()
266 request_list = [_struct_pb2_to_request_pb2(v)
267 for v in request_map.values()]
268 self.assertEqual(len(request_list), 1)
269 req = request_list[0]
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700270 req_tags = set([t for t in req.params.decorations.tags])
271 self.assertEqual(want, req_tags)
272 bb_tags = set([t for t in self._extract_tags_from_bb_client()])
Xinan Lin9e4917d2019-11-04 10:58:47 -0800273 self.assertEqual(want_bb, bb_tags)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700274
Prathmesh Prabhu73801e12019-08-30 14:09:31 -0700275 def _get_client(self, addr):
276 self.assertEqual(ADDR, addr)
277 return self.fake_client
278
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700279 def _assert_bb_client_called(self):
280 self.assertEqual(len(self.fake_client.called_with_requests), 1)
281
282 def _assert_bb_client_not_called(self):
283 self.assertEqual(len(self.fake_client.called_with_requests), 0)
284
Xinan Lin9e4917d2019-11-04 10:58:47 -0800285 def _extract_request_map_from_bb_client(self):
286 return self.fake_client.called_with_requests[0].properties['requests']
Prathmesh Prabhu6348ea82019-08-30 14:15:21 -0700287
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700288 def _extract_tags_from_bb_client(self):
289 return ['%s:%s' % (t.key, t.value)
290 for t in self.fake_client.called_with_requests[0].tags]
291
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700292
Xinan Lindf0698a2020-02-05 22:38:11 -0800293class TestTaskExecutions(TestPlatformClientTestCase):
294
295 def setUp(self):
296 super(TestTaskExecutions, self).setUp()
297 _mock_bq_client = mock.patch('rest_client.BigqueryRestClient')
298 mock_bq_client = _mock_bq_client.start()
299 self.addCleanup(_mock_bq_client.stop)
300 self.mock_bq_client = FakeBigqueryRestClient(
301 None, project='proj', dataset='dataset', table='foo')
302 mock_bq_client.return_value = self.mock_bq_client
303
304 def testRecordSuccessfulTaskExecution(self):
305 suite_kwargs = [_get_suite_params(board=b)
306 for b in ['foo', 'goo']]
307 for suite in suite_kwargs:
308 task_executor.push(task_executor.SUITES_QUEUE,
309 tag='fake_suite', **suite)
310 tasks = self.queue.lease_tasks_by_tag(3600,
311 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
312 self.client.multirequest_run(tasks, 'fake_suite')
313 self._assert_bb_client_called()
314 task_execution = json_format.Parse(
315 json.dumps(self.mock_bq_client.rows[0]['json']),
316 analytics_pb2.ExecutionTask())
317 self.assertEqual(task_execution.queued_task_id, FAKE_UUID)
318 self.assertEqual(task_execution.response.ctp_build_id, str(FAKE_BUILD_ID))
319 self.assertEqual(task_execution.error.error_message, '')
320
321 def testRecordFailedTaskExecution(self):
322 suite_kwargs = [_get_suite_params(board=b)
323 for b in ['foo', 'goo']]
324 for suite in suite_kwargs:
325 task_executor.push(task_executor.SUITES_QUEUE,
326 tag='fake_suite', **suite)
327 self.fake_client.error = "cros_test_platform error"
328 tasks = self.queue.lease_tasks_by_tag(3600,
329 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
330 self.client.multirequest_run(tasks, 'fake_suite')
331 self._assert_bb_client_called()
332 task_execution = json_format.Parse(
333 json.dumps(self.mock_bq_client.rows[0]['json']),
334 analytics_pb2.ExecutionTask())
335 self.assertEqual(task_execution.queued_task_id, FAKE_UUID)
336 self.assertEqual(task_execution.response.ctp_build_id, '')
337 self.assertEqual(task_execution.error.error_message, self.fake_client.error)
338
339
Xinan Lin3ba18a02019-08-13 15:44:55 -0700340def _struct_pb2_to_request_pb2(struct_pb2):
341 """Transform google struct proto to test_platform_request.
342
343 Args:
344 struct_pb2: A struct_pb2 instance.
345
346 Returns:
347 A request_pb2 instance.
348 """
349 json = json_format.MessageToJson(struct_pb2)
350 return json_format.Parse(json, request_pb2.Request())