| # Copyright (C) 2011 Google Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following disclaimer |
| # in the documentation and/or other materials provided with the |
| # distribution. |
| # * Neither the name of Google Inc. nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| """Represents a set of builder bots running web tests. |
| |
| This class is used to hold a list of builder bots running web tests and their |
| corresponding port names and TestExpectations specifiers. |
| """ |
| |
| import json |
| |
| from blinkpy.common.path_finder import PathFinder |
| |
| class BuilderList(object): |
| def __init__(self, builders_dict): |
| """Creates and validates a builders list. |
| |
| The given dictionary maps builder names to dicts with the keys: |
| "port_name": A fully qualified port name. |
| "specifiers": A list of specifiers used to describe a builder. |
| The specifiers list will at the very least have a valid |
| port version specifier like "Mac10.15" and and a valid build |
| type specifier like "Release". |
| "is_try_builder": Whether the builder is a trybot. |
| "master": The master name of the builder. It is deprecated, but still required |
| by test-results.appspot.com API." |
| "has_webdriver_tests": Whether webdriver_tests_suite runs on this builder. |
| |
| Possible refactoring note: Potentially, it might make sense to use |
| blinkpy.common.net.results_fetcher.Builder and add port_name and |
| specifiers properties to that class. |
| """ |
| self._builders = builders_dict |
| for builder in builders_dict: |
| specifiers = { |
| s.lower() for s in builders_dict[builder].get('specifiers', {})} |
| assert 'port_name' in builders_dict[builder] |
| assert ('android' in specifiers or |
| len(builders_dict[builder]['specifiers']) == 2) |
| |
| @staticmethod |
| def load_default_builder_list(filesystem): |
| """Loads the set of builders from a JSON file and returns the BuilderList.""" |
| path = PathFinder(filesystem).path_from_blink_tools( |
| 'blinkpy', 'common', 'config', 'builders.json') |
| contents = filesystem.read_text_file(path) |
| return BuilderList(json.loads(contents)) |
| |
| def all_builder_names(self): |
| return sorted(self._builders) |
| |
| def all_try_builder_names(self): |
| return self.filter_builders(is_try=True) |
| |
| def all_cq_try_builder_names(self): |
| return self.filter_builders(is_cq=True) |
| |
| def all_continuous_builder_names(self): |
| return self.filter_builders(is_try=False) |
| |
| def filter_builders(self, exclude_specifiers=None, include_specifiers=None, |
| is_try=False, is_cq=False): |
| _lower_specifiers = lambda specifiers: {s.lower() for s in specifiers} |
| exclude_specifiers = _lower_specifiers(exclude_specifiers or {}) |
| include_specifiers = _lower_specifiers(include_specifiers or {}) |
| builders = [] |
| for b in self._builders: |
| builder_specifiers = _lower_specifiers( |
| self._builders[b].get('specifiers', {})) |
| if is_try and self._builders[b].get('is_try_builder', False) != is_try: |
| continue |
| if is_cq and self._builders[b].get('is_cq_builder', False) != is_cq: |
| continue |
| if ((not is_cq and not is_try) and self._builders[b].get('is_try_builder', False)): |
| continue |
| if builder_specifiers & exclude_specifiers: |
| continue |
| if (include_specifiers and |
| not include_specifiers & builder_specifiers): |
| continue |
| builders.append(b) |
| return sorted(builders) |
| |
| def all_port_names(self): |
| return sorted({b['port_name'] for b in self._builders.values()}) |
| |
| def bucket_for_builder(self, builder_name): |
| return self._builders[builder_name].get('bucket', '') |
| |
| def master_for_builder(self, builder_name): |
| return self._builders[builder_name].get('master', '') |
| |
| def has_webdriver_tests_for_builder(self, builder_name): |
| return self._builders[builder_name].get('has_webdriver_tests') |
| |
| def port_name_for_builder_name(self, builder_name): |
| return self._builders[builder_name]['port_name'] |
| |
| def specifiers_for_builder(self, builder_name): |
| return self._builders[builder_name]['specifiers'] |
| |
| def is_try_server_builder(self, builder_name): |
| return self._builders[builder_name].get('is_try_builder', False) |
| |
| def is_wpt_builder(self, builder_name): |
| return 'wpt' in builder_name |
| |
| def platform_specifier_for_builder(self, builder_name): |
| return self.specifiers_for_builder(builder_name)[0] |
| |
| def builder_name_for_port_name(self, target_port_name): |
| """Returns a builder name for the given port name. |
| |
| Multiple builders can have the same port name; this function only |
| returns builder names for non-try-bot builders, and it gives preference |
| to non-debug builders. If no builder is found, None is returned. |
| """ |
| debug_builder_name = None |
| for builder_name, builder_info in self._builders.iteritems(): |
| if builder_info.get('is_try_builder'): |
| continue |
| if builder_info['port_name'] == target_port_name: |
| if 'dbg' in builder_name: |
| debug_builder_name = builder_name |
| else: |
| return builder_name |
| return debug_builder_name |
| |
| def version_specifier_for_port_name(self, target_port_name): |
| """Returns the OS version specifier for a given port name. |
| |
| This just uses information in the builder list, and it returns |
| the version specifier for the first builder that matches, even |
| if it's a try bot builder. |
| """ |
| for _, builder_info in sorted(self._builders.iteritems()): |
| if builder_info['port_name'] == target_port_name: |
| return builder_info['specifiers'][0] |
| return None |
| |
| def builder_name_for_specifiers(self, version, build_type, is_try_builder): |
| """Returns the builder name for a give version and build type. |
| |
| Args: |
| version: A string with the OS version specifier. e.g. "Trusty", "Win10". |
| build_type: A string with the build type. e.g. "Debug" or "Release". |
| |
| Returns: |
| The builder name if found, or an empty string if no match was found. |
| """ |
| for builder_name, info in sorted(self._builders.items()): |
| specifiers = set(spec.lower() for spec in info['specifiers']) |
| is_try_builder_info = info.get('is_try_builder', False) |
| if (version.lower() in specifiers |
| and build_type.lower() in specifiers |
| and is_try_builder_info == is_try_builder): |
| return builder_name |
| return '' |