blob: 1d21e00cd82c1674b9c18ee1df32834caafad7ed [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 Linc54a7462020-04-17 15:39:01 -0700108 _mock_application_id = mock.patch('constants.application_id')
109 self.mock_application_id = _mock_application_id.start()
110 self.addCleanup(_mock_application_id.stop)
111 self.mock_application_id.return_value = constants.AppID.PROD_APP
Xinan Lin3ba18a02019-08-13 15:44:55 -0700112
Xinan Lin9e4917d2019-11-04 10:58:47 -0800113 def testFormTestPlatformMultiRequestSuccessfully(self):
114 suite_kwargs = [_get_suite_params(board=b)
115 for b in ['foo', 'goo']]
116 for suite in suite_kwargs:
117 task_executor.push(task_executor.SUITES_QUEUE,
118 tag='fake_suite', **suite)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700119
Xinan Lin9e4917d2019-11-04 10:58:47 -0800120 tasks = self.queue.lease_tasks_by_tag(3600,
121 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
122
123 self.client.multirequest_run(tasks, 'fake_suite')
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700124 self._assert_bb_client_called()
Xinan Lin9e4917d2019-11-04 10:58:47 -0800125
126 request_map = self._extract_request_map_from_bb_client()
127 request_list = [_struct_pb2_to_request_pb2(v)
128 for v in request_map.values()]
129 request_list.sort(
130 key=lambda req: req.params.software_attributes.build_target.name)
131 for i, req in enumerate(request_list):
132 self.assertEqual(req.params.scheduling.priority, 10)
133 self.assertEqual(req.params.scheduling.managed_pool,
134 request_pb2.Request.Params.Scheduling.MANAGED_POOL_SUITES)
Prathmesh Prabhu9adfa632020-02-21 10:39:24 -0800135 # Don't check for exact max timeout to stay DRY.
136 # But do check that the maximum is sane.
137 self.assertLess(req.params.time.maximum_duration.seconds, 2*24*60*60)
Aviv Keshetc679faf2019-11-27 17:52:50 -0800138 gs_url = ('gs://chromeos-image-archive/%s' %
139 suite_kwargs[i]['test_source_build'])
140 self.assertEqual(req.params.metadata.test_metadata_url, gs_url)
141 self.assertEqual(req.params.metadata.debug_symbols_archive_url, gs_url)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800142 self.assertEqual(req.test_plan.suite[0].name, suite_kwargs[i]['suite'])
143 self.assertEqual(req.params.software_attributes.build_target.name,
144 suite_kwargs[i]['board'])
145
146 def testPoolName(self):
147 suite_kwargs = [_get_suite_params(board=b)
148 for b in ['foo', 'goo', 'hoo']]
149 suite_kwargs[0]['pool'] = 'cts'
150 suite_kwargs[1]['pool'] = 'wifi'
Xinan Lin9e4917d2019-11-04 10:58:47 -0800151 for suite in suite_kwargs:
152 task_executor.push(task_executor.SUITES_QUEUE,
153 tag='some_suite', **suite)
154
155 tasks = self.queue.lease_tasks_by_tag(3600,
156 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
157 self.client.multirequest_run(tasks, 'some_suite')
158 self._assert_bb_client_called()
159 request_map = self._extract_request_map_from_bb_client()
160 request_list = [_struct_pb2_to_request_pb2(v)
161 for v in request_map.values()]
162 self.assertEqual(len(request_list), 3)
163 request_list.sort(
164 key=lambda req: req.params.software_attributes.build_target.name)
165 self.assertEqual(request_list[0].params.scheduling.managed_pool,
Alex Zamorzaevc1935602019-08-28 14:37:35 -0700166 request_pb2.Request.Params.Scheduling.MANAGED_POOL_CTS)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800167 self.assertEqual(request_list[1].params.scheduling.unmanaged_pool,
168 suite_kwargs[1]['pool'])
Prathmesh Prabhu88409f62019-08-30 14:32:28 -0700169
Xinan Lin3ba18a02019-08-13 15:44:55 -0700170 def testNoFinalBuild(self):
Xinan Lin9e4917d2019-11-04 10:58:47 -0800171 suite_kwargs = _get_suite_params()
172 suite_kwargs[build_lib.BuildVersionKey.CROS_VERSION] = None
173 suite_kwargs[build_lib.BuildVersionKey.ANDROID_BUILD_VERSION] = None
174 suite_kwargs[build_lib.BuildVersionKey.TESTBED_BUILD_VERSION] = None
175
176 task_executor.push(task_executor.SUITES_QUEUE,
177 tag='fake_suite', **suite_kwargs)
178
179 tasks = self.queue.lease_tasks_by_tag(3600,
180 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
181 executed = self.client.multirequest_run(tasks, 'fake_suite')
182 self.assertEqual(len(executed), 0)
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700183 self._assert_bb_client_not_called()
Xinan Lin3ba18a02019-08-13 15:44:55 -0700184
Xinan Lin4757d6f2020-03-24 22:20:31 -0700185 def testShouldSetQsAccount(self):
186 suite_kwargs = _get_suite_params(pool='foo')
187 suite_kwargs['priority'] = '30'
188 suite_kwargs['qs_account'] = 'qs-foo'
189
190 task_executor.push(
191 task_executor.SUITES_QUEUE, tag='fake_suite', **suite_kwargs)
192
193 tasks = self.queue.lease_tasks_by_tag(
194 3600, constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
195 self.client.multirequest_run(tasks, 'fake_suite')
196 request_map = self._extract_request_map_from_bb_client()
197 self._assert_bb_client_called()
198 self.assertEqual(len(request_map), 1)
199 req = _struct_pb2_to_request_pb2(request_map['fake_board_fake_model'])
200 self.assertEqual(req.params.scheduling.qs_account, 'qs-foo')
201 # If qs_account is set, priority should be 0, the default value
202 # defined by proto3.
203 self.assertEqual(req.params.scheduling.priority, 0)
204
205 def testShouldNotSetQsAccountForManagedPool(self):
206 suite_kwargs = _get_suite_params(pool='DUT_POOL_SUITES')
207 suite_kwargs['qs_account'] = 'qs-foo'
208
209 task_executor.push(
210 task_executor.SUITES_QUEUE, tag='fake_suite', **suite_kwargs)
211
212 tasks = self.queue.lease_tasks_by_tag(
213 3600, constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
214 self.client.multirequest_run(tasks, 'fake_suite')
215 request_map = self._extract_request_map_from_bb_client()
216 self._assert_bb_client_called()
217 self.assertEqual(len(request_map), 1)
218 req = _struct_pb2_to_request_pb2(request_map['fake_board_fake_model'])
219 self.assertEqual(req.params.scheduling.qs_account, '')
220
Xinan Lin9e4917d2019-11-04 10:58:47 -0800221 def testShouldSetPriority(self):
222 suite_kwargs = _get_suite_params()
223 suite_kwargs['priority'] = '30'
Xinan Linfb63d572019-09-24 15:49:04 -0700224
Xinan Lin4757d6f2020-03-24 22:20:31 -0700225 task_executor.push(
226 task_executor.SUITES_QUEUE, tag='fake_suite', **suite_kwargs)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800227
228 tasks = self.queue.lease_tasks_by_tag(3600,
229 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
230 self.client.multirequest_run(tasks, 'fake_suite')
231 request_map = self._extract_request_map_from_bb_client()
Xinan Linfb63d572019-09-24 15:49:04 -0700232 self._assert_bb_client_called()
Xinan Lin9e4917d2019-11-04 10:58:47 -0800233 self.assertEqual(len(request_map), 1)
234 req = _struct_pb2_to_request_pb2(request_map['fake_board_fake_model'])
235 self.assertEqual(req.params.scheduling.priority, 30)
236
237 def testRequestWithInvalidTags(self):
238 suite_kwargs = [_get_suite_params(board=b)
239 for b in ['foo', 'foo']]
240
241 # test request having None String Tag
242 suite_kwargs[0]['model'] = 'None'
243
244 # test request having None Tag
245 suite_kwargs[1]['model'] = None
246
247 for suite in suite_kwargs:
248 task_executor.push(task_executor.SUITES_QUEUE,
249 tag='fake_suite', **suite)
250
251 tasks = self.queue.lease_tasks_by_tag(3600,
252 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
253 executed = self.client.multirequest_run(tasks, 'fake_suite')
254 self.assertEqual(len(executed), 2)
255
256 self._assert_bb_client_called()
257 request_map = self._extract_request_map_from_bb_client()
258 self.assertEqual(len(request_map), 2)
Xinan Linfb63d572019-09-24 15:49:04 -0700259 want = {
Xinan Lin9e4917d2019-11-04 10:58:47 -0800260 'suite:fake_suite',
261 'build:fake_cros_build',
Xinan Linfb63d572019-09-24 15:49:04 -0700262 'label-pool:DUT_POOL_SUITES',
Xinan Lin9e4917d2019-11-04 10:58:47 -0800263 'label-board:foo',
Xinan Linfb63d572019-09-24 15:49:04 -0700264 }
Xinan Lin9e4917d2019-11-04 10:58:47 -0800265 want_bb = {
266 'suite:fake_suite',
267 }
268 for req_name in ['foo', 'foo_1']:
269 req = _struct_pb2_to_request_pb2(request_map[req_name])
270 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()])
273 self.assertEqual(want_bb, bb_tags)
Xinan Linfb63d572019-09-24 15:49:04 -0700274
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700275 def testRequestTags(self):
Xinan Lin9e4917d2019-11-04 10:58:47 -0800276 suite_kwargs = _get_suite_params(
277 board='fake_board',
278 model='fake_model',
279 pool='DUT_POOL_SUITES',
280 suite='fake_suite',
281 cros_build='fake_build',
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700282 )
Xinan Linc8647112020-02-04 16:45:56 -0800283 suite_kwargs['dimensions'] = 'label-wifi:foo,label-bt:hoo'
Xinan Lin9e4917d2019-11-04 10:58:47 -0800284 task_executor.push(task_executor.SUITES_QUEUE,
285 tag='fake_suite', **suite_kwargs)
286
287 tasks = self.queue.lease_tasks_by_tag(3600,
288 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
289 self.client.multirequest_run(tasks, 'fake_suite')
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700290 self._assert_bb_client_called()
291 want = {
Xinan Lin9e4917d2019-11-04 10:58:47 -0800292 'label-board:fake_board',
293 'label-model:fake_model',
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700294 'label-pool:DUT_POOL_SUITES',
Xinan Lin9e4917d2019-11-04 10:58:47 -0800295 'suite:fake_suite',
296 'build:fake_build',
Xinan Linc8647112020-02-04 16:45:56 -0800297 'label-wifi:foo',
298 'label-bt:hoo'
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700299 }
Xinan Lin9e4917d2019-11-04 10:58:47 -0800300 want_bb = {
301 'suite:fake_suite',
302 }
303 request_map = self._extract_request_map_from_bb_client()
304 request_list = [_struct_pb2_to_request_pb2(v)
305 for v in request_map.values()]
306 self.assertEqual(len(request_list), 1)
307 req = request_list[0]
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700308 req_tags = set([t for t in req.params.decorations.tags])
309 self.assertEqual(want, req_tags)
310 bb_tags = set([t for t in self._extract_tags_from_bb_client()])
Xinan Lin9e4917d2019-11-04 10:58:47 -0800311 self.assertEqual(want_bb, bb_tags)
Xinan Lin3ba18a02019-08-13 15:44:55 -0700312
Prathmesh Prabhu73801e12019-08-30 14:09:31 -0700313 def _get_client(self, addr):
314 self.assertEqual(ADDR, addr)
315 return self.fake_client
316
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700317 def _assert_bb_client_called(self):
318 self.assertEqual(len(self.fake_client.called_with_requests), 1)
319
320 def _assert_bb_client_not_called(self):
321 self.assertEqual(len(self.fake_client.called_with_requests), 0)
322
Xinan Lin9e4917d2019-11-04 10:58:47 -0800323 def _extract_request_map_from_bb_client(self):
324 return self.fake_client.called_with_requests[0].properties['requests']
Prathmesh Prabhu6348ea82019-08-30 14:15:21 -0700325
Prathmesh Prabhu2382a182019-09-07 21:18:10 -0700326 def _extract_tags_from_bb_client(self):
327 return ['%s:%s' % (t.key, t.value)
328 for t in self.fake_client.called_with_requests[0].tags]
329
Prathmesh Prabhu9a8060a2019-08-30 14:13:23 -0700330
Xinan Lindf0698a2020-02-05 22:38:11 -0800331class TestTaskExecutions(TestPlatformClientTestCase):
332
333 def setUp(self):
334 super(TestTaskExecutions, self).setUp()
335 _mock_bq_client = mock.patch('rest_client.BigqueryRestClient')
336 mock_bq_client = _mock_bq_client.start()
337 self.addCleanup(_mock_bq_client.stop)
338 self.mock_bq_client = FakeBigqueryRestClient(
339 None, project='proj', dataset='dataset', table='foo')
340 mock_bq_client.return_value = self.mock_bq_client
341
342 def testRecordSuccessfulTaskExecution(self):
Xinan Lin083ba8f2020-02-06 13:55:18 -0800343 suite = _get_suite_params(board='foo')
344 suite['task_id'] = FAKE_UUID
345 task_executor.push(task_executor.SUITES_QUEUE,
346 tag='fake_suite', **suite)
Xinan Lindf0698a2020-02-05 22:38:11 -0800347 tasks = self.queue.lease_tasks_by_tag(3600,
348 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
349 self.client.multirequest_run(tasks, 'fake_suite')
350 self._assert_bb_client_called()
351 task_execution = json_format.Parse(
352 json.dumps(self.mock_bq_client.rows[0]['json']),
353 analytics_pb2.ExecutionTask())
354 self.assertEqual(task_execution.queued_task_id, FAKE_UUID)
355 self.assertEqual(task_execution.response.ctp_build_id, str(FAKE_BUILD_ID))
356 self.assertEqual(task_execution.error.error_message, '')
357
358 def testRecordFailedTaskExecution(self):
Xinan Lin083ba8f2020-02-06 13:55:18 -0800359 suite = _get_suite_params(board='foo')
360 suite['task_id'] = FAKE_UUID
361 task_executor.push(task_executor.SUITES_QUEUE,
362 tag='fake_suite', **suite)
Xinan Lindf0698a2020-02-05 22:38:11 -0800363 self.fake_client.error = "cros_test_platform error"
364 tasks = self.queue.lease_tasks_by_tag(3600,
365 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
366 self.client.multirequest_run(tasks, 'fake_suite')
367 self._assert_bb_client_called()
368 task_execution = json_format.Parse(
369 json.dumps(self.mock_bq_client.rows[0]['json']),
370 analytics_pb2.ExecutionTask())
371 self.assertEqual(task_execution.queued_task_id, FAKE_UUID)
372 self.assertEqual(task_execution.response.ctp_build_id, '')
373 self.assertEqual(task_execution.error.error_message, self.fake_client.error)
374
Xinan Lin083ba8f2020-02-06 13:55:18 -0800375 def testIgnoreTaskWithoutTaskID(self):
376 suite = _get_suite_params(board='foo')
377 task_executor.push(task_executor.SUITES_QUEUE,
378 tag='fake_suite', **suite)
379 self.fake_client.error = "cros_test_platform error"
380 tasks = self.queue.lease_tasks_by_tag(3600,
381 constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
382 self.client.multirequest_run(tasks, 'fake_suite')
383 self._assert_bb_client_called()
384 self.assertEqual(len(self.mock_bq_client.rows), 0)
385
Xinan Linc54a7462020-04-17 15:39:01 -0700386 def testIgnoreSuiteForUnamanagedPoolInStaging(self):
387 self.mock_application_id.return_value = 'suite-scheduler-staging'
388 suite = _get_suite_params(pool='foo')
389 task_executor.push(task_executor.SUITES_QUEUE, tag='fake_suite', **suite)
390 self.fake_client.error = "cros_test_platform error"
391 tasks = self.queue.lease_tasks_by_tag(
392 3600, constants.Buildbucket.MULTIREQUEST_SIZE, deadline=60)
393 self.client.multirequest_run(tasks, 'fake_suite')
394 self._assert_bb_client_not_called()
395
Xinan Lindf0698a2020-02-05 22:38:11 -0800396
Xinan Lin3ba18a02019-08-13 15:44:55 -0700397def _struct_pb2_to_request_pb2(struct_pb2):
398 """Transform google struct proto to test_platform_request.
399
400 Args:
401 struct_pb2: A struct_pb2 instance.
402
403 Returns:
404 A request_pb2 instance.
405 """
406 json = json_format.MessageToJson(struct_pb2)
407 return json_format.Parse(json, request_pb2.Request())