| # |
| # Copyright (C) 2020 Bloomberg Finance LP |
| # |
| # This program is free software; you can redistribute it and/or |
| # modify it under the terms of the GNU Lesser General Public |
| # License as published by the Free Software Foundation; either |
| # version 2 of the License, or (at your option) any later version. |
| # |
| # This library is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| # Lesser General Public License for more details. |
| # |
| # You should have received a copy of the GNU Lesser General Public |
| # License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| # |
| |
| import os |
| from collections import OrderedDict |
| |
| from .runcli import Cli |
| |
| |
| def check_cache_key_stability(project_path: os.PathLike, cli: Cli) -> None: |
| """ |
| Check that the cache key of various elements has not changed. |
| |
| This ensures that elements do not break cache keys unexpectedly. |
| |
| The format of the project is expected to be: |
| |
| .. code-block:: |
| |
| ./ |
| ./project.conf |
| ./elem1.bst |
| ./elem1.expected |
| ./elem2.bst |
| ./elem2.expected |
| # Or in sub-directories |
| ./mydir/elem3.bst |
| ./mydir/elem3.expected |
| |
| The ``.expected`` file should contain the expected cache key. |
| |
| In order to automatically created the ``.expected`` files, or updated them, |
| you can run ``python3 -m buildstream.testing._update_cachekeys`` in the |
| project's directory. |
| |
| :param project_path: Path to a project |
| :param cli: a `cli` object as provided by the fixture :func:`buildstream.testing.runcli.cli` |
| """ |
| result = cli.run( |
| project=project_path, silent=True, args=["show", "--format", "%{name}::%{full-key}", "target.bst"] |
| ) |
| result.assert_success() |
| _assert_cache_keys(project_path, result.output) |
| |
| |
| ############################### |
| ## Internal Helper functions ## |
| ############################### |
| # Those functions are for internal use only and are not part of the public API |
| |
| |
| def _element_filename(project_dir, element_name, alt_suffix=None): |
| # Get whole filename in the temp project with |
| # the option of changing the .bst suffix to something else |
| # |
| if alt_suffix: |
| |
| # Just in case... |
| assert element_name.endswith(".bst") |
| |
| # Chop off the 'bst' in '.bst' and add the new suffix |
| element_name = element_name[:-3] |
| element_name = element_name + alt_suffix |
| |
| return os.path.join(project_dir, element_name) |
| |
| |
| def _parse_output_keys(output): |
| # Returns an OrderedDict of element names |
| # and their cache keys |
| # |
| actual_keys = OrderedDict() |
| lines = output.splitlines() |
| for line in lines: |
| split = line.split("::") |
| name = split[0] |
| key = split[1] |
| actual_keys[name] = key |
| |
| return actual_keys |
| |
| |
| def _load_expected_keys(project_dir, actual_keys, raise_error=True): |
| # Returns an OrderedDict of element names |
| # and their cache keys |
| # |
| expected_keys = OrderedDict() |
| for element_name in actual_keys: |
| expected = _element_filename(project_dir, element_name, "expected") |
| try: |
| with open(expected, "r") as f: |
| expected_key = f.read() |
| expected_key = expected_key.strip() |
| except FileNotFoundError: |
| expected_key = None |
| if raise_error: |
| raise Exception( |
| "Cache key test needs update, " |
| + "expected file {} not found.\n\n".format(expected) |
| + "Use python3 -m buildstream.testing._update_cachekeys in the" |
| + " project's directory to automatically update this test case" |
| ) |
| |
| expected_keys[element_name] = expected_key |
| |
| return expected_keys |
| |
| |
| def _assert_cache_keys(project_dir, output): |
| # Read in the expected keys from the cache key test directory |
| # and parse the actual keys from the `bst show` output |
| # |
| actual_keys = _parse_output_keys(output) |
| expected_keys = _load_expected_keys(project_dir, actual_keys) |
| mismatches = [] |
| |
| for element_name in actual_keys: |
| if actual_keys[element_name] != expected_keys[element_name]: |
| mismatches.append(element_name) |
| |
| if mismatches: |
| info = "" |
| for element_name in mismatches: |
| info += ( |
| " Element: {}\n".format(element_name) |
| + " Expected: {}\n".format(expected_keys[element_name]) |
| + " Actual: {}\n".format(actual_keys[element_name]) |
| ) |
| |
| raise AssertionError( |
| "Cache key mismatches occurred:\n{}\n".format(info) |
| + "Use python3 -m buildstream.testing._update_cachekeys in the project's " |
| + "directory to automatically update this test case" |
| ) |