| # Copyright 2019 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Test API for cq_deps.""" |
| |
| import base64 |
| import json |
| from typing import Sequence |
| |
| from PB.recipe_modules.pigweed.cq_deps.properties import InputProperties |
| from recipe_engine import post_process, recipe_test_api |
| |
| |
| class CqDepsTestApi(recipe_test_api.RecipeTestApi): |
| def properties(self, **kwargs): |
| return {'$pigweed/cq_deps': InputProperties(**kwargs)} |
| |
| def lookup_cl(self, commit_hash, number=None, prefix='', found=True): |
| if not found: |
| return self.override_step_data( |
| f'{prefix}resolve CL deps.number {commit_hash}', |
| self.m.json.output(None), |
| ) |
| |
| return self.override_step_data( |
| f'{prefix}resolve CL deps.number {commit_hash}', |
| self.m.json.output([{'_number': number}] if number else []), |
| ) |
| |
| def _step_name(self, prefix, name, n): |
| return '{}resolve CL deps.details {}{}'.format( |
| prefix, |
| name, |
| ' ({})'.format(n) if n and n > 1 else '', |
| ) |
| |
| def patches_json( |
| self, |
| origin: str, |
| *reqs: Sequence[str], |
| prefix: str = '', |
| ): |
| """Test data for the retrieval of patches.json. |
| |
| Args: |
| origin: The Gerrit name plus the change number, separated by a colon |
| (e.g., "pigweed:12345") |
| reqs: A list of requirements of the origin, in the same format as |
| the origin. |
| prefix: Prefix for the step name. |
| """ |
| if prefix and not prefix.endswith('.'): |
| # Next line is really only here for tests outside this module. |
| prefix += '.' # pragma: no cover |
| |
| patches = [] |
| for req in reqs: |
| host, number = req.split(':') |
| number = int(number) |
| patches.append({'gerrit_name': host, 'number': number}) |
| |
| return self.m.gitiles.fetch( |
| f'{prefix}resolve CL deps.resolve deps for {origin}.' |
| 'fetch patches.json', |
| json.dumps(patches).encode(), |
| ) |
| |
| def patches_json_error(self, origin, status_code=404, **kwargs): |
| """Test data for the unsuccessful retrieval of patches.json. |
| |
| Args: |
| origin: The Gerrit name plus the change number, separated by a colon |
| (e.g., "pigweed:12345") |
| status_code: The HTTP error code from the attempt to read |
| patches.json. |
| prefix: Prefix for the step name. |
| """ |
| prefix = kwargs.pop('prefix', '') |
| assert not kwargs |
| if prefix and not prefix.endswith('.'): |
| # Next line is really only here for tests outside this module. |
| prefix += '.' # pragma: no cover |
| |
| return self.m.url.error( |
| f'{prefix}resolve CL deps.resolve deps for {origin}.' |
| 'fetch patches.json', |
| status_code, |
| 'error', |
| **kwargs, |
| ) |
| |
| def details( |
| self, |
| name, |
| message='', |
| status='NEW', |
| project='project', |
| patchset=1, |
| prefix='', |
| commit_hash='HASH', |
| parent=None, |
| n=None, |
| patches_json=False, |
| topic=None, |
| ): |
| """Test data for the retrieval of change details from Gerrit.""" |
| number = int(name.split(':')[1]) |
| |
| if not parent: |
| parent = f'parent-{number}' |
| |
| assert status in ('NEW', 'MERGED', 'ABANDONED') |
| |
| if patches_json: |
| files = {'patches.json': {'status': 'A'}} |
| else: |
| files = {'foo.py': {'status': 'A'}} |
| |
| return self.override_step_data( |
| self._step_name(prefix, name, n), |
| self.m.json.output( |
| { |
| '_number': number, |
| 'current_revision': commit_hash, |
| 'project': project, |
| 'revisions': { |
| commit_hash: { |
| '_number': patchset, |
| 'commit': { |
| 'message': message, |
| 'parents': [ |
| { |
| 'commit': parent, |
| } |
| ], |
| }, |
| 'files': files, |
| }, |
| }, |
| 'status': status, |
| 'topic': topic or '', |
| } |
| ), |
| ) |
| |
| def transient(self, name, prefix='', n=None): |
| return self.override_step_data( |
| self._step_name(prefix, name, n), |
| self.m.json.output({'transient': True}), |
| retcode=1, |
| ) |
| |
| def forbidden(self, name, prefix='', attempts=2): |
| data = [] |
| for i in range(0, attempts): |
| data.append( |
| self.override_step_data( |
| self._step_name(prefix, name, i + 1), |
| self.m.json.output({}), |
| retcode=1, |
| ) |
| ) |
| return sum(data[1:], data[0]) |
| |
| def has_deps(self, *names): |
| results = [] |
| for name in names: |
| results.append( |
| self.post_process( |
| post_process.MustRun, |
| f'final deps.{name}', |
| ) |
| ) |
| |
| return sum(results[1:], results[0]) |
| |
| def lacks_deps(self, *names): |
| results = [] |
| for name in names: |
| results.append( |
| self.post_process( |
| post_process.DoesNotRun, |
| f'final deps.{name}', |
| ) |
| ) |
| |
| return sum(results[1:], results[0]) |