| # -*- coding: utf-8 -*- |
| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Unittests for request_build.py.""" |
| |
| from __future__ import print_function |
| |
| import json |
| import sys |
| |
| import mock |
| |
| from chromite.lib import auth |
| from chromite.lib import buildbucket_lib |
| from chromite.lib import config_lib |
| from chromite.lib import constants |
| from chromite.lib import cros_test_lib |
| from chromite.lib import request_build |
| |
| # Tests need internal access. |
| # pylint: disable=protected-access |
| |
| |
| assert sys.version_info >= (3, 6), 'This module requires Python 3.6+' |
| |
| |
| class RequestBuildHelperTestsBase(cros_test_lib.MockTestCase): |
| """Tests for RequestBuild.""" |
| BRANCH = 'test-branch' |
| PATCHES = ('5555', '6666') |
| BUILD_CONFIG_MIN = 'amd64-generic-paladin-tryjob' |
| BUILD_CONFIG_MAX = 'amd64-generic-paladin' |
| UNKNOWN_CONFIG = 'unknown-config' |
| LUCI_BUILDER = 'luci_build' |
| DISPLAY_LABEL = 'display' |
| PASS_THROUGH_ARGS = ['funky', 'cold', 'medina'] |
| TEST_EMAIL = 'explicit_email' |
| TEST_TEMPLATE = 'explicit_template' |
| MASTER_CIDB_ID = 'master_cidb_id' |
| MASTER_BUILDBUCKET_ID = 'master_bb_id' |
| TEST_BUCKET = 'luci.chromeos.general' # Use Prod bucket for network test. |
| EXTRA_PROPERTIES = {'full_version': 'R84-13099.77.0'} |
| |
| def _CreateJobMin(self): |
| return request_build.RequestBuild(build_config=self.BUILD_CONFIG_MIN) |
| |
| def _CreateJobMax(self): |
| return request_build.RequestBuild( |
| build_config=self.BUILD_CONFIG_MAX, |
| luci_builder=self.LUCI_BUILDER, |
| display_label=self.DISPLAY_LABEL, |
| branch=self.BRANCH, |
| extra_args=self.PASS_THROUGH_ARGS, |
| extra_properties=self.EXTRA_PROPERTIES, |
| user_email=self.TEST_EMAIL, |
| email_template=self.TEST_TEMPLATE, |
| master_cidb_id=self.MASTER_CIDB_ID, |
| master_buildbucket_id=self.MASTER_BUILDBUCKET_ID, |
| bucket=self.TEST_BUCKET, |
| requested_bot='botname') |
| |
| def _CreateJobUnknown(self): |
| return request_build.RequestBuild( |
| build_config=self.UNKNOWN_CONFIG, |
| display_label=self.DISPLAY_LABEL, |
| branch='master', |
| extra_args=(), |
| user_email='default_email', |
| master_buildbucket_id=None) |
| |
| |
| class RequestBuildHelperTestsMock(RequestBuildHelperTestsBase): |
| """Perform real buildbucket requests against a fake instance.""" |
| |
| def setUp(self): |
| # This mocks out the class, then creates a return_value for a function on |
| # instances of it. We do this instead of just mocking out the function to |
| # ensure not real network requests are made in other parts of the class. |
| client_mock = self.PatchObject(buildbucket_lib, 'BuildbucketClient') |
| client_mock().PutBuildRequest.return_value = { |
| 'build': {'id': 'fake_buildbucket_id'} |
| } |
| |
| def testMinRequestBody(self): |
| """Verify our request body with min options.""" |
| job = self._CreateJobMin() |
| |
| self.assertEqual(job.bucket, constants.INTERNAL_SWARMING_BUILDBUCKET_BUCKET) |
| self.assertEqual(job.luci_builder, config_lib.LUCI_BUILDER_TRY) |
| self.assertEqual(job.display_label, config_lib.DISPLAY_LABEL_TRYJOB) |
| |
| body = job._GetRequestBody() |
| |
| self.assertEqual(body, { |
| 'parameters_json': mock.ANY, |
| 'bucket': 'luci.chromeos.general', |
| 'tags': [ |
| 'cbb_branch:master', |
| 'cbb_config:amd64-generic-paladin-tryjob', |
| 'cbb_display_label:tryjob', |
| ] |
| }) |
| |
| parameters_parsed = json.loads(body['parameters_json']) |
| |
| self.assertEqual(parameters_parsed, { |
| u'builder_name': u'Try', |
| u'properties': { |
| u'cbb_branch': u'master', |
| u'cbb_config': u'amd64-generic-paladin-tryjob', |
| u'cbb_display_label': u'tryjob', |
| u'cbb_extra_args': [], |
| } |
| }) |
| |
| |
| def testMaxRequestBody(self): |
| """Verify our request body with max options.""" |
| job = self._CreateJobMax() |
| |
| self.assertEqual(job.bucket, self.TEST_BUCKET) |
| self.assertEqual(job.luci_builder, self.LUCI_BUILDER) |
| self.assertEqual(job.display_label, 'display') |
| |
| body = job._GetRequestBody() |
| |
| self.assertEqual(body, { |
| 'parameters_json': mock.ANY, |
| 'bucket': self.TEST_BUCKET, |
| 'tags': [ |
| 'buildset:cros/parent_buildbucket_id/master_bb_id', |
| 'cbb_branch:test-branch', |
| 'cbb_config:amd64-generic-paladin', |
| 'cbb_display_label:display', |
| 'cbb_email:explicit_email', |
| 'cbb_master_build_id:master_cidb_id', |
| 'cbb_master_buildbucket_id:master_bb_id', |
| 'full_version:R84-13099.77.0', |
| 'master:False', |
| ] |
| }) |
| |
| parameters_parsed = json.loads(body['parameters_json']) |
| |
| self.assertEqual(parameters_parsed, { |
| u'builder_name': u'luci_build', |
| u'email_notify': [{u'email': u'explicit_email', |
| u'template': u'explicit_template'}], |
| u'properties': { |
| u'buildset': u'cros/parent_buildbucket_id/master_bb_id', |
| u'cbb_branch': u'test-branch', |
| u'cbb_config': u'amd64-generic-paladin', |
| u'cbb_display_label': u'display', |
| u'cbb_email': u'explicit_email', |
| u'cbb_extra_args': [u'funky', u'cold', u'medina'], |
| u'cbb_master_build_id': u'master_cidb_id', |
| u'cbb_master_buildbucket_id': u'master_bb_id', |
| u'full_version': u'R84-13099.77.0', |
| u'master': u'False', |
| }, |
| u'swarming': { |
| u'override_builder_cfg': { |
| u'dimensions': [ |
| u'240:id:botname', |
| ] |
| } |
| } |
| }) |
| |
| def testUnknownRequestBody(self): |
| """Verify our request body with max options.""" |
| body = self._CreateJobUnknown()._GetRequestBody() |
| |
| self.assertEqual(body, { |
| 'parameters_json': mock.ANY, |
| 'bucket': 'luci.chromeos.general', |
| 'tags': [ |
| 'cbb_branch:master', |
| 'cbb_config:unknown-config', |
| 'cbb_display_label:display', |
| 'cbb_email:default_email', |
| ] |
| }) |
| |
| parameters_parsed = json.loads(body['parameters_json']) |
| |
| self.assertEqual(parameters_parsed, { |
| u'builder_name': u'Try', |
| u'email_notify': [{u'email': u'default_email', |
| u'template': u'default'}], |
| u'properties': { |
| u'cbb_branch': u'master', |
| u'cbb_config': u'unknown-config', |
| u'cbb_display_label': u'display', |
| u'cbb_email': u'default_email', |
| u'cbb_extra_args': [], |
| } |
| }) |
| |
| def testMinDryRun(self): |
| """Do a dryrun of posting the request, min options.""" |
| job = self._CreateJobMin() |
| job.Submit(testjob=True, dryrun=True) |
| |
| def testMaxDryRun(self): |
| """Do a dryrun of posting the request, max options.""" |
| job = self._CreateJobMax() |
| job.Submit(testjob=True, dryrun=True) |
| |
| def testLogGeneration(self): |
| """Validate an import log message.""" |
| sb = request_build.ScheduledBuild( |
| 'bucket', 'buildbucket_id', 'build_config', 'url', 'created_ts') |
| |
| msg = request_build.RequestBuild.BUILDBUCKET_PUT_RESP_FORMAT % sb._asdict() |
| |
| expected = ('Successfully sent PUT request to [buildbucket_bucket:bucket] ' |
| 'with [config:build_config] [buildbucket_id:buildbucket_id].') |
| |
| # This test both validates that we can generate the log message, and that |
| # it's format hasn't changed. Since there are scripts that parse it via |
| # regex, the later is important. |
| self.assertEqual(msg, expected) |
| |
| |
| class RequestBuildHelperTestsNetork(RequestBuildHelperTestsBase): |
| """Perform real buildbucket requests against a test instance.""" |
| |
| def verifyBuildbucketRequest(self, |
| buildbucket_id, |
| expected_bucket, |
| expected_tags, |
| expected_parameters): |
| """Verify the contents of a push to the TEST buildbucket instance. |
| |
| Args: |
| buildbucket_id: Id to verify. |
| expected_bucket: Bucket the push was supposed to go to as a string. |
| expected_tags: List of buildbucket tags as strings. |
| expected_parameters: Python dict equivalent to json string in |
| parameters_json. |
| """ |
| buildbucket_client = buildbucket_lib.BuildbucketClient( |
| auth.GetAccessToken, buildbucket_lib.BUILDBUCKET_TEST_HOST, |
| service_account_json=buildbucket_lib.GetServiceAccount( |
| constants.CHROMEOS_SERVICE_ACCOUNT)) |
| |
| request = buildbucket_client.GetBuildRequest(buildbucket_id, False) |
| |
| self.assertEqual(request['build']['id'], buildbucket_id) |
| self.assertEqual(request['build']['bucket'], expected_bucket) |
| self.assertCountEqual(request['build']['tags'], expected_tags) |
| |
| request_parameters = json.loads(request['build']['parameters_json']) |
| self.assertEqual(request_parameters, expected_parameters) |
| |
| @cros_test_lib.NetworkTest() |
| def testMinTestBucket(self): |
| """Talk to a test buildbucket instance with min job settings.""" |
| job = self._CreateJobMin() |
| result = job.Submit(testjob=True) |
| |
| self.verifyBuildbucketRequest( |
| result.buildbucket_id, |
| 'luci.chromeos.general', |
| [ |
| 'builder:Try', |
| 'cbb_branch:master', |
| 'cbb_config:amd64-generic-paladin-tryjob', |
| 'cbb_display_label:tryjob', |
| ], |
| { |
| u'builder_name': u'Try', |
| u'properties': { |
| u'cbb_branch': u'master', |
| u'cbb_config': u'amd64-generic-paladin-tryjob', |
| u'cbb_display_label': u'tryjob', |
| u'cbb_extra_args': [], |
| }, |
| }) |
| |
| self.assertEqual( |
| result, |
| request_build.ScheduledBuild( |
| bucket='luci.chromeos.general', |
| buildbucket_id=result.buildbucket_id, |
| build_config='amd64-generic-paladin-tryjob', |
| url=u'https://ci.chromium.org/b/%s' % result.buildbucket_id, |
| created_ts=mock.ANY), |
| ) |
| |
| @cros_test_lib.NetworkTest() |
| def testMaxTestBucket(self): |
| """Talk to a test buildbucket instance with max job settings.""" |
| job = self._CreateJobMax() |
| result = job.Submit(testjob=True) |
| |
| self.verifyBuildbucketRequest( |
| result.buildbucket_id, |
| self.TEST_BUCKET, |
| [ |
| 'builder:luci_build', |
| 'buildset:cros/parent_buildbucket_id/master_bb_id', |
| 'cbb_branch:test-branch', |
| 'cbb_display_label:display', |
| 'cbb_config:amd64-generic-paladin', |
| 'cbb_email:explicit_email', |
| 'cbb_master_build_id:master_cidb_id', |
| 'cbb_master_buildbucket_id:master_bb_id', |
| 'master:False', |
| ], |
| { |
| u'builder_name': u'luci_build', |
| u'email_notify': [{u'email': u'explicit_email', |
| u'template': u'explicit_template'}], |
| u'properties': { |
| u'buildset': u'cros/parent_buildbucket_id/master_bb_id', |
| u'cbb_branch': u'test-branch', |
| u'cbb_config': u'amd64-generic-paladin', |
| u'cbb_display_label': u'display', |
| u'cbb_email': u'explicit_email', |
| u'cbb_extra_args': [u'funky', u'cold', u'medina'], |
| u'cbb_master_build_id': u'master_cidb_id', |
| u'cbb_master_buildbucket_id': u'master_bb_id', |
| u'master': u'False', |
| }, |
| u'swarming': { |
| u'override_builder_cfg': { |
| u'dimensions': [ |
| u'240:id:botname', |
| ] |
| } |
| } |
| }) |
| |
| self.assertEqual( |
| result, |
| request_build.ScheduledBuild( |
| bucket='luci.chromeos.general', |
| buildbucket_id=result.buildbucket_id, |
| build_config='amd64-generic-paladin', |
| url=u'https://ci.chromium.org/b/%s' % result.buildbucket_id, |
| created_ts=mock.ANY), |
| ) |
| |
| # pylint: disable=protected-access |
| def testPostConfigToBuildBucket(self): |
| """Check syntax for PostConfigsToBuildBucket.""" |
| self.PatchObject(auth, 'Login') |
| self.PatchObject(auth, 'Token') |
| self.PatchObject(request_build.RequestBuild, '_PutConfigToBuildBucket') |
| |
| remote_try_job = request_build.RequestBuild( |
| build_config=self.BUILD_CONFIG_MIN, |
| display_label=self.DISPLAY_LABEL, |
| branch='master', |
| extra_args=(), |
| user_email='default_email', |
| master_buildbucket_id=None) |
| remote_try_job.Submit(testjob=True, dryrun=True) |