blob: 791ea99da2f6422be50f96a82a8786189d61a8e6 [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,
38 'board': board,
39 'model': model,
40 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
Xinan Lin3ba18a02019-08-13 15:44:55 -070041 build_lib.BuildVersionKey.FW_RW_VERSION: 'fake_firmware_rw_build',
42 build_lib.BuildVersionKey.FW_RO_VERSION: 'fake_firmware_ro_build',
43 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: 'fake_android_build',
44 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: 'fake_testbed_build',
45 'num': 1,
Prathmesh Prabhu2382a182019-09-07 21:18:10 -070046 'pool': pool,
Xinan Lin3ba18a02019-08-13 15:44:55 -070047 'priority': 10,
Xinan Lin6e097382019-08-27 18:43:35 -070048 'timeout': 12,
49 'timeout_mins': 4320,
50 'max_runtime_mins': 4320,
Xinan Lin3ba18a02019-08-13 15:44:55 -070051 'no_wait_for_results': True,
52 'test_source_build': 'fake_test_source_build',
53 'job_retry': False,
54 'no_delay': False,
55 'force': False,
56 'run_prod_code': True,
57 'is_skylab': False,
58 }
59
60
61class FakeTestPlatformClient(object):
62
63 def __init__(self, test):
64 self._test = test
65 self.called_with_requests = []
Xinan Lindf0698a2020-02-05 22:38:11 -080066 self.error = None
Xinan Lin3ba18a02019-08-13 15:44:55 -070067
68 def ScheduleBuild(self, req, credentials=None):
69 self.called_with_requests.append(req)
Xinan Lindf0698a2020-02-05 22:38:11 -080070 if self.error:
71 return self.error
72 return build_pb2.Build(id=FAKE_BUILD_ID)
73
74
75class FakeBigqueryRestClient(object):
76
77 def __init__(self, rest_client, project=None, dataset=None, table=None):
78 """Initialize the mock class."""
79 self.table = table
80 self.rows = []
81
82 def insert(self, rows):
83 self.rows = rows
84 return True
Xinan Lin3ba18a02019-08-13 15:44:55 -070085
86
87class TestPlatformClientTestCase(unittest.TestCase):
88
89 def setUp(self):
90 super(TestPlatformClientTestCase, self).setUp()
91 self.fake_client = FakeTestPlatformClient(self)
92 patcher = mock.patch('buildbucket._get_client',
93 return_value=self._get_client(ADDR))
94 patcher.start()
95 self.client = buildbucket.TestPlatformClient(ADDR,
96 'foo-proj',
97 'foo-bucket',
98 'foo-builder')
99 self.addCleanup(patcher.stop)
100 self.testbed = testbed.Testbed()
101 self.testbed.activate()
102 self.addCleanup(self.testbed.deactivate)
103 self.testbed.init_taskqueue_stub(
104 root_path=os.path.join(os.path.dirname(__file__)))
105 self.taskqueue_stub = self.testbed.get_stub(
106 testbed.TASKQUEUE_SERVICE_NAME)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800107 self.queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700108
Xinan Lin9e4917d2019-11-04 10:58:47 -0800109 def testFormTestPlatformMultiRequestSuccessfully(self):
110 suite_kwargs = [_get_suite_params(board=b)
111 for b in ['foo', 'goo']]
112 for suite in suite_kwargs:
113 task_executor.push(task_executor.SUITES_QUEUE,
114 tag='fake_suite', **suite)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700115
Xinan Lin9e4917d2019-11-04 10:58:47 -0800116 tasks = self.queue.lease_tasks_by_tag(3600,
117 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
118
119 self.client.multirequest_run(tasks, 'fake_suite')
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700120 self._assert_bb_client_called()
Xinan Lin9e4917d2019-11-04 10:58:47 -0800121
122 request_map = self._extract_request_map_from_bb_client()
123 request_list = [_struct_pb2_to_request_pb2(v)
124 for v in request_map.values()]
125 request_list.sort(
126 key=lambda req: req.params.software_attributes.build_target.name)
127 for i, req in enumerate(request_list):
128 self.assertEqual(req.params.scheduling.priority, 10)
129 self.assertEqual(req.params.scheduling.managed_pool,
130 request_pb2.Request.Params.Scheduling.MANAGED_POOL_SUITES)
131 self.assertEqual(req.params.time.maximum_duration.seconds, 165600)
Aviv Keshetc679faf2019-11-27 17:52:50 -0800132 gs_url = ('gs://chromeos-image-archive/%s' %
133 suite_kwargs[i]['test_source_build'])
134 self.assertEqual(req.params.metadata.test_metadata_url, gs_url)
135 self.assertEqual(req.params.metadata.debug_symbols_archive_url, gs_url)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800136 self.assertEqual(req.test_plan.suite[0].name, suite_kwargs[i]['suite'])
137 self.assertEqual(req.params.software_attributes.build_target.name,
138 suite_kwargs[i]['board'])
139
140 def testPoolName(self):
141 suite_kwargs = [_get_suite_params(board=b)
142 for b in ['foo', 'goo', 'hoo']]
143 suite_kwargs[0]['pool'] = 'cts'
144 suite_kwargs[1]['pool'] = 'wifi'
145 suite_kwargs[2]['override_qs_account'] = 'dummy@foo.com'
146 for suite in suite_kwargs:
147 task_executor.push(task_executor.SUITES_QUEUE,
148 tag='some_suite', **suite)
149
150 tasks = self.queue.lease_tasks_by_tag(3600,
151 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
152 self.client.multirequest_run(tasks, 'some_suite')
153 self._assert_bb_client_called()
154 request_map = self._extract_request_map_from_bb_client()
155 request_list = [_struct_pb2_to_request_pb2(v)
156 for v in request_map.values()]
157 self.assertEqual(len(request_list), 3)
158 request_list.sort(
159 key=lambda req: req.params.software_attributes.build_target.name)
160 self.assertEqual(request_list[0].params.scheduling.managed_pool,
Alex Zamorzaevc1935602019-08-28 14:37:35 -0700161 request_pb2.Request.Params.Scheduling.MANAGED_POOL_CTS)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800162 self.assertEqual(request_list[1].params.scheduling.unmanaged_pool,
163 suite_kwargs[1]['pool'])
164 self.assertEqual(request_list[2].params.scheduling.quota_account,
165 suite_kwargs[2]['override_qs_account'])
Prathmesh Prabhu88409f62019-08-30 14:32:28 -0700166
Xinan Lin3ba18a02019-08-13 15:44:55 -0700167 def testNoFinalBuild(self):
Xinan Lin9e4917d2019-11-04 10:58:47 -0800168 suite_kwargs = _get_suite_params()
169 suite_kwargs[build_lib.BuildVersionKey.CROS_VERSION] = None
170 suite_kwargs[build_lib.BuildVersionKey.ANDROID_BUILD_VERSION] = None
171 suite_kwargs[build_lib.BuildVersionKey.TESTBED_BUILD_VERSION] = None
172
173 task_executor.push(task_executor.SUITES_QUEUE,
174 tag='fake_suite', **suite_kwargs)
175
176 tasks = self.queue.lease_tasks_by_tag(3600,
177 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
178 executed = self.client.multirequest_run(tasks, 'fake_suite')
179 self.assertEqual(len(executed), 0)
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700180 self._assert_bb_client_not_called()
Xinan Lin3ba18a02019-08-13 15:44:55 -0700181
Xinan Lin9e4917d2019-11-04 10:58:47 -0800182 def testShouldSetPriority(self):
183 suite_kwargs = _get_suite_params()
184 suite_kwargs['priority'] = '30'
Xinan Linfb63d572019-09-24 15:49:04 -0700185
Xinan Lin9e4917d2019-11-04 10:58:47 -0800186 task_executor.push(task_executor.SUITES_QUEUE,
187 tag='fake_suite', **suite_kwargs)
188
189 tasks = self.queue.lease_tasks_by_tag(3600,
190 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
191 self.client.multirequest_run(tasks, 'fake_suite')
192 request_map = self._extract_request_map_from_bb_client()
Xinan Linfb63d572019-09-24 15:49:04 -0700193 self._assert_bb_client_called()
Xinan Lin9e4917d2019-11-04 10:58:47 -0800194 self.assertEqual(len(request_map), 1)
195 req = _struct_pb2_to_request_pb2(request_map['fake_board_fake_model'])
196 self.assertEqual(req.params.scheduling.priority, 30)
197
198 def testRequestWithInvalidTags(self):
199 suite_kwargs = [_get_suite_params(board=b)
200 for b in ['foo', 'foo']]
201
202 # test request having None String Tag
203 suite_kwargs[0]['model'] = 'None'
204
205 # test request having None Tag
206 suite_kwargs[1]['model'] = None
207
208 for suite in suite_kwargs:
209 task_executor.push(task_executor.SUITES_QUEUE,
210 tag='fake_suite', **suite)
211
212 tasks = self.queue.lease_tasks_by_tag(3600,
213 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
214 executed = self.client.multirequest_run(tasks, 'fake_suite')
215 self.assertEqual(len(executed), 2)
216
217 self._assert_bb_client_called()
218 request_map = self._extract_request_map_from_bb_client()
219 self.assertEqual(len(request_map), 2)
Xinan Linfb63d572019-09-24 15:49:04 -0700220 want = {
Xinan Lin9e4917d2019-11-04 10:58:47 -0800221 'suite:fake_suite',
222 'build:fake_cros_build',
Xinan Linfb63d572019-09-24 15:49:04 -0700223 'label-pool:DUT_POOL_SUITES',
Xinan Lin9e4917d2019-11-04 10:58:47 -0800224 'label-board:foo',
Xinan Linfb63d572019-09-24 15:49:04 -0700225 }
Xinan Lin9e4917d2019-11-04 10:58:47 -0800226 want_bb = {
227 'suite:fake_suite',
228 }
229 for req_name in ['foo', 'foo_1']:
230 req = _struct_pb2_to_request_pb2(request_map[req_name])
231 req_tags = set([t for t in req.params.decorations.tags])
232 self.assertEqual(want, req_tags)
233 bb_tags = set([t for t in self._extract_tags_from_bb_client()])
234 self.assertEqual(want_bb, bb_tags)
Xinan Linfb63d572019-09-24 15:49:04 -0700235
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700236 def testRequestTags(self):
Xinan Lin9e4917d2019-11-04 10:58:47 -0800237 suite_kwargs = _get_suite_params(
238 board='fake_board',
239 model='fake_model',
240 pool='DUT_POOL_SUITES',
241 suite='fake_suite',
242 cros_build='fake_build',
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700243 )
Xinan Linc8647112020-02-04 16:45:56 -0800244 suite_kwargs['dimensions'] = 'label-wifi:foo,label-bt:hoo'
Xinan Lin9e4917d2019-11-04 10:58:47 -0800245 task_executor.push(task_executor.SUITES_QUEUE,
246 tag='fake_suite', **suite_kwargs)
247
248 tasks = self.queue.lease_tasks_by_tag(3600,
249 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
250 self.client.multirequest_run(tasks, 'fake_suite')
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700251 self._assert_bb_client_called()
252 want = {
Xinan Lin9e4917d2019-11-04 10:58:47 -0800253 'label-board:fake_board',
254 'label-model:fake_model',
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700255 'label-pool:DUT_POOL_SUITES',
Xinan Lin9e4917d2019-11-04 10:58:47 -0800256 'suite:fake_suite',
257 'build:fake_build',
Xinan Linc8647112020-02-04 16:45:56 -0800258 'label-wifi:foo',
259 'label-bt:hoo'
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700260 }
Xinan Lin9e4917d2019-11-04 10:58:47 -0800261 want_bb = {
262 'suite:fake_suite',
263 }
264 request_map = self._extract_request_map_from_bb_client()
265 request_list = [_struct_pb2_to_request_pb2(v)
266 for v in request_map.values()]
267 self.assertEqual(len(request_list), 1)
268 req = request_list[0]
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700269 req_tags = set([t for t in req.params.decorations.tags])
270 self.assertEqual(want, req_tags)
271 bb_tags = set([t for t in self._extract_tags_from_bb_client()])
Xinan Lin9e4917d2019-11-04 10:58:47 -0800272 self.assertEqual(want_bb, bb_tags)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700273
Prathmesh Prabhu73801e12019-08-30 14:09:31 -0700274 def _get_client(self, addr):
275 self.assertEqual(ADDR, addr)
276 return self.fake_client
277
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700278 def _assert_bb_client_called(self):
279 self.assertEqual(len(self.fake_client.called_with_requests), 1)
280
281 def _assert_bb_client_not_called(self):
282 self.assertEqual(len(self.fake_client.called_with_requests), 0)
283
Xinan Lin9e4917d2019-11-04 10:58:47 -0800284 def _extract_request_map_from_bb_client(self):
285 return self.fake_client.called_with_requests[0].properties['requests']
Prathmesh Prabhu6348ea82019-08-30 14:15:21 -0700286
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700287 def _extract_tags_from_bb_client(self):
288 return ['%s:%s' % (t.key, t.value)
289 for t in self.fake_client.called_with_requests[0].tags]
290
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700291
Xinan Lindf0698a2020-02-05 22:38:11 -0800292class TestTaskExecutions(TestPlatformClientTestCase):
293
294 def setUp(self):
295 super(TestTaskExecutions, self).setUp()
296 _mock_bq_client = mock.patch('rest_client.BigqueryRestClient')
297 mock_bq_client = _mock_bq_client.start()
298 self.addCleanup(_mock_bq_client.stop)
299 self.mock_bq_client = FakeBigqueryRestClient(
300 None, project='proj', dataset='dataset', table='foo')
301 mock_bq_client.return_value = self.mock_bq_client
302
303 def testRecordSuccessfulTaskExecution(self):
Xinan Lin083ba8f2020-02-06 13:55:18 -0800304 suite = _get_suite_params(board='foo')
305 suite['task_id'] = FAKE_UUID
306 task_executor.push(task_executor.SUITES_QUEUE,
307 tag='fake_suite', **suite)
Xinan Lindf0698a2020-02-05 22:38:11 -0800308 tasks = self.queue.lease_tasks_by_tag(3600,
309 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
310 self.client.multirequest_run(tasks, 'fake_suite')
311 self._assert_bb_client_called()
312 task_execution = json_format.Parse(
313 json.dumps(self.mock_bq_client.rows[0]['json']),
314 analytics_pb2.ExecutionTask())
315 self.assertEqual(task_execution.queued_task_id, FAKE_UUID)
316 self.assertEqual(task_execution.response.ctp_build_id, str(FAKE_BUILD_ID))
317 self.assertEqual(task_execution.error.error_message, '')
318
319 def testRecordFailedTaskExecution(self):
Xinan Lin083ba8f2020-02-06 13:55:18 -0800320 suite = _get_suite_params(board='foo')
321 suite['task_id'] = FAKE_UUID
322 task_executor.push(task_executor.SUITES_QUEUE,
323 tag='fake_suite', **suite)
Xinan Lindf0698a2020-02-05 22:38:11 -0800324 self.fake_client.error = "cros_test_platform error"
325 tasks = self.queue.lease_tasks_by_tag(3600,
326 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
327 self.client.multirequest_run(tasks, 'fake_suite')
328 self._assert_bb_client_called()
329 task_execution = json_format.Parse(
330 json.dumps(self.mock_bq_client.rows[0]['json']),
331 analytics_pb2.ExecutionTask())
332 self.assertEqual(task_execution.queued_task_id, FAKE_UUID)
333 self.assertEqual(task_execution.response.ctp_build_id, '')
334 self.assertEqual(task_execution.error.error_message, self.fake_client.error)
335
Xinan Lin083ba8f2020-02-06 13:55:18 -0800336 def testIgnoreTaskWithoutTaskID(self):
337 suite = _get_suite_params(board='foo')
338 task_executor.push(task_executor.SUITES_QUEUE,
339 tag='fake_suite', **suite)
340 self.fake_client.error = "cros_test_platform error"
341 tasks = self.queue.lease_tasks_by_tag(3600,
342 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
343 self.client.multirequest_run(tasks, 'fake_suite')
344 self._assert_bb_client_called()
345 self.assertEqual(len(self.mock_bq_client.rows), 0)
346
Xinan Lindf0698a2020-02-05 22:38:11 -0800347
Xinan Lin3ba18a02019-08-13 15:44:55 -0700348def _struct_pb2_to_request_pb2(struct_pb2):
349 """Transform google struct proto to test_platform_request.
350
351 Args:
352 struct_pb2: A struct_pb2 instance.
353
354 Returns:
355 A request_pb2 instance.
356 """
357 json = json_format.MessageToJson(struct_pb2)
358 return json_format.Parse(json, request_pb2.Request())