blob: 04cb2fe65889a7c1b714635f52cb485cb6da7e62 [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
Xinan Lin57e2d962020-03-30 17:24:53 -070068 def ScheduleBuild(self, req, credentials=None, timeout=10):
Xinan Lin3ba18a02019-08-13 15:44:55 -070069 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)
Prathmesh Prabhu9adfa632020-02-21 10:39:24 -0800131 # Don't check for exact max timeout to stay DRY.
132 # But do check that the maximum is sane.
133 self.assertLess(req.params.time.maximum_duration.seconds, 2*24*60*60)
Aviv Keshetc679faf2019-11-27 17:52:50 -0800134 gs_url = ('gs://chromeos-image-archive/%s' %
135 suite_kwargs[i]['test_source_build'])
136 self.assertEqual(req.params.metadata.test_metadata_url, gs_url)
137 self.assertEqual(req.params.metadata.debug_symbols_archive_url, gs_url)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800138 self.assertEqual(req.test_plan.suite[0].name, suite_kwargs[i]['suite'])
139 self.assertEqual(req.params.software_attributes.build_target.name,
140 suite_kwargs[i]['board'])
141
142 def testPoolName(self):
143 suite_kwargs = [_get_suite_params(board=b)
144 for b in ['foo', 'goo', 'hoo']]
145 suite_kwargs[0]['pool'] = 'cts'
146 suite_kwargs[1]['pool'] = 'wifi'
Xinan Lin9e4917d2019-11-04 10:58:47 -0800147 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'])
Prathmesh Prabhu88409f62019-08-30 14:32:28 -0700165
Xinan Lin3ba18a02019-08-13 15:44:55 -0700166 def testNoFinalBuild(self):
Xinan Lin9e4917d2019-11-04 10:58:47 -0800167 suite_kwargs = _get_suite_params()
168 suite_kwargs[build_lib.BuildVersionKey.CROS_VERSION] = None
169 suite_kwargs[build_lib.BuildVersionKey.ANDROID_BUILD_VERSION] = None
170 suite_kwargs[build_lib.BuildVersionKey.TESTBED_BUILD_VERSION] = None
171
172 task_executor.push(task_executor.SUITES_QUEUE,
173 tag='fake_suite', **suite_kwargs)
174
175 tasks = self.queue.lease_tasks_by_tag(3600,
176 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
177 executed = self.client.multirequest_run(tasks, 'fake_suite')
178 self.assertEqual(len(executed), 0)
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700179 self._assert_bb_client_not_called()
Xinan Lin3ba18a02019-08-13 15:44:55 -0700180
Xinan Lin4757d6f2020-03-24 22:20:31 -0700181 def testShouldSetQsAccount(self):
182 suite_kwargs = _get_suite_params(pool='foo')
183 suite_kwargs['priority'] = '30'
184 suite_kwargs['qs_account'] = 'qs-foo'
185
186 task_executor.push(
187 task_executor.SUITES_QUEUE, tag='fake_suite', **suite_kwargs)
188
189 tasks = self.queue.lease_tasks_by_tag(
190 3600, constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
191 self.client.multirequest_run(tasks, 'fake_suite')
192 request_map = self._extract_request_map_from_bb_client()
193 self._assert_bb_client_called()
194 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.qs_account, 'qs-foo')
197 # If qs_account is set, priority should be 0, the default value
198 # defined by proto3.
199 self.assertEqual(req.params.scheduling.priority, 0)
200
201 def testShouldNotSetQsAccountForManagedPool(self):
202 suite_kwargs = _get_suite_params(pool='DUT_POOL_SUITES')
203 suite_kwargs['qs_account'] = 'qs-foo'
204
205 task_executor.push(
206 task_executor.SUITES_QUEUE, tag='fake_suite', **suite_kwargs)
207
208 tasks = self.queue.lease_tasks_by_tag(
209 3600, constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
210 self.client.multirequest_run(tasks, 'fake_suite')
211 request_map = self._extract_request_map_from_bb_client()
212 self._assert_bb_client_called()
213 self.assertEqual(len(request_map), 1)
214 req = _struct_pb2_to_request_pb2(request_map['fake_board_fake_model'])
215 self.assertEqual(req.params.scheduling.qs_account, '')
216
Xinan Lin9e4917d2019-11-04 10:58:47 -0800217 def testShouldSetPriority(self):
218 suite_kwargs = _get_suite_params()
219 suite_kwargs['priority'] = '30'
Xinan Linfb63d572019-09-24 15:49:04 -0700220
Xinan Lin4757d6f2020-03-24 22:20:31 -0700221 task_executor.push(
222 task_executor.SUITES_QUEUE, tag='fake_suite', **suite_kwargs)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800223
224 tasks = self.queue.lease_tasks_by_tag(3600,
225 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
226 self.client.multirequest_run(tasks, 'fake_suite')
227 request_map = self._extract_request_map_from_bb_client()
Xinan Linfb63d572019-09-24 15:49:04 -0700228 self._assert_bb_client_called()
Xinan Lin9e4917d2019-11-04 10:58:47 -0800229 self.assertEqual(len(request_map), 1)
230 req = _struct_pb2_to_request_pb2(request_map['fake_board_fake_model'])
231 self.assertEqual(req.params.scheduling.priority, 30)
232
233 def testRequestWithInvalidTags(self):
234 suite_kwargs = [_get_suite_params(board=b)
235 for b in ['foo', 'foo']]
236
237 # test request having None String Tag
238 suite_kwargs[0]['model'] = 'None'
239
240 # test request having None Tag
241 suite_kwargs[1]['model'] = None
242
243 for suite in suite_kwargs:
244 task_executor.push(task_executor.SUITES_QUEUE,
245 tag='fake_suite', **suite)
246
247 tasks = self.queue.lease_tasks_by_tag(3600,
248 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
249 executed = self.client.multirequest_run(tasks, 'fake_suite')
250 self.assertEqual(len(executed), 2)
251
252 self._assert_bb_client_called()
253 request_map = self._extract_request_map_from_bb_client()
254 self.assertEqual(len(request_map), 2)
Xinan Linfb63d572019-09-24 15:49:04 -0700255 want = {
Xinan Lin9e4917d2019-11-04 10:58:47 -0800256 'suite:fake_suite',
257 'build:fake_cros_build',
Xinan Linfb63d572019-09-24 15:49:04 -0700258 'label-pool:DUT_POOL_SUITES',
Xinan Lin9e4917d2019-11-04 10:58:47 -0800259 'label-board:foo',
Xinan Linfb63d572019-09-24 15:49:04 -0700260 }
Xinan Lin9e4917d2019-11-04 10:58:47 -0800261 want_bb = {
262 'suite:fake_suite',
263 }
264 for req_name in ['foo', 'foo_1']:
265 req = _struct_pb2_to_request_pb2(request_map[req_name])
266 req_tags = set([t for t in req.params.decorations.tags])
267 self.assertEqual(want, req_tags)
268 bb_tags = set([t for t in self._extract_tags_from_bb_client()])
269 self.assertEqual(want_bb, bb_tags)
Xinan Linfb63d572019-09-24 15:49:04 -0700270
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700271 def testRequestTags(self):
Xinan Lin9e4917d2019-11-04 10:58:47 -0800272 suite_kwargs = _get_suite_params(
273 board='fake_board',
274 model='fake_model',
275 pool='DUT_POOL_SUITES',
276 suite='fake_suite',
277 cros_build='fake_build',
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700278 )
Xinan Linc8647112020-02-04 16:45:56 -0800279 suite_kwargs['dimensions'] = 'label-wifi:foo,label-bt:hoo'
Xinan Lin9e4917d2019-11-04 10:58:47 -0800280 task_executor.push(task_executor.SUITES_QUEUE,
281 tag='fake_suite', **suite_kwargs)
282
283 tasks = self.queue.lease_tasks_by_tag(3600,
284 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
285 self.client.multirequest_run(tasks, 'fake_suite')
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700286 self._assert_bb_client_called()
287 want = {
Xinan Lin9e4917d2019-11-04 10:58:47 -0800288 'label-board:fake_board',
289 'label-model:fake_model',
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700290 'label-pool:DUT_POOL_SUITES',
Xinan Lin9e4917d2019-11-04 10:58:47 -0800291 'suite:fake_suite',
292 'build:fake_build',
Xinan Linc8647112020-02-04 16:45:56 -0800293 'label-wifi:foo',
294 'label-bt:hoo'
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700295 }
Xinan Lin9e4917d2019-11-04 10:58:47 -0800296 want_bb = {
297 'suite:fake_suite',
298 }
299 request_map = self._extract_request_map_from_bb_client()
300 request_list = [_struct_pb2_to_request_pb2(v)
301 for v in request_map.values()]
302 self.assertEqual(len(request_list), 1)
303 req = request_list[0]
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700304 req_tags = set([t for t in req.params.decorations.tags])
305 self.assertEqual(want, req_tags)
306 bb_tags = set([t for t in self._extract_tags_from_bb_client()])
Xinan Lin9e4917d2019-11-04 10:58:47 -0800307 self.assertEqual(want_bb, bb_tags)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700308
Prathmesh Prabhu73801e12019-08-30 14:09:31 -0700309 def _get_client(self, addr):
310 self.assertEqual(ADDR, addr)
311 return self.fake_client
312
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700313 def _assert_bb_client_called(self):
314 self.assertEqual(len(self.fake_client.called_with_requests), 1)
315
316 def _assert_bb_client_not_called(self):
317 self.assertEqual(len(self.fake_client.called_with_requests), 0)
318
Xinan Lin9e4917d2019-11-04 10:58:47 -0800319 def _extract_request_map_from_bb_client(self):
320 return self.fake_client.called_with_requests[0].properties['requests']
Prathmesh Prabhu6348ea82019-08-30 14:15:21 -0700321
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700322 def _extract_tags_from_bb_client(self):
323 return ['%s:%s' % (t.key, t.value)
324 for t in self.fake_client.called_with_requests[0].tags]
325
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700326
Xinan Lindf0698a2020-02-05 22:38:11 -0800327class TestTaskExecutions(TestPlatformClientTestCase):
328
329 def setUp(self):
330 super(TestTaskExecutions, self).setUp()
331 _mock_bq_client = mock.patch('rest_client.BigqueryRestClient')
332 mock_bq_client = _mock_bq_client.start()
333 self.addCleanup(_mock_bq_client.stop)
334 self.mock_bq_client = FakeBigqueryRestClient(
335 None, project='proj', dataset='dataset', table='foo')
336 mock_bq_client.return_value = self.mock_bq_client
337
338 def testRecordSuccessfulTaskExecution(self):
Xinan Lin083ba8f2020-02-06 13:55:18 -0800339 suite = _get_suite_params(board='foo')
340 suite['task_id'] = FAKE_UUID
341 task_executor.push(task_executor.SUITES_QUEUE,
342 tag='fake_suite', **suite)
Xinan Lindf0698a2020-02-05 22:38:11 -0800343 tasks = self.queue.lease_tasks_by_tag(3600,
344 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
345 self.client.multirequest_run(tasks, 'fake_suite')
346 self._assert_bb_client_called()
347 task_execution = json_format.Parse(
348 json.dumps(self.mock_bq_client.rows[0]['json']),
349 analytics_pb2.ExecutionTask())
350 self.assertEqual(task_execution.queued_task_id, FAKE_UUID)
351 self.assertEqual(task_execution.response.ctp_build_id, str(FAKE_BUILD_ID))
352 self.assertEqual(task_execution.error.error_message, '')
353
354 def testRecordFailedTaskExecution(self):
Xinan Lin083ba8f2020-02-06 13:55:18 -0800355 suite = _get_suite_params(board='foo')
356 suite['task_id'] = FAKE_UUID
357 task_executor.push(task_executor.SUITES_QUEUE,
358 tag='fake_suite', **suite)
Xinan Lindf0698a2020-02-05 22:38:11 -0800359 self.fake_client.error = "cros_test_platform error"
360 tasks = self.queue.lease_tasks_by_tag(3600,
361 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
362 self.client.multirequest_run(tasks, 'fake_suite')
363 self._assert_bb_client_called()
364 task_execution = json_format.Parse(
365 json.dumps(self.mock_bq_client.rows[0]['json']),
366 analytics_pb2.ExecutionTask())
367 self.assertEqual(task_execution.queued_task_id, FAKE_UUID)
368 self.assertEqual(task_execution.response.ctp_build_id, '')
369 self.assertEqual(task_execution.error.error_message, self.fake_client.error)
370
Xinan Lin083ba8f2020-02-06 13:55:18 -0800371 def testIgnoreTaskWithoutTaskID(self):
372 suite = _get_suite_params(board='foo')
373 task_executor.push(task_executor.SUITES_QUEUE,
374 tag='fake_suite', **suite)
375 self.fake_client.error = "cros_test_platform error"
376 tasks = self.queue.lease_tasks_by_tag(3600,
377 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
378 self.client.multirequest_run(tasks, 'fake_suite')
379 self._assert_bb_client_called()
380 self.assertEqual(len(self.mock_bq_client.rows), 0)
381
Xinan Lindf0698a2020-02-05 22:38:11 -0800382
Xinan Lin3ba18a02019-08-13 15:44:55 -0700383def _struct_pb2_to_request_pb2(struct_pb2):
384 """Transform google struct proto to test_platform_request.
385
386 Args:
387 struct_pb2: A struct_pb2 instance.
388
389 Returns:
390 A request_pb2 instance.
391 """
392 json = json_format.MessageToJson(struct_pb2)
393 return json_format.Parse(json, request_pb2.Request())