From 97bbedbef15fc9edea5b04f66a30ec788da7f130 Mon Sep 17 00:00:00 2001 From: Phil Dawson Date: Mon, 15 Apr 2019 11:02:33 +0100 Subject: plugintestutils: Rename 'plugintestutils' package to 'testing' - Rename plugintestutils to testing. - Don't run the tests from bst-plugins-template. This imports buildstream.plugintestutils so will have to be disabled to get through CI. This can be re nabled once bst-plugins-template has been patched. --- MANIFEST.in | 2 +- buildstream/plugintestutils/__init__.py | 98 --- .../plugintestutils/_sourcetests/__init__.py | 0 .../plugintestutils/_sourcetests/build_checkout.py | 83 -- buildstream/plugintestutils/_sourcetests/fetch.py | 107 --- buildstream/plugintestutils/_sourcetests/mirror.py | 427 ---------- .../_sourcetests/project/elements/base.bst | 5 - .../project/elements/base/base-alpine.bst | 17 - .../_sourcetests/project/elements/import-bin.bst | 4 - .../_sourcetests/project/elements/import-dev.bst | 4 - .../multiple_targets/dependency/horsey.bst | 3 - .../elements/multiple_targets/dependency/pony.bst | 1 - .../elements/multiple_targets/dependency/zebry.bst | 3 - .../project/elements/multiple_targets/order/0.bst | 7 - .../project/elements/multiple_targets/order/1.bst | 4 - .../project/elements/multiple_targets/order/2.bst | 4 - .../project/elements/multiple_targets/order/3.bst | 6 - .../project/elements/multiple_targets/order/4.bst | 2 - .../project/elements/multiple_targets/order/5.bst | 2 - .../project/elements/multiple_targets/order/6.bst | 4 - .../project/elements/multiple_targets/order/7.bst | 4 - .../project/elements/multiple_targets/order/8.bst | 4 - .../project/elements/multiple_targets/order/9.bst | 4 - .../elements/multiple_targets/order/run.bst | 2 - .../plugintestutils/_sourcetests/project/files/bar | 0 .../project/files/bin-files/usr/bin/hello | 3 - .../project/files/dev-files/usr/include/pony.h | 12 - .../project/files/etc-files/etc/buildstream/config | 1 - .../plugintestutils/_sourcetests/project/files/foo | 0 .../project/files/source-bundle/llamas.txt | 1 - .../files/sub-project/elements/import-etc.bst | 4 - .../sub-project/files/etc-files/etc/animal.conf | 1 - .../project/files/sub-project/project.conf | 4 - .../_sourcetests/project/project.conf | 27 - .../_sourcetests/source_determinism.py | 118 --- buildstream/plugintestutils/_sourcetests/track.py | 420 ---------- .../_sourcetests/track_cross_junction.py | 186 ----- .../plugintestutils/_sourcetests/workspace.py | 161 ---- buildstream/plugintestutils/_utils/__init__.py | 10 - buildstream/plugintestutils/_utils/junction.py | 83 -- buildstream/plugintestutils/_utils/site.py | 46 -- buildstream/plugintestutils/integration.py | 51 -- buildstream/plugintestutils/repo.py | 109 --- buildstream/plugintestutils/runcli.py | 857 --------------------- buildstream/testing/__init__.py | 98 +++ buildstream/testing/_sourcetests/__init__.py | 0 buildstream/testing/_sourcetests/build_checkout.py | 83 ++ buildstream/testing/_sourcetests/fetch.py | 107 +++ buildstream/testing/_sourcetests/mirror.py | 427 ++++++++++ .../testing/_sourcetests/project/elements/base.bst | 5 + .../project/elements/base/base-alpine.bst | 17 + .../_sourcetests/project/elements/import-bin.bst | 4 + .../_sourcetests/project/elements/import-dev.bst | 4 + .../multiple_targets/dependency/horsey.bst | 3 + .../elements/multiple_targets/dependency/pony.bst | 1 + .../elements/multiple_targets/dependency/zebry.bst | 3 + .../project/elements/multiple_targets/order/0.bst | 7 + .../project/elements/multiple_targets/order/1.bst | 4 + .../project/elements/multiple_targets/order/2.bst | 4 + .../project/elements/multiple_targets/order/3.bst | 6 + .../project/elements/multiple_targets/order/4.bst | 2 + .../project/elements/multiple_targets/order/5.bst | 2 + .../project/elements/multiple_targets/order/6.bst | 4 + .../project/elements/multiple_targets/order/7.bst | 4 + .../project/elements/multiple_targets/order/8.bst | 4 + .../project/elements/multiple_targets/order/9.bst | 4 + .../elements/multiple_targets/order/run.bst | 2 + buildstream/testing/_sourcetests/project/files/bar | 0 .../project/files/bin-files/usr/bin/hello | 3 + .../project/files/dev-files/usr/include/pony.h | 12 + .../project/files/etc-files/etc/buildstream/config | 1 + buildstream/testing/_sourcetests/project/files/foo | 0 .../project/files/source-bundle/llamas.txt | 1 + .../files/sub-project/elements/import-etc.bst | 4 + .../sub-project/files/etc-files/etc/animal.conf | 1 + .../project/files/sub-project/project.conf | 4 + .../testing/_sourcetests/project/project.conf | 27 + .../testing/_sourcetests/source_determinism.py | 118 +++ buildstream/testing/_sourcetests/track.py | 420 ++++++++++ .../testing/_sourcetests/track_cross_junction.py | 186 +++++ buildstream/testing/_sourcetests/workspace.py | 161 ++++ buildstream/testing/_utils/__init__.py | 10 + buildstream/testing/_utils/junction.py | 83 ++ buildstream/testing/_utils/site.py | 46 ++ buildstream/testing/integration.py | 51 ++ buildstream/testing/repo.py | 109 +++ buildstream/testing/runcli.py | 857 +++++++++++++++++++++ doc/source/core_framework.rst | 2 +- tests/artifactcache/cache_size.py | 2 +- tests/artifactcache/config.py | 2 +- tests/artifactcache/expiry.py | 2 +- tests/artifactcache/junctions.py | 2 +- tests/artifactcache/pull.py | 2 +- tests/artifactcache/push.py | 2 +- tests/cachekey/cachekey.py | 2 +- tests/cachekey/update.py | 2 +- tests/conftest.py | 4 +- tests/elements/filter.py | 4 +- tests/examples/autotools.py | 4 +- tests/examples/developing.py | 4 +- tests/examples/first-project.py | 4 +- tests/examples/flatpak-autotools.py | 4 +- tests/examples/integration-commands.py | 2 +- tests/examples/junctions.py | 2 +- tests/examples/running-commands.py | 2 +- tests/external_plugins.py | 8 +- tests/format/assertion.py | 2 +- tests/format/dependencies.py | 2 +- tests/format/include.py | 4 +- tests/format/junctions.py | 4 +- tests/format/listdirectiveerrors.py | 2 +- tests/format/optionarch.py | 2 +- tests/format/optionbool.py | 2 +- tests/format/optioneltmask.py | 2 +- tests/format/optionenum.py | 2 +- tests/format/optionexports.py | 2 +- tests/format/optionflags.py | 2 +- tests/format/optionos.py | 2 +- tests/format/optionoverrides.py | 2 +- tests/format/options.py | 2 +- tests/format/project.py | 2 +- tests/format/projectoverrides.py | 2 +- tests/format/variables.py | 2 +- tests/frontend/artifact.py | 2 +- tests/frontend/buildcheckout.py | 2 +- tests/frontend/buildtrack.py | 4 +- tests/frontend/completions.py | 2 +- tests/frontend/compose_splits.py | 2 +- tests/frontend/configurable_warnings.py | 2 +- tests/frontend/cross_junction_workspace.py | 4 +- tests/frontend/fetch.py | 4 +- tests/frontend/help.py | 2 +- tests/frontend/init.py | 2 +- tests/frontend/logging.py | 4 +- tests/frontend/mirror.py | 4 +- tests/frontend/order.py | 4 +- tests/frontend/overlaps.py | 2 +- tests/frontend/pull.py | 2 +- tests/frontend/push.py | 2 +- tests/frontend/rebuild.py | 2 +- tests/frontend/remote-caches.py | 2 +- tests/frontend/show.py | 2 +- tests/frontend/source_checkout.py | 2 +- tests/frontend/track.py | 4 +- tests/frontend/version.py | 2 +- tests/frontend/workspace.py | 4 +- tests/integration/artifact.py | 2 +- tests/integration/autotools.py | 4 +- tests/integration/build-uid.py | 2 +- tests/integration/cachedfail.py | 2 +- tests/integration/cmake.py | 4 +- tests/integration/compose-symlinks.py | 2 +- tests/integration/compose.py | 4 +- tests/integration/import.py | 4 +- tests/integration/make.py | 4 +- tests/integration/manual.py | 2 +- tests/integration/messages.py | 2 +- tests/integration/pip_element.py | 4 +- tests/integration/pip_source.py | 4 +- tests/integration/pullbuildtrees.py | 2 +- tests/integration/sandbox-bwrap.py | 2 +- tests/integration/script.py | 2 +- tests/integration/shell.py | 2 +- tests/integration/shellbuildtrees.py | 2 +- tests/integration/sockets.py | 2 +- tests/integration/source-determinism.py | 4 +- tests/integration/stack.py | 2 +- tests/integration/symlinks.py | 2 +- tests/integration/workspace.py | 2 +- .../deprecationwarnings/deprecationwarnings.py | 2 +- tests/remoteexecution/partial.py | 4 +- tests/remoteexecution/simple.py | 4 +- tests/sandboxes/missing-command.py | 2 +- tests/sandboxes/missing_dependencies.py | 2 +- tests/sandboxes/remote-exec-config.py | 2 +- tests/sourcecache/config.py | 2 +- tests/sourcecache/fetch.py | 4 +- tests/sourcecache/push.py | 4 +- tests/sourcecache/source-checkout.py | 2 +- tests/sourcecache/staging.py | 2 +- tests/sourcecache/workspace.py | 2 +- tests/sources/bzr.py | 4 +- tests/sources/deb.py | 2 +- tests/sources/git.py | 4 +- tests/sources/keytest.py | 2 +- tests/sources/local.py | 2 +- tests/sources/no_fetch_cached.py | 4 +- tests/sources/ostree.py | 4 +- tests/sources/patch.py | 2 +- tests/sources/pip.py | 2 +- tests/sources/previous_source_access.py | 2 +- tests/sources/remote.py | 2 +- tests/sources/tar.py | 2 +- tests/sources/zip.py | 2 +- tests/testutils/element_generators.py | 2 +- tests/testutils/junction.py | 2 +- tests/testutils/repo/bzr.py | 2 +- tests/testutils/repo/git.py | 2 +- tests/testutils/repo/ostree.py | 2 +- tests/testutils/repo/tar.py | 2 +- tests/testutils/repo/zip.py | 2 +- tox.ini | 4 +- 202 files changed, 3038 insertions(+), 3044 deletions(-) delete mode 100644 buildstream/plugintestutils/__init__.py delete mode 100644 buildstream/plugintestutils/_sourcetests/__init__.py delete mode 100644 buildstream/plugintestutils/_sourcetests/build_checkout.py delete mode 100644 buildstream/plugintestutils/_sourcetests/fetch.py delete mode 100644 buildstream/plugintestutils/_sourcetests/mirror.py delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/base.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/base/base-alpine.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/import-bin.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/import-dev.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/horsey.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/pony.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/zebry.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/0.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/1.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/2.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/3.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/4.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/5.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/6.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/7.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/8.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/9.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/run.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/files/bar delete mode 100755 buildstream/plugintestutils/_sourcetests/project/files/bin-files/usr/bin/hello delete mode 100644 buildstream/plugintestutils/_sourcetests/project/files/dev-files/usr/include/pony.h delete mode 100644 buildstream/plugintestutils/_sourcetests/project/files/etc-files/etc/buildstream/config delete mode 100644 buildstream/plugintestutils/_sourcetests/project/files/foo delete mode 100644 buildstream/plugintestutils/_sourcetests/project/files/source-bundle/llamas.txt delete mode 100644 buildstream/plugintestutils/_sourcetests/project/files/sub-project/elements/import-etc.bst delete mode 100644 buildstream/plugintestutils/_sourcetests/project/files/sub-project/files/etc-files/etc/animal.conf delete mode 100644 buildstream/plugintestutils/_sourcetests/project/files/sub-project/project.conf delete mode 100644 buildstream/plugintestutils/_sourcetests/project/project.conf delete mode 100644 buildstream/plugintestutils/_sourcetests/source_determinism.py delete mode 100644 buildstream/plugintestutils/_sourcetests/track.py delete mode 100644 buildstream/plugintestutils/_sourcetests/track_cross_junction.py delete mode 100644 buildstream/plugintestutils/_sourcetests/workspace.py delete mode 100644 buildstream/plugintestutils/_utils/__init__.py delete mode 100644 buildstream/plugintestutils/_utils/junction.py delete mode 100644 buildstream/plugintestutils/_utils/site.py delete mode 100644 buildstream/plugintestutils/integration.py delete mode 100644 buildstream/plugintestutils/repo.py delete mode 100644 buildstream/plugintestutils/runcli.py create mode 100644 buildstream/testing/__init__.py create mode 100644 buildstream/testing/_sourcetests/__init__.py create mode 100644 buildstream/testing/_sourcetests/build_checkout.py create mode 100644 buildstream/testing/_sourcetests/fetch.py create mode 100644 buildstream/testing/_sourcetests/mirror.py create mode 100644 buildstream/testing/_sourcetests/project/elements/base.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/base/base-alpine.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/import-bin.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/import-dev.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/horsey.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/pony.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/zebry.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/0.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/1.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/2.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/3.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/4.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/5.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/6.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/7.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/8.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/9.bst create mode 100644 buildstream/testing/_sourcetests/project/elements/multiple_targets/order/run.bst create mode 100644 buildstream/testing/_sourcetests/project/files/bar create mode 100755 buildstream/testing/_sourcetests/project/files/bin-files/usr/bin/hello create mode 100644 buildstream/testing/_sourcetests/project/files/dev-files/usr/include/pony.h create mode 100644 buildstream/testing/_sourcetests/project/files/etc-files/etc/buildstream/config create mode 100644 buildstream/testing/_sourcetests/project/files/foo create mode 100644 buildstream/testing/_sourcetests/project/files/source-bundle/llamas.txt create mode 100644 buildstream/testing/_sourcetests/project/files/sub-project/elements/import-etc.bst create mode 100644 buildstream/testing/_sourcetests/project/files/sub-project/files/etc-files/etc/animal.conf create mode 100644 buildstream/testing/_sourcetests/project/files/sub-project/project.conf create mode 100644 buildstream/testing/_sourcetests/project/project.conf create mode 100644 buildstream/testing/_sourcetests/source_determinism.py create mode 100644 buildstream/testing/_sourcetests/track.py create mode 100644 buildstream/testing/_sourcetests/track_cross_junction.py create mode 100644 buildstream/testing/_sourcetests/workspace.py create mode 100644 buildstream/testing/_utils/__init__.py create mode 100644 buildstream/testing/_utils/junction.py create mode 100644 buildstream/testing/_utils/site.py create mode 100644 buildstream/testing/integration.py create mode 100644 buildstream/testing/repo.py create mode 100644 buildstream/testing/runcli.py diff --git a/MANIFEST.in b/MANIFEST.in index 4365f0df1..729a3e957 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,7 +7,7 @@ include NEWS include README.rst # Data files required by BuildStream's generic source tests -recursive-include buildstream/plugintestutils/_sourcetests/project * +recursive-include buildstream/testing/_sourcetests/project * # Documentation package includes include doc/Makefile diff --git a/buildstream/plugintestutils/__init__.py b/buildstream/plugintestutils/__init__.py deleted file mode 100644 index 19cb1bf9a..000000000 --- a/buildstream/plugintestutils/__init__.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# Copyright (C) 2019 Codethink Limited -# Copyright (C) 2019 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 . - -""" -This package contains various utilities which make it easier to test plugins. -""" - -import os -from collections import OrderedDict -from . import _sourcetests -from .repo import Repo -from .runcli import cli, cli_integration, cli_remote_execution - -# To make use of these test utilities it is necessary to have pytest -# available. However, we don't want to have a hard dependency on -# pytest. -try: - import pytest -except ImportError: - module_name = globals()['__name__'] - msg = "Could not import pytest:\n" \ - "To use the {} module, you must have pytest installed.".format(module_name) - raise ImportError(msg) - - -ALL_REPO_KINDS = OrderedDict() - - -def create_repo(kind, directory, subdir='repo'): - """Convenience method for creating a Repo - - Args: - kind (str): The kind of repo to create (a source plugin basename). This - must have previously been registered using - `register_repo_kind` - directory (str): The path where the repo will keep a cache - - Returns: - (Repo): A new Repo object - """ - try: - constructor = ALL_REPO_KINDS[kind] - except KeyError as e: - raise AssertionError("Unsupported repo kind {}".format(kind)) from e - - return constructor(directory, subdir=subdir) - - -def register_repo_kind(kind, cls): - """Register a new repo kind. - - Registering a repo kind will allow the use of the `create_repo` - method for that kind and include that repo kind in ALL_REPO_KINDS - - In addition, repo_kinds registred prior to - `sourcetests_collection_hook` being called will be automatically - used to test the basic behaviour of their associated source - plugins using the tests in `plugintestutils._sourcetests`. - - Args: - kind (str): The kind of repo to create (a source plugin basename) - cls (cls) : A class derived from Repo. - - """ - ALL_REPO_KINDS[kind] = cls - - -def sourcetests_collection_hook(session): - """ Used to hook the templated source plugin tests into a pyest test suite. - - This should be called via the `pytest_sessionstart - hook `_. - The tests in the _sourcetests package will be collected as part of - whichever test package this hook is called from. - - Args: - session (pytest.Session): The current pytest session - """ - SOURCE_TESTS_PATH = os.path.dirname(_sourcetests.__file__) - # Add the location of the source tests to the session's - # python_files config. Without this, pytest may filter out these - # tests during collection. - session.config.addinivalue_line("python_files", os.path.join(SOURCE_TESTS_PATH, "*.py")) - session.config.args.append(SOURCE_TESTS_PATH) diff --git a/buildstream/plugintestutils/_sourcetests/__init__.py b/buildstream/plugintestutils/_sourcetests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/buildstream/plugintestutils/_sourcetests/build_checkout.py b/buildstream/plugintestutils/_sourcetests/build_checkout.py deleted file mode 100644 index 074be8429..000000000 --- a/buildstream/plugintestutils/_sourcetests/build_checkout.py +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (C) 2018 Codethink Limited -# Copyright (C) 2019 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 . -# - -# Pylint doesn't play well with fixtures and dependency injection from pytest -# pylint: disable=redefined-outer-name - -import os -import pytest - -from buildstream.plugintestutils import create_repo, ALL_REPO_KINDS -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream import _yaml - -# Project directory -TOP_DIR = os.path.dirname(os.path.realpath(__file__)) -DATA_DIR = os.path.join(TOP_DIR, 'project') - -fetch_build_checkout_combos = \ - [("strict", kind) for kind in ALL_REPO_KINDS] + \ - [("non-strict", kind) for kind in ALL_REPO_KINDS] - - -def strict_args(args, strict): - if strict != "strict": - return ['--no-strict', *args] - return args - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("strict,kind", fetch_build_checkout_combos) -def test_fetch_build_checkout(cli, tmpdir, datafiles, strict, kind): - checkout = os.path.join(cli.directory, 'checkout') - project = str(datafiles) - dev_files_path = os.path.join(project, 'files', 'dev-files') - element_path = os.path.join(project, 'elements') - element_name = 'build-test-{}.bst'.format(kind) - - # Create our repo object of the given source type with - # the dev files, and then collect the initial ref. - # - repo = create_repo(kind, str(tmpdir)) - ref = repo.create(dev_files_path) - - # Write out our test target - element = { - 'kind': 'import', - 'sources': [ - repo.source_config(ref=ref) - ] - } - _yaml.dump(element, - os.path.join(element_path, - element_name)) - - assert cli.get_element_state(project, element_name) == 'fetch needed' - result = cli.run(project=project, args=strict_args(['build', element_name], strict)) - result.assert_success() - assert cli.get_element_state(project, element_name) == 'cached' - - # Now check it out - result = cli.run(project=project, args=strict_args([ - 'artifact', 'checkout', element_name, '--directory', checkout - ], strict)) - result.assert_success() - - # Check that the pony.h include from files/dev-files exists - filename = os.path.join(checkout, 'usr', 'include', 'pony.h') - assert os.path.exists(filename) diff --git a/buildstream/plugintestutils/_sourcetests/fetch.py b/buildstream/plugintestutils/_sourcetests/fetch.py deleted file mode 100644 index aaf92a14d..000000000 --- a/buildstream/plugintestutils/_sourcetests/fetch.py +++ /dev/null @@ -1,107 +0,0 @@ -# -# Copyright (C) 2018 Codethink Limited -# Copyright (C) 2019 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 . -# - -# Pylint doesn't play well with fixtures and dependency injection from pytest -# pylint: disable=redefined-outer-name - -import os -import pytest - -from buildstream import _yaml -from .._utils import generate_junction, configure_project -from .. import create_repo, ALL_REPO_KINDS -from .. import cli # pylint: disable=unused-import - -# Project directory -TOP_DIR = os.path.dirname(os.path.realpath(__file__)) -DATA_DIR = os.path.join(TOP_DIR, 'project') - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_fetch(cli, tmpdir, datafiles, kind): - project = str(datafiles) - bin_files_path = os.path.join(project, 'files', 'bin-files') - element_path = os.path.join(project, 'elements') - element_name = 'fetch-test-{}.bst'.format(kind) - - # Create our repo object of the given source type with - # the bin files, and then collect the initial ref. - # - repo = create_repo(kind, str(tmpdir)) - ref = repo.create(bin_files_path) - - # Write out our test target - element = { - 'kind': 'import', - 'sources': [ - repo.source_config(ref=ref) - ] - } - _yaml.dump(element, - os.path.join(element_path, - element_name)) - - # Assert that a fetch is needed - assert cli.get_element_state(project, element_name) == 'fetch needed' - - # Now try to fetch it - result = cli.run(project=project, args=['source', 'fetch', element_name]) - result.assert_success() - - # Assert that we are now buildable because the source is - # now cached. - assert cli.get_element_state(project, element_name) == 'buildable' - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_fetch_cross_junction(cli, tmpdir, datafiles, ref_storage, kind): - project = str(datafiles) - subproject_path = os.path.join(project, 'files', 'sub-project') - junction_path = os.path.join(project, 'elements', 'junction.bst') - - import_etc_path = os.path.join(subproject_path, 'elements', 'import-etc-repo.bst') - etc_files_path = os.path.join(subproject_path, 'files', 'etc-files') - - repo = create_repo(kind, str(tmpdir.join('import-etc'))) - ref = repo.create(etc_files_path) - - element = { - 'kind': 'import', - 'sources': [ - repo.source_config(ref=(ref if ref_storage == 'inline' else None)) - ] - } - _yaml.dump(element, import_etc_path) - - configure_project(project, { - 'ref-storage': ref_storage - }) - - generate_junction(tmpdir, subproject_path, junction_path, store_ref=(ref_storage == 'inline')) - - if ref_storage == 'project.refs': - result = cli.run(project=project, args=['source', 'track', 'junction.bst']) - result.assert_success() - result = cli.run(project=project, args=['source', 'track', 'junction.bst:import-etc.bst']) - result.assert_success() - - result = cli.run(project=project, args=['source', 'fetch', 'junction.bst:import-etc.bst']) - result.assert_success() diff --git a/buildstream/plugintestutils/_sourcetests/mirror.py b/buildstream/plugintestutils/_sourcetests/mirror.py deleted file mode 100644 index d682bb2ef..000000000 --- a/buildstream/plugintestutils/_sourcetests/mirror.py +++ /dev/null @@ -1,427 +0,0 @@ -# -# Copyright (C) 2018 Codethink Limited -# Copyright (C) 2019 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 . -# - -# Pylint doesn't play well with fixtures and dependency injection from pytest -# pylint: disable=redefined-outer-name - -import os -import pytest - -from buildstream import _yaml -from buildstream._exceptions import ErrorDomain -from .._utils import generate_junction -from .. import create_repo, ALL_REPO_KINDS -from .. import cli # pylint: disable=unused-import - -# Project directory -TOP_DIR = os.path.dirname(os.path.realpath(__file__)) -DATA_DIR = os.path.join(TOP_DIR, 'project') - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_mirror_fetch(cli, tmpdir, datafiles, kind): - bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') - dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr') - upstream_repodir = os.path.join(str(tmpdir), 'upstream') - mirror_repodir = os.path.join(str(tmpdir), 'mirror') - project_dir = os.path.join(str(tmpdir), 'project') - os.makedirs(project_dir) - element_dir = os.path.join(project_dir, 'elements') - - # Create repo objects of the upstream and mirror - upstream_repo = create_repo(kind, upstream_repodir) - upstream_repo.create(bin_files_path) - mirror_repo = upstream_repo.copy(mirror_repodir) - upstream_ref = upstream_repo.create(dev_files_path) - - element = { - 'kind': 'import', - 'sources': [ - upstream_repo.source_config(ref=upstream_ref) - ] - } - element_name = 'test.bst' - element_path = os.path.join(element_dir, element_name) - full_repo = element['sources'][0]['url'] - upstream_map, repo_name = os.path.split(full_repo) - alias = 'foo-' + kind - aliased_repo = alias + ':' + repo_name - element['sources'][0]['url'] = aliased_repo - full_mirror = mirror_repo.source_config()['url'] - mirror_map, _ = os.path.split(full_mirror) - os.makedirs(element_dir) - _yaml.dump(element, element_path) - - project = { - 'name': 'test', - 'element-path': 'elements', - 'aliases': { - alias: upstream_map + "/" - }, - 'mirrors': [ - { - 'name': 'middle-earth', - 'aliases': { - alias: [mirror_map + "/"], - }, - }, - ] - } - project_file = os.path.join(project_dir, 'project.conf') - _yaml.dump(project, project_file) - - # No obvious ways of checking that the mirror has been fetched - # But at least we can be sure it succeeds - result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) - result.assert_success() - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_mirror_fetch_upstream_absent(cli, tmpdir, datafiles, kind): - dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr') - upstream_repodir = os.path.join(str(tmpdir), 'upstream') - mirror_repodir = os.path.join(str(tmpdir), 'mirror') - project_dir = os.path.join(str(tmpdir), 'project') - os.makedirs(project_dir) - element_dir = os.path.join(project_dir, 'elements') - - # Create repo objects of the upstream and mirror - upstream_repo = create_repo(kind, upstream_repodir) - ref = upstream_repo.create(dev_files_path) - mirror_repo = upstream_repo.copy(mirror_repodir) - - element = { - 'kind': 'import', - 'sources': [ - upstream_repo.source_config(ref=ref) - ] - } - - element_name = 'test.bst' - element_path = os.path.join(element_dir, element_name) - full_repo = element['sources'][0]['url'] - _, repo_name = os.path.split(full_repo) - alias = 'foo-' + kind - aliased_repo = alias + ':' + repo_name - element['sources'][0]['url'] = aliased_repo - full_mirror = mirror_repo.source_config()['url'] - mirror_map, _ = os.path.split(full_mirror) - os.makedirs(element_dir) - _yaml.dump(element, element_path) - - project = { - 'name': 'test', - 'element-path': 'elements', - 'aliases': { - alias: 'http://www.example.com/' - }, - 'mirrors': [ - { - 'name': 'middle-earth', - 'aliases': { - alias: [mirror_map + "/"], - }, - }, - ] - } - project_file = os.path.join(project_dir, 'project.conf') - _yaml.dump(project, project_file) - - result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) - result.assert_success() - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_mirror_from_includes(cli, tmpdir, datafiles, kind): - bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') - upstream_repodir = os.path.join(str(tmpdir), 'upstream') - mirror_repodir = os.path.join(str(tmpdir), 'mirror') - project_dir = os.path.join(str(tmpdir), 'project') - os.makedirs(project_dir) - element_dir = os.path.join(project_dir, 'elements') - - # Create repo objects of the upstream and mirror - upstream_repo = create_repo(kind, upstream_repodir) - upstream_ref = upstream_repo.create(bin_files_path) - mirror_repo = upstream_repo.copy(mirror_repodir) - - element = { - 'kind': 'import', - 'sources': [ - upstream_repo.source_config(ref=upstream_ref) - ] - } - element_name = 'test.bst' - element_path = os.path.join(element_dir, element_name) - full_repo = element['sources'][0]['url'] - upstream_map, repo_name = os.path.split(full_repo) - alias = 'foo-' + kind - aliased_repo = alias + ':' + repo_name - element['sources'][0]['url'] = aliased_repo - full_mirror = mirror_repo.source_config()['url'] - mirror_map, _ = os.path.split(full_mirror) - os.makedirs(element_dir) - _yaml.dump(element, element_path) - - config_project_dir = str(tmpdir.join('config')) - os.makedirs(config_project_dir, exist_ok=True) - config_project = { - 'name': 'config' - } - _yaml.dump(config_project, os.path.join(config_project_dir, 'project.conf')) - extra_mirrors = { - 'mirrors': [ - { - 'name': 'middle-earth', - 'aliases': { - alias: [mirror_map + "/"], - } - } - ] - } - _yaml.dump(extra_mirrors, os.path.join(config_project_dir, 'mirrors.yml')) - generate_junction(str(tmpdir.join('config_repo')), - config_project_dir, - os.path.join(element_dir, 'config.bst')) - - project = { - 'name': 'test', - 'element-path': 'elements', - 'aliases': { - alias: upstream_map + "/" - }, - '(@)': [ - 'config.bst:mirrors.yml' - ] - } - project_file = os.path.join(project_dir, 'project.conf') - _yaml.dump(project, project_file) - - # Now make the upstream unavailable. - os.rename(upstream_repo.repo, '{}.bak'.format(upstream_repo.repo)) - result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) - result.assert_success() - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_mirror_junction_from_includes(cli, tmpdir, datafiles, kind): - bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') - upstream_repodir = os.path.join(str(tmpdir), 'upstream') - mirror_repodir = os.path.join(str(tmpdir), 'mirror') - project_dir = os.path.join(str(tmpdir), 'project') - os.makedirs(project_dir) - element_dir = os.path.join(project_dir, 'elements') - - # Create repo objects of the upstream and mirror - upstream_repo = create_repo(kind, upstream_repodir) - upstream_ref = upstream_repo.create(bin_files_path) - mirror_repo = upstream_repo.copy(mirror_repodir) - - element = { - 'kind': 'junction', - 'sources': [ - upstream_repo.source_config(ref=upstream_ref) - ] - } - element_name = 'test.bst' - element_path = os.path.join(element_dir, element_name) - full_repo = element['sources'][0]['url'] - upstream_map, repo_name = os.path.split(full_repo) - alias = 'foo-' + kind - aliased_repo = alias + ':' + repo_name - element['sources'][0]['url'] = aliased_repo - full_mirror = mirror_repo.source_config()['url'] - mirror_map, _ = os.path.split(full_mirror) - os.makedirs(element_dir) - _yaml.dump(element, element_path) - - config_project_dir = str(tmpdir.join('config')) - os.makedirs(config_project_dir, exist_ok=True) - config_project = { - 'name': 'config' - } - _yaml.dump(config_project, os.path.join(config_project_dir, 'project.conf')) - extra_mirrors = { - 'mirrors': [ - { - 'name': 'middle-earth', - 'aliases': { - alias: [mirror_map + "/"], - } - } - ] - } - _yaml.dump(extra_mirrors, os.path.join(config_project_dir, 'mirrors.yml')) - generate_junction(str(tmpdir.join('config_repo')), - config_project_dir, - os.path.join(element_dir, 'config.bst')) - - project = { - 'name': 'test', - 'element-path': 'elements', - 'aliases': { - alias: upstream_map + "/" - }, - '(@)': [ - 'config.bst:mirrors.yml' - ] - } - project_file = os.path.join(project_dir, 'project.conf') - _yaml.dump(project, project_file) - - # Now make the upstream unavailable. - os.rename(upstream_repo.repo, '{}.bak'.format(upstream_repo.repo)) - result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) - result.assert_main_error(ErrorDomain.STREAM, None) - # Now make the upstream available again. - os.rename('{}.bak'.format(upstream_repo.repo), upstream_repo.repo) - result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) - result.assert_success() - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_mirror_track_upstream_present(cli, tmpdir, datafiles, kind): - bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') - dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr') - upstream_repodir = os.path.join(str(tmpdir), 'upstream') - mirror_repodir = os.path.join(str(tmpdir), 'mirror') - project_dir = os.path.join(str(tmpdir), 'project') - os.makedirs(project_dir) - element_dir = os.path.join(project_dir, 'elements') - - # Create repo objects of the upstream and mirror - upstream_repo = create_repo(kind, upstream_repodir) - upstream_repo.create(bin_files_path) - mirror_repo = upstream_repo.copy(mirror_repodir) - upstream_ref = upstream_repo.create(dev_files_path) - - element = { - 'kind': 'import', - 'sources': [ - upstream_repo.source_config(ref=upstream_ref) - ] - } - - element_name = 'test.bst' - element_path = os.path.join(element_dir, element_name) - full_repo = element['sources'][0]['url'] - upstream_map, repo_name = os.path.split(full_repo) - alias = 'foo-' + kind - aliased_repo = alias + ':' + repo_name - element['sources'][0]['url'] = aliased_repo - full_mirror = mirror_repo.source_config()['url'] - mirror_map, _ = os.path.split(full_mirror) - os.makedirs(element_dir) - _yaml.dump(element, element_path) - - project = { - 'name': 'test', - 'element-path': 'elements', - 'aliases': { - alias: upstream_map + "/" - }, - 'mirrors': [ - { - 'name': 'middle-earth', - 'aliases': { - alias: [mirror_map + "/"], - }, - }, - ] - } - project_file = os.path.join(project_dir, 'project.conf') - _yaml.dump(project, project_file) - - result = cli.run(project=project_dir, args=['source', 'track', element_name]) - result.assert_success() - - # Tracking tries upstream first. Check the ref is from upstream. - new_element = _yaml.load(element_path) - source = _yaml.node_get(new_element, dict, 'sources', [0]) - if 'ref' in source: - assert _yaml.node_get(source, str, 'ref') == upstream_ref - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_mirror_track_upstream_absent(cli, tmpdir, datafiles, kind): - bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') - dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr') - upstream_repodir = os.path.join(str(tmpdir), 'upstream') - mirror_repodir = os.path.join(str(tmpdir), 'mirror') - project_dir = os.path.join(str(tmpdir), 'project') - os.makedirs(project_dir) - element_dir = os.path.join(project_dir, 'elements') - - # Create repo objects of the upstream and mirror - upstream_repo = create_repo(kind, upstream_repodir) - upstream_ref = upstream_repo.create(bin_files_path) - mirror_repo = upstream_repo.copy(mirror_repodir) - mirror_ref = upstream_ref - upstream_ref = upstream_repo.create(dev_files_path) - - element = { - 'kind': 'import', - 'sources': [ - upstream_repo.source_config(ref=upstream_ref) - ] - } - - element_name = 'test.bst' - element_path = os.path.join(element_dir, element_name) - full_repo = element['sources'][0]['url'] - _, repo_name = os.path.split(full_repo) - alias = 'foo-' + kind - aliased_repo = alias + ':' + repo_name - element['sources'][0]['url'] = aliased_repo - full_mirror = mirror_repo.source_config()['url'] - mirror_map, _ = os.path.split(full_mirror) - os.makedirs(element_dir) - _yaml.dump(element, element_path) - - project = { - 'name': 'test', - 'element-path': 'elements', - 'aliases': { - alias: 'http://www.example.com/' - }, - 'mirrors': [ - { - 'name': 'middle-earth', - 'aliases': { - alias: [mirror_map + "/"], - }, - }, - ] - } - project_file = os.path.join(project_dir, 'project.conf') - _yaml.dump(project, project_file) - - result = cli.run(project=project_dir, args=['source', 'track', element_name]) - result.assert_success() - - # Check that tracking fell back to the mirror - new_element = _yaml.load(element_path) - source = _yaml.node_get(new_element, dict, 'sources', [0]) - if 'ref' in source: - assert _yaml.node_get(source, str, 'ref') == mirror_ref diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/base.bst b/buildstream/plugintestutils/_sourcetests/project/elements/base.bst deleted file mode 100644 index 428afa736..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/base.bst +++ /dev/null @@ -1,5 +0,0 @@ -# elements/base.bst - -kind: stack -depends: - - base/base-alpine.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/base/base-alpine.bst b/buildstream/plugintestutils/_sourcetests/project/elements/base/base-alpine.bst deleted file mode 100644 index c5833095d..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/base/base-alpine.bst +++ /dev/null @@ -1,17 +0,0 @@ -kind: import - -description: | - Alpine Linux base for tests - - Generated using the `tests/integration-tests/base/generate-base.sh` script. - -sources: - - kind: tar - base-dir: '' - (?): - - arch == "x86-64": - ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 - url: "alpine:integration-tests-base.v1.x86_64.tar.xz" - - arch == "aarch64": - ref: 431fb5362032ede6f172e70a3258354a8fd71fcbdeb1edebc0e20968c792329a - url: "alpine:integration-tests-base.v1.aarch64.tar.xz" diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/import-bin.bst b/buildstream/plugintestutils/_sourcetests/project/elements/import-bin.bst deleted file mode 100644 index a847c0c23..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/import-bin.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: import -sources: -- kind: local - path: files/bin-files diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/import-dev.bst b/buildstream/plugintestutils/_sourcetests/project/elements/import-dev.bst deleted file mode 100644 index 152a54667..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/import-dev.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: import -sources: -- kind: local - path: files/dev-files diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/horsey.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/horsey.bst deleted file mode 100644 index bd1ffae9c..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/horsey.bst +++ /dev/null @@ -1,3 +0,0 @@ -kind: autotools -depends: - - multiple_targets/dependency/pony.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/pony.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/pony.bst deleted file mode 100644 index 3c29b4ea1..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/pony.bst +++ /dev/null @@ -1 +0,0 @@ -kind: autotools diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/zebry.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/zebry.bst deleted file mode 100644 index 98447ab52..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/dependency/zebry.bst +++ /dev/null @@ -1,3 +0,0 @@ -kind: autotools -depends: - - multiple_targets/dependency/horsey.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/0.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/0.bst deleted file mode 100644 index a99be06a0..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/0.bst +++ /dev/null @@ -1,7 +0,0 @@ -kind: autotools -description: Root node -depends: - - multiple_targets/order/2.bst - - multiple_targets/order/3.bst - - filename: multiple_targets/order/run.bst - type: runtime diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/1.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/1.bst deleted file mode 100644 index 82b507a62..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/1.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: autotools -description: Root node -depends: - - multiple_targets/order/9.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/2.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/2.bst deleted file mode 100644 index ee1afae20..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/2.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: autotools -description: First dependency level -depends: - - multiple_targets/order/3.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/3.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/3.bst deleted file mode 100644 index 4c3a23dab..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/3.bst +++ /dev/null @@ -1,6 +0,0 @@ -kind: autotools -description: Second dependency level -depends: - - multiple_targets/order/4.bst - - multiple_targets/order/5.bst - - multiple_targets/order/6.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/4.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/4.bst deleted file mode 100644 index b663a0b52..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/4.bst +++ /dev/null @@ -1,2 +0,0 @@ -kind: autotools -description: Third level dependency diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/5.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/5.bst deleted file mode 100644 index b9efcf71b..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/5.bst +++ /dev/null @@ -1,2 +0,0 @@ -kind: autotools -description: Fifth level dependency diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/6.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/6.bst deleted file mode 100644 index 6c19d04e3..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/6.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: autotools -description: Fourth level dependency -depends: - - multiple_targets/order/5.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/7.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/7.bst deleted file mode 100644 index 6805b3e6d..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/7.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: autotools -description: Third level dependency -depends: - - multiple_targets/order/6.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/8.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/8.bst deleted file mode 100644 index b8d8964a0..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/8.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: autotools -description: Second level dependency -depends: - - multiple_targets/order/7.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/9.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/9.bst deleted file mode 100644 index cc13bf3f0..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/9.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: autotools -description: First level dependency -depends: - - multiple_targets/order/8.bst diff --git a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/run.bst b/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/run.bst deleted file mode 100644 index 9b3d2446c..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/elements/multiple_targets/order/run.bst +++ /dev/null @@ -1,2 +0,0 @@ -kind: autotools -description: Not a root node, yet built at the same time as root nodes diff --git a/buildstream/plugintestutils/_sourcetests/project/files/bar b/buildstream/plugintestutils/_sourcetests/project/files/bar deleted file mode 100644 index e69de29bb..000000000 diff --git a/buildstream/plugintestutils/_sourcetests/project/files/bin-files/usr/bin/hello b/buildstream/plugintestutils/_sourcetests/project/files/bin-files/usr/bin/hello deleted file mode 100755 index f534a4083..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/files/bin-files/usr/bin/hello +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -echo "Hello !" diff --git a/buildstream/plugintestutils/_sourcetests/project/files/dev-files/usr/include/pony.h b/buildstream/plugintestutils/_sourcetests/project/files/dev-files/usr/include/pony.h deleted file mode 100644 index 40bd0c2e7..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/files/dev-files/usr/include/pony.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __PONY_H__ -#define __PONY_H__ - -#define PONY_BEGIN "Once upon a time, there was a pony." -#define PONY_END "And they lived happily ever after, the end." - -#define MAKE_PONY(story) \ - PONY_BEGIN \ - story \ - PONY_END - -#endif /* __PONY_H__ */ diff --git a/buildstream/plugintestutils/_sourcetests/project/files/etc-files/etc/buildstream/config b/buildstream/plugintestutils/_sourcetests/project/files/etc-files/etc/buildstream/config deleted file mode 100644 index 04204c7c9..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/files/etc-files/etc/buildstream/config +++ /dev/null @@ -1 +0,0 @@ -config diff --git a/buildstream/plugintestutils/_sourcetests/project/files/foo b/buildstream/plugintestutils/_sourcetests/project/files/foo deleted file mode 100644 index e69de29bb..000000000 diff --git a/buildstream/plugintestutils/_sourcetests/project/files/source-bundle/llamas.txt b/buildstream/plugintestutils/_sourcetests/project/files/source-bundle/llamas.txt deleted file mode 100644 index f98b24871..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/files/source-bundle/llamas.txt +++ /dev/null @@ -1 +0,0 @@ -llamas diff --git a/buildstream/plugintestutils/_sourcetests/project/files/sub-project/elements/import-etc.bst b/buildstream/plugintestutils/_sourcetests/project/files/sub-project/elements/import-etc.bst deleted file mode 100644 index f0171990e..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/files/sub-project/elements/import-etc.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: import -sources: -- kind: local - path: files/etc-files diff --git a/buildstream/plugintestutils/_sourcetests/project/files/sub-project/files/etc-files/etc/animal.conf b/buildstream/plugintestutils/_sourcetests/project/files/sub-project/files/etc-files/etc/animal.conf deleted file mode 100644 index db8c36cba..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/files/sub-project/files/etc-files/etc/animal.conf +++ /dev/null @@ -1 +0,0 @@ -animal=Pony diff --git a/buildstream/plugintestutils/_sourcetests/project/files/sub-project/project.conf b/buildstream/plugintestutils/_sourcetests/project/files/sub-project/project.conf deleted file mode 100644 index bbb8414a3..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/files/sub-project/project.conf +++ /dev/null @@ -1,4 +0,0 @@ -# Project config for frontend build test -name: subtest - -element-path: elements diff --git a/buildstream/plugintestutils/_sourcetests/project/project.conf b/buildstream/plugintestutils/_sourcetests/project/project.conf deleted file mode 100644 index 05b68bfeb..000000000 --- a/buildstream/plugintestutils/_sourcetests/project/project.conf +++ /dev/null @@ -1,27 +0,0 @@ -# Project config for frontend build test -name: test -element-path: elements -aliases: - alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ - project_dir: file://{project_dir} -options: - linux: - type: bool - description: Whether to expect a linux platform - default: True - arch: - type: arch - description: Current architecture - values: - - x86-64 - - aarch64 -split-rules: - test: - - | - /tests - - | - /tests/* - -fatal-warnings: -- bad-element-suffix -- bad-characters-in-name diff --git a/buildstream/plugintestutils/_sourcetests/source_determinism.py b/buildstream/plugintestutils/_sourcetests/source_determinism.py deleted file mode 100644 index 8597a7072..000000000 --- a/buildstream/plugintestutils/_sourcetests/source_determinism.py +++ /dev/null @@ -1,118 +0,0 @@ -# -# Copyright (C) 2018 Codethink Limited -# Copyright (C) 2019 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 . -# - -# Pylint doesn't play well with fixtures and dependency injection from pytest -# pylint: disable=redefined-outer-name - -import os -import pytest - -from buildstream import _yaml -from .._utils.site import HAVE_SANDBOX -from .. import create_repo, ALL_REPO_KINDS -from .. import cli # pylint: disable=unused-import - -# Project directory -TOP_DIR = os.path.dirname(os.path.realpath(__file__)) -DATA_DIR = os.path.join(TOP_DIR, 'project') - - -def create_test_file(*path, mode=0o644, content='content\n'): - path = os.path.join(*path) - os.makedirs(os.path.dirname(path), exist_ok=True) - with open(path, 'w') as f: - f.write(content) - os.fchmod(f.fileno(), mode) - - -def create_test_directory(*path, mode=0o644): - create_test_file(*path, '.keep', content='') - path = os.path.join(*path) - os.chmod(path, mode) - - -@pytest.mark.integration -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", ['local', *ALL_REPO_KINDS]) -@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') -def test_deterministic_source_umask(cli, tmpdir, datafiles, kind): - project = str(datafiles) - element_name = 'list.bst' - element_path = os.path.join(project, 'elements', element_name) - repodir = os.path.join(str(tmpdir), 'repo') - sourcedir = os.path.join(project, 'source') - - create_test_file(sourcedir, 'a.txt', mode=0o700) - create_test_file(sourcedir, 'b.txt', mode=0o755) - create_test_file(sourcedir, 'c.txt', mode=0o600) - create_test_file(sourcedir, 'd.txt', mode=0o400) - create_test_file(sourcedir, 'e.txt', mode=0o644) - create_test_file(sourcedir, 'f.txt', mode=0o4755) - create_test_file(sourcedir, 'g.txt', mode=0o2755) - create_test_file(sourcedir, 'h.txt', mode=0o1755) - create_test_directory(sourcedir, 'dir-a', mode=0o0700) - create_test_directory(sourcedir, 'dir-c', mode=0o0755) - create_test_directory(sourcedir, 'dir-d', mode=0o4755) - create_test_directory(sourcedir, 'dir-e', mode=0o2755) - create_test_directory(sourcedir, 'dir-f', mode=0o1755) - - if kind == 'local': - source = {'kind': 'local', - 'path': 'source'} - else: - repo = create_repo(kind, repodir) - ref = repo.create(sourcedir) - source = repo.source_config(ref=ref) - element = { - 'kind': 'manual', - 'depends': [ - { - 'filename': 'base.bst', - 'type': 'build' - } - ], - 'sources': [ - source - ], - 'config': { - 'install-commands': [ - 'ls -l >"%{install-root}/ls-l"' - ] - } - } - _yaml.dump(element, element_path) - - def get_value_for_umask(umask): - checkoutdir = os.path.join(str(tmpdir), 'checkout-{}'.format(umask)) - - old_umask = os.umask(umask) - - try: - result = cli.run(project=project, args=['build', element_name]) - result.assert_success() - - result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkoutdir]) - result.assert_success() - - with open(os.path.join(checkoutdir, 'ls-l'), 'r') as f: - return f.read() - finally: - os.umask(old_umask) - cli.remove_artifact_from_cache(project, element_name) - - assert get_value_for_umask(0o022) == get_value_for_umask(0o077) diff --git a/buildstream/plugintestutils/_sourcetests/track.py b/buildstream/plugintestutils/_sourcetests/track.py deleted file mode 100644 index 668ea29e5..000000000 --- a/buildstream/plugintestutils/_sourcetests/track.py +++ /dev/null @@ -1,420 +0,0 @@ -# -# Copyright (C) 2018 Codethink Limited -# Copyright (C) 2019 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 . -# - -# Pylint doesn't play well with fixtures and dependency injection from pytest -# pylint: disable=redefined-outer-name - -import os -import pytest - -from buildstream import _yaml -from buildstream._exceptions import ErrorDomain -from .._utils import generate_junction, configure_project -from .. import create_repo, ALL_REPO_KINDS -from .. import cli # pylint: disable=unused-import - -# Project directory -TOP_DIR = os.path.dirname(os.path.realpath(__file__)) -DATA_DIR = os.path.join(TOP_DIR, 'project') - - -def generate_element(repo, element_path, dep_name=None): - element = { - 'kind': 'import', - 'sources': [ - repo.source_config() - ] - } - if dep_name: - element['depends'] = [dep_name] - - _yaml.dump(element, element_path) - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_track(cli, tmpdir, datafiles, ref_storage, kind): - project = str(datafiles) - dev_files_path = os.path.join(project, 'files', 'dev-files') - element_path = os.path.join(project, 'elements') - element_name = 'track-test-{}.bst'.format(kind) - - configure_project(project, { - 'ref-storage': ref_storage - }) - - # Create our repo object of the given source type with - # the dev files, and then collect the initial ref. - # - repo = create_repo(kind, str(tmpdir)) - repo.create(dev_files_path) - - # Generate the element - generate_element(repo, os.path.join(element_path, element_name)) - - # Assert that a fetch is needed - assert cli.get_element_state(project, element_name) == 'no reference' - - # Now first try to track it - result = cli.run(project=project, args=['source', 'track', element_name]) - result.assert_success() - - # And now fetch it: The Source has probably already cached the - # latest ref locally, but it is not required to have cached - # the associated content of the latest ref at track time, that - # is the job of fetch. - result = cli.run(project=project, args=['source', 'fetch', element_name]) - result.assert_success() - - # Assert that we are now buildable because the source is - # now cached. - assert cli.get_element_state(project, element_name) == 'buildable' - - # Assert there was a project.refs created, depending on the configuration - if ref_storage == 'project.refs': - assert os.path.exists(os.path.join(project, 'project.refs')) - else: - assert not os.path.exists(os.path.join(project, 'project.refs')) - - -# NOTE: -# -# This test checks that recursive tracking works by observing -# element states after running a recursive tracking operation. -# -# However, this test is ALSO valuable as it stresses the source -# plugins in a situation where many source plugins are operating -# at once on the same backing repository. -# -# Do not change this test to use a separate 'Repo' per element -# as that would defeat the purpose of the stress test, otherwise -# please refactor that aspect into another test. -# -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("amount", [(1), (10)]) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_track_recurse(cli, tmpdir, datafiles, kind, amount): - project = str(datafiles) - dev_files_path = os.path.join(project, 'files', 'dev-files') - element_path = os.path.join(project, 'elements') - - # Try to actually launch as many fetch jobs as possible at the same time - # - # This stresses the Source plugins and helps to ensure that - # they handle concurrent access to the store correctly. - cli.configure({ - 'scheduler': { - 'fetchers': amount, - } - }) - - # Create our repo object of the given source type with - # the dev files, and then collect the initial ref. - # - repo = create_repo(kind, str(tmpdir)) - repo.create(dev_files_path) - - # Write out our test targets - element_names = [] - last_element_name = None - for i in range(amount + 1): - element_name = 'track-test-{}-{}.bst'.format(kind, i + 1) - filename = os.path.join(element_path, element_name) - - element_names.append(element_name) - - generate_element(repo, filename, dep_name=last_element_name) - last_element_name = element_name - - # Assert that a fetch is needed - states = cli.get_element_states(project, [last_element_name]) - for element_name in element_names: - assert states[element_name] == 'no reference' - - # Now first try to track it - result = cli.run(project=project, args=[ - 'source', 'track', '--deps', 'all', - last_element_name]) - result.assert_success() - - # And now fetch it: The Source has probably already cached the - # latest ref locally, but it is not required to have cached - # the associated content of the latest ref at track time, that - # is the job of fetch. - result = cli.run(project=project, args=[ - 'source', 'fetch', '--deps', 'all', - last_element_name]) - result.assert_success() - - # Assert that the base is buildable and the rest are waiting - states = cli.get_element_states(project, [last_element_name]) - for element_name in element_names: - if element_name == element_names[0]: - assert states[element_name] == 'buildable' - else: - assert states[element_name] == 'waiting' - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_track_recurse_except(cli, tmpdir, datafiles, kind): - project = str(datafiles) - dev_files_path = os.path.join(project, 'files', 'dev-files') - element_path = os.path.join(project, 'elements') - element_dep_name = 'track-test-dep-{}.bst'.format(kind) - element_target_name = 'track-test-target-{}.bst'.format(kind) - - # Create our repo object of the given source type with - # the dev files, and then collect the initial ref. - # - repo = create_repo(kind, str(tmpdir)) - repo.create(dev_files_path) - - # Write out our test targets - generate_element(repo, os.path.join(element_path, element_dep_name)) - generate_element(repo, os.path.join(element_path, element_target_name), - dep_name=element_dep_name) - - # Assert that a fetch is needed - states = cli.get_element_states(project, [element_target_name]) - assert states[element_dep_name] == 'no reference' - assert states[element_target_name] == 'no reference' - - # Now first try to track it - result = cli.run(project=project, args=[ - 'source', 'track', '--deps', 'all', '--except', element_dep_name, - element_target_name]) - result.assert_success() - - # And now fetch it: The Source has probably already cached the - # latest ref locally, but it is not required to have cached - # the associated content of the latest ref at track time, that - # is the job of fetch. - result = cli.run(project=project, args=[ - 'source', 'fetch', '--deps', 'none', - element_target_name]) - result.assert_success() - - # Assert that the dependency is buildable and the target is waiting - states = cli.get_element_states(project, [element_target_name]) - assert states[element_dep_name] == 'no reference' - assert states[element_target_name] == 'waiting' - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_cross_junction(cli, tmpdir, datafiles, ref_storage, kind): - project = str(datafiles) - subproject_path = os.path.join(project, 'files', 'sub-project') - junction_path = os.path.join(project, 'elements', 'junction.bst') - etc_files = os.path.join(subproject_path, 'files', 'etc-files') - repo_element_path = os.path.join(subproject_path, 'elements', - 'import-etc-repo.bst') - - configure_project(project, { - 'ref-storage': ref_storage - }) - - repo = create_repo(kind, str(tmpdir.join('element_repo'))) - repo.create(etc_files) - - generate_element(repo, repo_element_path) - - generate_junction(str(tmpdir.join('junction_repo')), - subproject_path, junction_path, store_ref=False) - - # Track the junction itself first. - result = cli.run(project=project, args=['source', 'track', 'junction.bst']) - result.assert_success() - - assert cli.get_element_state(project, 'junction.bst:import-etc-repo.bst') == 'no reference' - - # Track the cross junction element. -J is not given, it is implied. - result = cli.run(project=project, args=['source', 'track', 'junction.bst:import-etc-repo.bst']) - - if ref_storage == 'inline': - # This is not allowed to track cross junction without project.refs. - result.assert_main_error(ErrorDomain.PIPELINE, 'untrackable-sources') - else: - result.assert_success() - - assert cli.get_element_state(project, 'junction.bst:import-etc-repo.bst') == 'buildable' - - assert os.path.exists(os.path.join(project, 'project.refs')) - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_track_include(cli, tmpdir, datafiles, ref_storage, kind): - project = str(datafiles) - dev_files_path = os.path.join(project, 'files', 'dev-files') - element_path = os.path.join(project, 'elements') - element_name = 'track-test-{}.bst'.format(kind) - - configure_project(project, { - 'ref-storage': ref_storage - }) - - # Create our repo object of the given source type with - # the dev files, and then collect the initial ref. - # - repo = create_repo(kind, str(tmpdir)) - ref = repo.create(dev_files_path) - - # Generate the element - element = { - 'kind': 'import', - '(@)': ['elements/sources.yml'] - } - sources = { - 'sources': [ - repo.source_config() - ] - } - - _yaml.dump(element, os.path.join(element_path, element_name)) - _yaml.dump(sources, os.path.join(element_path, 'sources.yml')) - - # Assert that a fetch is needed - assert cli.get_element_state(project, element_name) == 'no reference' - - # Now first try to track it - result = cli.run(project=project, args=['source', 'track', element_name]) - result.assert_success() - - # And now fetch it: The Source has probably already cached the - # latest ref locally, but it is not required to have cached - # the associated content of the latest ref at track time, that - # is the job of fetch. - result = cli.run(project=project, args=['source', 'fetch', element_name]) - result.assert_success() - - # Assert that we are now buildable because the source is - # now cached. - assert cli.get_element_state(project, element_name) == 'buildable' - - # Assert there was a project.refs created, depending on the configuration - if ref_storage == 'project.refs': - assert os.path.exists(os.path.join(project, 'project.refs')) - else: - assert not os.path.exists(os.path.join(project, 'project.refs')) - - new_sources = _yaml.load(os.path.join(element_path, 'sources.yml')) - - # Get all of the sources - assert 'sources' in new_sources - sources_list = _yaml.node_get(new_sources, list, 'sources') - assert len(sources_list) == 1 - - # Get the first source from the sources list - new_source = _yaml.node_get(new_sources, dict, 'sources', indices=[0]) - assert 'ref' in new_source - assert ref == _yaml.node_get(new_source, str, 'ref') - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_track_include_junction(cli, tmpdir, datafiles, ref_storage, kind): - project = str(datafiles) - dev_files_path = os.path.join(project, 'files', 'dev-files') - element_path = os.path.join(project, 'elements') - element_name = 'track-test-{}.bst'.format(kind) - subproject_path = os.path.join(project, 'files', 'sub-project') - sub_element_path = os.path.join(subproject_path, 'elements') - junction_path = os.path.join(element_path, 'junction.bst') - - configure_project(project, { - 'ref-storage': ref_storage - }) - - # Create our repo object of the given source type with - # the dev files, and then collect the initial ref. - # - repo = create_repo(kind, str(tmpdir.join('element_repo'))) - repo.create(dev_files_path) - - # Generate the element - element = { - 'kind': 'import', - '(@)': ['junction.bst:elements/sources.yml'] - } - sources = { - 'sources': [ - repo.source_config() - ] - } - - _yaml.dump(element, os.path.join(element_path, element_name)) - _yaml.dump(sources, os.path.join(sub_element_path, 'sources.yml')) - - generate_junction(str(tmpdir.join('junction_repo')), - subproject_path, junction_path, store_ref=True) - - result = cli.run(project=project, args=['source', 'track', 'junction.bst']) - result.assert_success() - - # Assert that a fetch is needed - assert cli.get_element_state(project, element_name) == 'no reference' - - # Now first try to track it - result = cli.run(project=project, args=['source', 'track', element_name]) - - # Assert there was a project.refs created, depending on the configuration - if ref_storage == 'inline': - # FIXME: We should expect an error. But only a warning is emitted - # result.assert_main_error(ErrorDomain.SOURCE, 'tracking-junction-fragment') - - assert 'junction.bst:elements/sources.yml: Cannot track source in a fragment from a junction' in result.stderr - else: - assert os.path.exists(os.path.join(project, 'project.refs')) - - # And now fetch it: The Source has probably already cached the - # latest ref locally, but it is not required to have cached - # the associated content of the latest ref at track time, that - # is the job of fetch. - result = cli.run(project=project, args=['source', 'fetch', element_name]) - result.assert_success() - - # Assert that we are now buildable because the source is - # now cached. - assert cli.get_element_state(project, element_name) == 'buildable' - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_track_junction_included(cli, tmpdir, datafiles, ref_storage, kind): - project = str(datafiles) - element_path = os.path.join(project, 'elements') - subproject_path = os.path.join(project, 'files', 'sub-project') - junction_path = os.path.join(element_path, 'junction.bst') - - configure_project(project, { - 'ref-storage': ref_storage, - '(@)': ['junction.bst:test.yml'] - }) - - generate_junction(str(tmpdir.join('junction_repo')), - subproject_path, junction_path, store_ref=False) - - result = cli.run(project=project, args=['source', 'track', 'junction.bst']) - result.assert_success() diff --git a/buildstream/plugintestutils/_sourcetests/track_cross_junction.py b/buildstream/plugintestutils/_sourcetests/track_cross_junction.py deleted file mode 100644 index ece3e0b8f..000000000 --- a/buildstream/plugintestutils/_sourcetests/track_cross_junction.py +++ /dev/null @@ -1,186 +0,0 @@ -# -# Copyright (C) 2018 Codethink Limited -# Copyright (C) 2019 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 . -# - -# Pylint doesn't play well with fixtures and dependency injection from pytest -# pylint: disable=redefined-outer-name - -import os -import pytest - -from buildstream import _yaml -from .._utils import generate_junction -from .. import create_repo, ALL_REPO_KINDS -from .. import cli # pylint: disable=unused-import - -# Project directory -TOP_DIR = os.path.dirname(os.path.realpath(__file__)) -DATA_DIR = os.path.join(TOP_DIR, 'project') - - -def generate_element(repo, element_path, dep_name=None): - element = { - 'kind': 'import', - 'sources': [ - repo.source_config() - ] - } - if dep_name: - element['depends'] = [dep_name] - - _yaml.dump(element, element_path) - - -def generate_import_element(tmpdir, kind, project, name): - element_name = 'import-{}.bst'.format(name) - repo_element_path = os.path.join(project, 'elements', element_name) - files = str(tmpdir.join("imported_files_{}".format(name))) - os.makedirs(files) - - with open(os.path.join(files, '{}.txt'.format(name)), 'w') as f: - f.write(name) - - repo = create_repo(kind, str(tmpdir.join('element_{}_repo'.format(name)))) - repo.create(files) - - generate_element(repo, repo_element_path) - - return element_name - - -def generate_project(tmpdir, name, config=None): - if config is None: - config = {} - - project_name = 'project-{}'.format(name) - subproject_path = os.path.join(str(tmpdir.join(project_name))) - os.makedirs(os.path.join(subproject_path, 'elements')) - - project_conf = { - 'name': name, - 'element-path': 'elements' - } - project_conf.update(config) - _yaml.dump(project_conf, os.path.join(subproject_path, 'project.conf')) - - return project_name, subproject_path - - -def generate_simple_stack(project, name, dependencies): - element_name = '{}.bst'.format(name) - element_path = os.path.join(project, 'elements', element_name) - element = { - 'kind': 'stack', - 'depends': dependencies - } - _yaml.dump(element, element_path) - - return element_name - - -def generate_cross_element(project, subproject_name, import_name): - basename, _ = os.path.splitext(import_name) - return generate_simple_stack(project, 'import-{}-{}'.format(subproject_name, basename), - [{ - 'junction': '{}.bst'.format(subproject_name), - 'filename': import_name - }]) - - -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_cross_junction_multiple_projects(cli, tmpdir, kind): - tmpdir = tmpdir.join(kind) - - # Generate 3 projects: main, a, b - _, project = generate_project(tmpdir, 'main', {'ref-storage': 'project.refs'}) - project_a, project_a_path = generate_project(tmpdir, 'a') - project_b, project_b_path = generate_project(tmpdir, 'b') - - # Generate an element with a trackable source for each project - element_a = generate_import_element(tmpdir, kind, project_a_path, 'a') - element_b = generate_import_element(tmpdir, kind, project_b_path, 'b') - element_c = generate_import_element(tmpdir, kind, project, 'c') - - # Create some indirections to the elements with dependencies to test --deps - stack_a = generate_simple_stack(project_a_path, 'stack-a', [element_a]) - stack_b = generate_simple_stack(project_b_path, 'stack-b', [element_b]) - - # Create junctions for projects a and b in main. - junction_a = '{}.bst'.format(project_a) - junction_a_path = os.path.join(project, 'elements', junction_a) - generate_junction(tmpdir.join('repo_a'), project_a_path, junction_a_path, store_ref=False) - - junction_b = '{}.bst'.format(project_b) - junction_b_path = os.path.join(project, 'elements', junction_b) - generate_junction(tmpdir.join('repo_b'), project_b_path, junction_b_path, store_ref=False) - - # Track the junctions. - result = cli.run(project=project, args=['source', 'track', junction_a, junction_b]) - result.assert_success() - - # Import elements from a and b in to main. - imported_a = generate_cross_element(project, project_a, stack_a) - imported_b = generate_cross_element(project, project_b, stack_b) - - # Generate a top level stack depending on everything - all_bst = generate_simple_stack(project, 'all', [imported_a, imported_b, element_c]) - - # Track without following junctions. But explicitly also track the elements in project a. - result = cli.run(project=project, args=['source', 'track', - '--deps', 'all', - all_bst, - '{}:{}'.format(junction_a, stack_a)]) - result.assert_success() - - # Elements in project b should not be tracked. But elements in project a and main should. - expected = [element_c, - '{}:{}'.format(junction_a, element_a)] - assert set(result.get_tracked_elements()) == set(expected) - - -@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) -def test_track_exceptions(cli, tmpdir, kind): - tmpdir = tmpdir.join(kind) - - _, project = generate_project(tmpdir, 'main', {'ref-storage': 'project.refs'}) - project_a, project_a_path = generate_project(tmpdir, 'a') - - element_a = generate_import_element(tmpdir, kind, project_a_path, 'a') - element_b = generate_import_element(tmpdir, kind, project_a_path, 'b') - - all_bst = generate_simple_stack(project_a_path, 'all', [element_a, - element_b]) - - junction_a = '{}.bst'.format(project_a) - junction_a_path = os.path.join(project, 'elements', junction_a) - generate_junction(tmpdir.join('repo_a'), project_a_path, junction_a_path, store_ref=False) - - result = cli.run(project=project, args=['source', 'track', junction_a]) - result.assert_success() - - imported_b = generate_cross_element(project, project_a, element_b) - indirection = generate_simple_stack(project, 'indirection', [imported_b]) - - result = cli.run(project=project, - args=['source', 'track', '--deps', 'all', - '--except', indirection, - '{}:{}'.format(junction_a, all_bst), imported_b]) - result.assert_success() - - expected = ['{}:{}'.format(junction_a, element_a), - '{}:{}'.format(junction_a, element_b)] - assert set(result.get_tracked_elements()) == set(expected) diff --git a/buildstream/plugintestutils/_sourcetests/workspace.py b/buildstream/plugintestutils/_sourcetests/workspace.py deleted file mode 100644 index 5218f8f1e..000000000 --- a/buildstream/plugintestutils/_sourcetests/workspace.py +++ /dev/null @@ -1,161 +0,0 @@ -# -# Copyright (C) 2018 Codethink Limited -# Copyright (C) 2019 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 . -# - -# Pylint doesn't play well with fixtures and dependency injection from pytest -# pylint: disable=redefined-outer-name - -import os -import shutil -import pytest - -from buildstream import _yaml -from .. import create_repo, ALL_REPO_KINDS -from .. import cli # pylint: disable=unused-import - -# Project directory -TOP_DIR = os.path.dirname(os.path.realpath(__file__)) -DATA_DIR = os.path.join(TOP_DIR, 'project') - - -class WorkspaceCreator(): - def __init__(self, cli, tmpdir, datafiles, project_path=None): - self.cli = cli - self.tmpdir = tmpdir - self.datafiles = datafiles - - if not project_path: - project_path = str(datafiles) - else: - shutil.copytree(str(datafiles), project_path) - - self.project_path = project_path - self.bin_files_path = os.path.join(project_path, 'files', 'bin-files') - - self.workspace_cmd = os.path.join(self.project_path, 'workspace_cmd') - - def create_workspace_element(self, kind, track, suffix='', workspace_dir=None, - element_attrs=None): - element_name = 'workspace-test-{}{}.bst'.format(kind, suffix) - element_path = os.path.join(self.project_path, 'elements') - if not workspace_dir: - workspace_dir = os.path.join(self.workspace_cmd, element_name) - if workspace_dir[-4:] == '.bst': - workspace_dir = workspace_dir[:-4] - - # Create our repo object of the given source type with - # the bin files, and then collect the initial ref. - repo = create_repo(kind, str(self.tmpdir)) - ref = repo.create(self.bin_files_path) - if track: - ref = None - - # Write out our test target - element = { - 'kind': 'import', - 'sources': [ - repo.source_config(ref=ref) - ] - } - if element_attrs: - element = {**element, **element_attrs} - _yaml.dump(element, - os.path.join(element_path, - element_name)) - return element_name, element_path, workspace_dir - - def create_workspace_elements(self, kinds, track, suffixs=None, workspace_dir_usr=None, - element_attrs=None): - - element_tuples = [] - - if suffixs is None: - suffixs = ['', ] * len(kinds) - else: - if len(suffixs) != len(kinds): - raise "terable error" - - for suffix, kind in zip(suffixs, kinds): - element_name, _, workspace_dir = \ - self.create_workspace_element(kind, track, suffix, workspace_dir_usr, - element_attrs) - element_tuples.append((element_name, workspace_dir)) - - # Assert that there is no reference, a track & fetch is needed - states = self.cli.get_element_states(self.project_path, [ - e for e, _ in element_tuples - ]) - if track: - assert not any(states[e] != 'no reference' for e, _ in element_tuples) - else: - assert not any(states[e] != 'fetch needed' for e, _ in element_tuples) - - return element_tuples - - def open_workspaces(self, kinds, track, suffixs=None, workspace_dir=None, - element_attrs=None, no_checkout=False): - - element_tuples = self.create_workspace_elements(kinds, track, suffixs, workspace_dir, - element_attrs) - os.makedirs(self.workspace_cmd, exist_ok=True) - - # Now open the workspace, this should have the effect of automatically - # tracking & fetching the source from the repo. - args = ['workspace', 'open'] - if track: - args.append('--track') - if no_checkout: - args.append('--no-checkout') - if workspace_dir is not None: - assert len(element_tuples) == 1, "test logic error" - _, workspace_dir = element_tuples[0] - args.extend(['--directory', workspace_dir]) - - args.extend([element_name for element_name, workspace_dir_suffix in element_tuples]) - result = self.cli.run(cwd=self.workspace_cmd, project=self.project_path, args=args) - - result.assert_success() - - if not no_checkout: - # Assert that we are now buildable because the source is now cached. - states = self.cli.get_element_states(self.project_path, [ - e for e, _ in element_tuples - ]) - assert not any(states[e] != 'buildable' for e, _ in element_tuples) - - # Check that the executable hello file is found in each workspace - for _, workspace in element_tuples: - filename = os.path.join(workspace, 'usr', 'bin', 'hello') - assert os.path.exists(filename) - - return element_tuples - - -def open_workspace(cli, tmpdir, datafiles, kind, track, suffix='', workspace_dir=None, - project_path=None, element_attrs=None, no_checkout=False): - workspace_object = WorkspaceCreator(cli, tmpdir, datafiles, project_path) - workspaces = workspace_object.open_workspaces((kind, ), track, (suffix, ), workspace_dir, - element_attrs, no_checkout) - assert len(workspaces) == 1 - element_name, workspace = workspaces[0] - return element_name, workspace_object.project_path, workspace - - -@pytest.mark.datafiles(DATA_DIR) -@pytest.mark.parametrize("kind", ALL_REPO_KINDS) -def test_open(cli, tmpdir, datafiles, kind): - open_workspace(cli, tmpdir, datafiles, kind, False) diff --git a/buildstream/plugintestutils/_utils/__init__.py b/buildstream/plugintestutils/_utils/__init__.py deleted file mode 100644 index b419d72b7..000000000 --- a/buildstream/plugintestutils/_utils/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -import os - -from buildstream import _yaml -from .junction import generate_junction - - -def configure_project(path, config): - config['name'] = 'test' - config['element-path'] = 'elements' - _yaml.dump(config, os.path.join(path, 'project.conf')) diff --git a/buildstream/plugintestutils/_utils/junction.py b/buildstream/plugintestutils/_utils/junction.py deleted file mode 100644 index ca059eb8b..000000000 --- a/buildstream/plugintestutils/_utils/junction.py +++ /dev/null @@ -1,83 +0,0 @@ -import subprocess -import pytest - -from buildstream import _yaml -from .. import Repo -from .site import HAVE_GIT, GIT, GIT_ENV - - -# generate_junction() -# -# Generates a junction element with a git repository -# -# Args: -# tmpdir: The tmpdir fixture, for storing the generated git repo -# subproject_path: The path for the subproject, to add to the git repo -# junction_path: The location to store the generated junction element -# store_ref: Whether to store the ref in the junction.bst file -# -# Returns: -# (str): The ref -# -def generate_junction(tmpdir, subproject_path, junction_path, *, store_ref=True): - # Create a repo to hold the subproject and generate - # a junction element for it - # - repo = _SimpleGit(str(tmpdir)) - source_ref = ref = repo.create(subproject_path) - if not store_ref: - source_ref = None - - element = { - 'kind': 'junction', - 'sources': [ - repo.source_config(ref=source_ref) - ] - } - _yaml.dump(element, junction_path) - - return ref - - -# A barebones Git Repo class to use for generating junctions -class _SimpleGit(Repo): - def __init__(self, directory, subdir='repo'): - if not HAVE_GIT: - pytest.skip('git is not available') - super().__init__(directory, subdir) - - def create(self, directory): - self.copy_directory(directory, self.repo) - self._run_git('init', '.') - self._run_git('add', '.') - self._run_git('commit', '-m', 'Initial commit') - return self.latest_commit() - - def latest_commit(self): - return self._run_git( - 'rev-parse', 'HEAD', - stdout=subprocess.PIPE, - universal_newlines=True, - ).stdout.strip() - - def source_config(self, ref=None, checkout_submodules=None): - config = { - 'kind': 'git', - 'url': 'file://' + self.repo, - 'track': 'master' - } - if ref is not None: - config['ref'] = ref - if checkout_submodules is not None: - config['checkout-submodules'] = checkout_submodules - - return config - - def _run_git(self, *args, **kwargs): - argv = [GIT] - argv.extend(args) - if 'env' not in kwargs: - kwargs['env'] = dict(GIT_ENV, PWD=self.repo) - kwargs.setdefault('cwd', self.repo) - kwargs.setdefault('check', True) - return subprocess.run(argv, **kwargs) diff --git a/buildstream/plugintestutils/_utils/site.py b/buildstream/plugintestutils/_utils/site.py deleted file mode 100644 index 54c5b467b..000000000 --- a/buildstream/plugintestutils/_utils/site.py +++ /dev/null @@ -1,46 +0,0 @@ -# Some things resolved about the execution site, -# so we dont have to repeat this everywhere -# -import os -import sys -import platform - -from buildstream import _site, utils, ProgramNotFoundError - - -try: - GIT = utils.get_host_tool('git') - HAVE_GIT = True - GIT_ENV = { - 'GIT_AUTHOR_DATE': '1320966000 +0200', - 'GIT_AUTHOR_NAME': 'tomjon', - 'GIT_AUTHOR_EMAIL': 'tom@jon.com', - 'GIT_COMMITTER_DATE': '1320966000 +0200', - 'GIT_COMMITTER_NAME': 'tomjon', - 'GIT_COMMITTER_EMAIL': 'tom@jon.com' - } -except ProgramNotFoundError: - GIT = None - HAVE_GIT = False - GIT_ENV = dict() - -try: - utils.get_host_tool('bwrap') - HAVE_BWRAP = True - HAVE_BWRAP_JSON_STATUS = _site.get_bwrap_version() >= (0, 3, 2) -except ProgramNotFoundError: - HAVE_BWRAP = False - HAVE_BWRAP_JSON_STATUS = False - -IS_LINUX = os.getenv('BST_FORCE_BACKEND', sys.platform).startswith('linux') -IS_WSL = (IS_LINUX and 'Microsoft' in platform.uname().release) -IS_WINDOWS = (os.name == 'nt') - -if not IS_LINUX: - HAVE_SANDBOX = True # fallback to a chroot sandbox on unix -elif IS_WSL: - HAVE_SANDBOX = False # Sandboxes are inoperable under WSL due to lack of FUSE -elif IS_LINUX and HAVE_BWRAP: - HAVE_SANDBOX = True -else: - HAVE_SANDBOX = False diff --git a/buildstream/plugintestutils/integration.py b/buildstream/plugintestutils/integration.py deleted file mode 100644 index e29f480ea..000000000 --- a/buildstream/plugintestutils/integration.py +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright (C) 2017 Codethink Limited -# Copyright (C) 2018 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 . -""" -Integration - tools for inspecting the output of plugin integration tests -========================================================================= - -This module contains utilities for inspecting the artifacts produced during -integration tests. -""" - -import os - - -# Return a list of files relative to the given directory -def walk_dir(root): - for dirname, dirnames, filenames in os.walk(root): - # ensure consistent traversal order, needed for consistent - # handling of symlinks. - dirnames.sort() - filenames.sort() - - # print path to all subdirectories first. - for subdirname in dirnames: - yield os.path.join(dirname, subdirname)[len(root):] - - # print path to all filenames. - for filename in filenames: - yield os.path.join(dirname, filename)[len(root):] - - -# Ensure that a directory contains the given filenames. -def assert_contains(directory, expected): - missing = set(expected) - missing.difference_update(walk_dir(directory)) - if missing: - raise AssertionError("Missing {} expected elements from list: {}" - .format(len(missing), missing)) diff --git a/buildstream/plugintestutils/repo.py b/buildstream/plugintestutils/repo.py deleted file mode 100644 index e3293ea75..000000000 --- a/buildstream/plugintestutils/repo.py +++ /dev/null @@ -1,109 +0,0 @@ -# -# Copyright (C) 2016-2018 Codethink Limited -# Copyright (C) 2019 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 . - -""" -Repo - Utility class for testing source plugins -=============================================== - - -""" -import os -import shutil - - -class Repo(): - """Repo() - - Abstract class providing scaffolding for generating data to be - used with various sources. Subclasses of Repo may be registered to - run through the suite of generic source plugin tests provided in - buildstream.plugintestutils. - - Args: - directory (str): The base temp directory for the test - subdir (str): The subdir for the repo, in case there is more than one - - """ - def __init__(self, directory, subdir='repo'): - - # The working directory for the repo object - # - self.directory = os.path.abspath(directory) - - # The directory the actual repo will be stored in - self.repo = os.path.join(self.directory, subdir) - - os.makedirs(self.repo, exist_ok=True) - - def create(self, directory): - """Create a repository in self.directory and add the initial content - - Args: - directory: A directory with content to commit - - Returns: - (smth): A new ref corresponding to this commit, which can - be passed as the ref in the Repo.source_config() API. - """ - raise NotImplementedError("create method has not been implemeted") - - def source_config(self, ref=None): - """ - Args: - ref (smth): An optional abstract ref object, usually a string. - - Returns: - (dict): A configuration which can be serialized as a - source when generating an element file on the fly - - """ - raise NotImplementedError("source_config method has not been implemeted") - - def copy_directory(self, src, dest): - """ Copies the content of src to the directory dest - - Like shutil.copytree(), except dest is expected - to exist. - - Args: - src (str): The source directory - dest (str): The destination directory - """ - for filename in os.listdir(src): - src_path = os.path.join(src, filename) - dest_path = os.path.join(dest, filename) - if os.path.isdir(src_path): - shutil.copytree(src_path, dest_path) - else: - shutil.copy2(src_path, dest_path) - - def copy(self, dest): - """Creates a copy of this repository in the specified destination. - - Args: - dest (str): The destination directory - - Returns: - (Repo): A Repo object for the new repository. - """ - subdir = self.repo[len(self.directory):].lstrip(os.sep) - new_dir = os.path.join(dest, subdir) - os.makedirs(new_dir, exist_ok=True) - self.copy_directory(self.repo, new_dir) - repo_type = type(self) - new_repo = repo_type(dest, subdir) - return new_repo diff --git a/buildstream/plugintestutils/runcli.py b/buildstream/plugintestutils/runcli.py deleted file mode 100644 index 72bdce09e..000000000 --- a/buildstream/plugintestutils/runcli.py +++ /dev/null @@ -1,857 +0,0 @@ -# -# Copyright (C) 2017 Codethink Limited -# Copyright (C) 2018 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 . -""" -runcli - Test fixtures used for running BuildStream commands -============================================================ - -:function:'cli' Use result = cli.run([arg1, arg2]) to run buildstream commands - -:function:'cli_integration' A variant of the main fixture that keeps persistent - artifact and source caches. It also does not use - the click test runner to avoid deadlock issues when - running `bst shell`, but unfortunately cannot produce - nice stacktraces. - -""" - - -import os -import re -import sys -import shutil -import tempfile -import itertools -import traceback -from contextlib import contextmanager, ExitStack -from ruamel import yaml -import pytest - -# XXX Using pytest private internals here -# -# We use pytest internals to capture the stdout/stderr during -# a run of the buildstream CLI. We do this because click's -# CliRunner convenience API (click.testing module) does not support -# separation of stdout/stderr. -# -from _pytest.capture import MultiCapture, FDCapture, FDCaptureBinary - -# Import the main cli entrypoint -from buildstream._frontend import cli as bst_cli -from buildstream import _yaml -from buildstream._cas import CASCache - -# Special private exception accessor, for test case purposes -from buildstream._exceptions import BstError, get_last_exception, get_last_task_error - - -# Wrapper for the click.testing result -class Result(): - - def __init__(self, - exit_code=None, - exception=None, - exc_info=None, - output=None, - stderr=None): - self.exit_code = exit_code - self.exc = exception - self.exc_info = exc_info - self.output = output - self.stderr = stderr - self.unhandled_exception = False - - # The last exception/error state is stored at exception - # creation time in BstError(), but this breaks down with - # recoverable errors where code blocks ignore some errors - # and fallback to alternative branches. - # - # For this reason, we just ignore the exception and errors - # in the case that the exit code reported is 0 (success). - # - if self.exit_code != 0: - - # Check if buildstream failed to handle an - # exception, topevel CLI exit should always - # be a SystemExit exception. - # - if not isinstance(exception, SystemExit): - self.unhandled_exception = True - - self.exception = get_last_exception() - self.task_error_domain, \ - self.task_error_reason = get_last_task_error() - else: - self.exception = None - self.task_error_domain = None - self.task_error_reason = None - - # assert_success() - # - # Asserts that the buildstream session completed successfully - # - # Args: - # fail_message (str): An optional message to override the automatic - # assertion error messages - # Raises: - # (AssertionError): If the session did not complete successfully - # - def assert_success(self, fail_message=''): - assert self.exit_code == 0, fail_message - assert self.exc is None, fail_message - assert self.exception is None, fail_message - assert self.unhandled_exception is False - - # assert_main_error() - # - # Asserts that the buildstream session failed, and that - # the main process error report is as expected - # - # Args: - # error_domain (ErrorDomain): The domain of the error which occurred - # error_reason (any): The reason field of the error which occurred - # fail_message (str): An optional message to override the automatic - # assertion error messages - # debug (bool): If true, prints information regarding the exit state of the result() - # Raises: - # (AssertionError): If any of the assertions fail - # - def assert_main_error(self, - error_domain, - error_reason, - fail_message='', - *, debug=False): - if debug: - print( - """ - Exit code: {} - Exception: {} - Domain: {} - Reason: {} - """.format( - self.exit_code, - self.exception, - self.exception.domain, - self.exception.reason - )) - assert self.exit_code == -1, fail_message - assert self.exc is not None, fail_message - assert self.exception is not None, fail_message - assert isinstance(self.exception, BstError), fail_message - assert self.unhandled_exception is False - - assert self.exception.domain == error_domain, fail_message - assert self.exception.reason == error_reason, fail_message - - # assert_task_error() - # - # Asserts that the buildstream session failed, and that - # the child task error which caused buildstream to exit - # is as expected. - # - # Args: - # error_domain (ErrorDomain): The domain of the error which occurred - # error_reason (any): The reason field of the error which occurred - # fail_message (str): An optional message to override the automatic - # assertion error messages - # Raises: - # (AssertionError): If any of the assertions fail - # - def assert_task_error(self, - error_domain, - error_reason, - fail_message=''): - - assert self.exit_code == -1, fail_message - assert self.exc is not None, fail_message - assert self.exception is not None, fail_message - assert isinstance(self.exception, BstError), fail_message - assert self.unhandled_exception is False - - assert self.task_error_domain == error_domain, fail_message - assert self.task_error_reason == error_reason, fail_message - - # assert_shell_error() - # - # Asserts that the buildstream created a shell and that the task in the - # shell failed. - # - # Args: - # fail_message (str): An optional message to override the automatic - # assertion error messages - # Raises: - # (AssertionError): If any of the assertions fail - # - def assert_shell_error(self, fail_message=''): - assert self.exit_code == 1, fail_message - - # get_start_order() - # - # Gets the list of elements processed in a given queue, in the - # order of their first appearances in the session. - # - # Args: - # activity (str): The queue activity name (like 'fetch') - # - # Returns: - # (list): A list of element names in the order which they first appeared in the result - # - def get_start_order(self, activity): - results = re.findall(r'\[\s*{}:(\S+)\s*\]\s*START\s*.*\.log'.format(activity), self.stderr) - if results is None: - return [] - return list(results) - - # get_tracked_elements() - # - # Produces a list of element names on which tracking occurred - # during the session. - # - # This is done by parsing the buildstream stderr log - # - # Returns: - # (list): A list of element names - # - def get_tracked_elements(self): - tracked = re.findall(r'\[\s*track:(\S+)\s*]', self.stderr) - if tracked is None: - return [] - - return list(tracked) - - def get_pushed_elements(self): - pushed = re.findall(r'\[\s*push:(\S+)\s*\]\s*INFO\s*Pushed artifact', self.stderr) - if pushed is None: - return [] - - return list(pushed) - - def get_pulled_elements(self): - pulled = re.findall(r'\[\s*pull:(\S+)\s*\]\s*INFO\s*Pulled artifact', self.stderr) - if pulled is None: - return [] - - return list(pulled) - - -class Cli(): - - def __init__(self, directory, verbose=True, default_options=None): - self.directory = directory - self.config = None - self.verbose = verbose - self.artifact = TestArtifact() - - if default_options is None: - default_options = [] - - self.default_options = default_options - - # configure(): - # - # Serializes a user configuration into a buildstream.conf - # to use for this test cli. - # - # Args: - # config (dict): The user configuration to use - # - def configure(self, config): - if self.config is None: - self.config = {} - - for key, val in config.items(): - self.config[key] = val - - # remove_artifact_from_cache(): - # - # Remove given element artifact from artifact cache - # - # Args: - # project (str): The project path under test - # element_name (str): The name of the element artifact - # cache_dir (str): Specific cache dir to remove artifact from - # - def remove_artifact_from_cache(self, project, element_name, - *, cache_dir=None): - # Read configuration to figure out where artifacts are stored - if not cache_dir: - default = os.path.join(project, 'cache') - - if self.config is not None: - cache_dir = self.config.get('cachedir', default) - else: - cache_dir = default - - self.artifact.remove_artifact_from_cache(cache_dir, element_name) - - # run(): - # - # Runs buildstream with the given arguments, additionally - # also passes some global options to buildstream in order - # to stay contained in the testing environment. - # - # Args: - # configure (bool): Whether to pass a --config argument - # project (str): An optional path to a project - # silent (bool): Whether to pass --no-verbose - # env (dict): Environment variables to temporarily set during the test - # args (list): A list of arguments to pass buildstream - # binary_capture (bool): Whether to capture the stdout/stderr as binary - # - def run(self, configure=True, project=None, silent=False, env=None, - cwd=None, options=None, args=None, binary_capture=False): - if args is None: - args = [] - if options is None: - options = [] - - # We may have been passed e.g. pathlib.Path or py.path - args = [str(x) for x in args] - project = str(project) - - options = self.default_options + options - - with ExitStack() as stack: - bst_args = ['--no-colors'] - - if silent: - bst_args += ['--no-verbose'] - - if configure: - config_file = stack.enter_context( - configured(self.directory, self.config) - ) - bst_args += ['--config', config_file] - - if project: - bst_args += ['--directory', project] - - for option, value in options: - bst_args += ['--option', option, value] - - bst_args += args - - if cwd is not None: - stack.enter_context(chdir(cwd)) - - if env is not None: - stack.enter_context(environment(env)) - - # Ensure we have a working stdout - required to work - # around a bug that appears to cause AIX to close - # sys.__stdout__ after setup.py - try: - sys.__stdout__.fileno() - except ValueError: - sys.__stdout__ = open('/dev/stdout', 'w') - - result = self._invoke(bst_cli, bst_args, binary_capture=binary_capture) - - # Some informative stdout we can observe when anything fails - if self.verbose: - command = "bst " + " ".join(bst_args) - print("BuildStream exited with code {} for invocation:\n\t{}" - .format(result.exit_code, command)) - if result.output: - print("Program output was:\n{}".format(result.output)) - if result.stderr: - print("Program stderr was:\n{}".format(result.stderr)) - - if result.exc_info and result.exc_info[0] != SystemExit: - traceback.print_exception(*result.exc_info) - - return result - - def _invoke(self, cli_object, args=None, binary_capture=False): - exc_info = None - exception = None - exit_code = 0 - - # Temporarily redirect sys.stdin to /dev/null to ensure that - # Popen doesn't attempt to read pytest's dummy stdin. - old_stdin = sys.stdin - with open(os.devnull) as devnull: - sys.stdin = devnull - capture_kind = FDCaptureBinary if binary_capture else FDCapture - capture = MultiCapture(out=True, err=True, in_=False, Capture=capture_kind) - capture.start_capturing() - - try: - cli_object.main(args=args or (), prog_name=cli_object.name) - except SystemExit as e: - if e.code != 0: - exception = e - - exc_info = sys.exc_info() - - exit_code = e.code - if not isinstance(exit_code, int): - sys.stdout.write('Program exit code was not an integer: ') - sys.stdout.write(str(exit_code)) - sys.stdout.write('\n') - exit_code = 1 - except Exception as e: # pylint: disable=broad-except - exception = e - exit_code = -1 - exc_info = sys.exc_info() - finally: - sys.stdout.flush() - - sys.stdin = old_stdin - out, err = capture.readouterr() - capture.stop_capturing() - - return Result(exit_code=exit_code, - exception=exception, - exc_info=exc_info, - output=out, - stderr=err) - - # Fetch an element state by name by - # invoking bst show on the project with the CLI - # - # If you need to get the states of multiple elements, - # then use get_element_states(s) instead. - # - def get_element_state(self, project, element_name): - result = self.run(project=project, silent=True, args=[ - 'show', - '--deps', 'none', - '--format', '%{state}', - element_name - ]) - result.assert_success() - return result.output.strip() - - # Fetch the states of elements for a given target / deps - # - # Returns a dictionary with the element names as keys - # - def get_element_states(self, project, targets, deps='all'): - result = self.run(project=project, silent=True, args=[ - 'show', - '--deps', deps, - '--format', '%{name}||%{state}', - *targets - ]) - result.assert_success() - lines = result.output.splitlines() - states = {} - for line in lines: - split = line.split(sep='||') - states[split[0]] = split[1] - return states - - # Fetch an element's cache key by invoking bst show - # on the project with the CLI - # - def get_element_key(self, project, element_name): - result = self.run(project=project, silent=True, args=[ - 'show', - '--deps', 'none', - '--format', '%{full-key}', - element_name - ]) - result.assert_success() - return result.output.strip() - - # Get the decoded config of an element. - # - def get_element_config(self, project, element_name): - result = self.run(project=project, silent=True, args=[ - 'show', - '--deps', 'none', - '--format', '%{config}', - element_name - ]) - - result.assert_success() - return yaml.safe_load(result.output) - - # Fetch the elements that would be in the pipeline with the given - # arguments. - # - def get_pipeline(self, project, elements, except_=None, scope='plan'): - if except_ is None: - except_ = [] - - args = ['show', '--deps', scope, '--format', '%{name}'] - args += list(itertools.chain.from_iterable(zip(itertools.repeat('--except'), except_))) - - result = self.run(project=project, silent=True, args=args + elements) - result.assert_success() - return result.output.splitlines() - - -class CliIntegration(Cli): - - # run() - # - # This supports the same arguments as Cli.run() and additionally - # it supports the project_config keyword argument. - # - # This will first load the project.conf file from the specified - # project directory ('project' keyword argument) and perform substitutions - # of any {project_dir} specified in the existing project.conf. - # - # If the project_config parameter is specified, it is expected to - # be a dictionary of additional project configuration options, and - # will be composited on top of the already loaded project.conf - # - def run(self, *args, project_config=None, **kwargs): - - # First load the project.conf and substitute {project_dir} - # - # Save the original project.conf, because we will run more than - # once in the same temp directory - # - project_directory = kwargs['project'] - project_filename = os.path.join(project_directory, 'project.conf') - project_backup = os.path.join(project_directory, 'project.conf.backup') - project_load_filename = project_filename - - if not os.path.exists(project_backup): - shutil.copy(project_filename, project_backup) - else: - project_load_filename = project_backup - - with open(project_load_filename) as f: - config = f.read() - config = config.format(project_dir=project_directory) - - if project_config is not None: - - # If a custom project configuration dictionary was - # specified, composite it on top of the already - # substituted base project configuration - # - base_config = _yaml.load_data(config) - - # In order to leverage _yaml.composite_dict(), both - # dictionaries need to be loaded via _yaml.load_data() first - # - with tempfile.TemporaryDirectory(dir=project_directory) as scratchdir: - - temp_project = os.path.join(scratchdir, 'project.conf') - with open(temp_project, 'w') as f: - yaml.safe_dump(project_config, f) - - project_config = _yaml.load(temp_project) - - _yaml.composite_dict(base_config, project_config) - - base_config = _yaml.node_sanitize(base_config) - _yaml.dump(base_config, project_filename) - - else: - - # Otherwise, just dump it as is - with open(project_filename, 'w') as f: - f.write(config) - - return super().run(*args, **kwargs) - - -class CliRemote(CliIntegration): - - # ensure_services(): - # - # Make sure that required services are configured and that - # non-required ones are not. - # - # Args: - # actions (bool): Whether to use the 'action-cache' service - # artifacts (bool): Whether to use the 'artifact-cache' service - # execution (bool): Whether to use the 'execution' service - # sources (bool): Whether to use the 'source-cache' service - # storage (bool): Whether to use the 'storage' service - # - # Returns a list of configured services (by names). - # - def ensure_services(self, actions=True, execution=True, storage=True, - artifacts=False, sources=False): - # Build a list of configured services by name: - configured_services = [] - if not self.config: - return configured_services - - if 'remote-execution' in self.config: - rexec_config = self.config['remote-execution'] - - if 'action-cache-service' in rexec_config: - if actions: - configured_services.append('action-cache') - else: - rexec_config.pop('action-cache-service') - - if 'execution-service' in rexec_config: - if execution: - configured_services.append('execution') - else: - rexec_config.pop('execution-service') - - if 'storage-service' in rexec_config: - if storage: - configured_services.append('storage') - else: - rexec_config.pop('storage-service') - - if 'artifacts' in self.config: - if artifacts: - configured_services.append('artifact-cache') - else: - self.config.pop('artifacts') - - if 'source-caches' in self.config: - if sources: - configured_services.append('source-cache') - else: - self.config.pop('source-caches') - - return configured_services - - -class TestArtifact(): - - # remove_artifact_from_cache(): - # - # Remove given element artifact from artifact cache - # - # Args: - # cache_dir (str): Specific cache dir to remove artifact from - # element_name (str): The name of the element artifact - # - def remove_artifact_from_cache(self, cache_dir, element_name): - - cache_dir = os.path.join(cache_dir, 'cas', 'refs', 'heads') - - cache_dir = os.path.splitext(os.path.join(cache_dir, 'test', element_name))[0] - shutil.rmtree(cache_dir) - - # is_cached(): - # - # Check if given element has a cached artifact - # - # Args: - # cache_dir (str): Specific cache dir to check - # element (Element): The element object - # element_key (str): The element's cache key - # - # Returns: - # (bool): If the cache contains the element's artifact - # - def is_cached(self, cache_dir, element, element_key): - - cas = CASCache(str(cache_dir)) - artifact_ref = element.get_artifact_name(element_key) - return cas.contains(artifact_ref) - - # get_digest(): - # - # Get the digest for a given element's artifact - # - # Args: - # cache_dir (str): Specific cache dir to check - # element (Element): The element object - # element_key (str): The element's cache key - # - # Returns: - # (Digest): The digest stored in the ref - # - def get_digest(self, cache_dir, element, element_key): - - cas = CASCache(str(cache_dir)) - artifact_ref = element.get_artifact_name(element_key) - digest = cas.resolve_ref(artifact_ref) - return digest - - # extract_buildtree(): - # - # Context manager for extracting an elements artifact buildtree for - # inspection. - # - # Args: - # tmpdir (LocalPath): pytest fixture for the tests tmp dir - # digest (Digest): The element directory digest to extract - # - # Yields: - # (str): path to extracted buildtree directory, does not guarantee - # existence. - @contextmanager - def extract_buildtree(self, tmpdir, digest): - with self._extract_subdirectory(tmpdir, digest, 'buildtree') as extract: - yield extract - - # _extract_subdirectory(): - # - # Context manager for extracting an element artifact for inspection, - # providing an expected path for a given subdirectory - # - # Args: - # tmpdir (LocalPath): pytest fixture for the tests tmp dir - # digest (Digest): The element directory digest to extract - # subdir (str): Subdirectory to path - # - # Yields: - # (str): path to extracted subdir directory, does not guarantee - # existence. - @contextmanager - def _extract_subdirectory(self, tmpdir, digest, subdir): - with tempfile.TemporaryDirectory() as extractdir: - try: - cas = CASCache(str(tmpdir)) - cas.checkout(extractdir, digest) - yield os.path.join(extractdir, subdir) - except FileNotFoundError: - yield None - - -# Main fixture -# -# Use result = cli.run([arg1, arg2]) to run buildstream commands -# -@pytest.fixture() -def cli(tmpdir): - directory = os.path.join(str(tmpdir), 'cache') - os.makedirs(directory) - return Cli(directory) - - -# A variant of the main fixture that keeps persistent artifact and -# source caches. -# -# It also does not use the click test runner to avoid deadlock issues -# when running `bst shell`, but unfortunately cannot produce nice -# stacktraces. -@pytest.fixture() -def cli_integration(tmpdir, integration_cache): - directory = os.path.join(str(tmpdir), 'cache') - os.makedirs(directory) - - if os.environ.get('BST_FORCE_BACKEND') == 'unix': - fixture = CliIntegration(directory, default_options=[('linux', 'False')]) - else: - fixture = CliIntegration(directory) - - # We want to cache sources for integration tests more permanently, - # to avoid downloading the huge base-sdk repeatedly - fixture.configure({ - 'cachedir': integration_cache.cachedir, - 'sourcedir': integration_cache.sources, - }) - - yield fixture - - # remove following folders if necessary - try: - shutil.rmtree(os.path.join(integration_cache.cachedir, 'build')) - except FileNotFoundError: - pass - try: - shutil.rmtree(os.path.join(integration_cache.cachedir, 'tmp')) - except FileNotFoundError: - pass - - -# A variant of the main fixture that is configured for remote-execution. -# -# It also does not use the click test runner to avoid deadlock issues -# when running `bst shell`, but unfortunately cannot produce nice -# stacktraces. -@pytest.fixture() -def cli_remote_execution(tmpdir, remote_services): - directory = os.path.join(str(tmpdir), 'cache') - os.makedirs(directory) - - fixture = CliRemote(directory) - - if remote_services.artifact_service: - fixture.configure({'artifacts': [{ - 'url': remote_services.artifact_service, - }]}) - - remote_execution = {} - if remote_services.action_service: - remote_execution['action-cache-service'] = { - 'url': remote_services.action_service, - } - if remote_services.exec_service: - remote_execution['execution-service'] = { - 'url': remote_services.exec_service, - } - if remote_services.storage_service: - remote_execution['storage-service'] = { - 'url': remote_services.storage_service, - } - if remote_execution: - fixture.configure({'remote-execution': remote_execution}) - - if remote_services.source_service: - fixture.configure({'source-caches': [{ - 'url': remote_services.source_service, - }]}) - - return fixture - - -@contextmanager -def chdir(directory): - old_dir = os.getcwd() - os.chdir(directory) - yield - os.chdir(old_dir) - - -@contextmanager -def environment(env): - - old_env = {} - for key, value in env.items(): - old_env[key] = os.environ.get(key) - if value is None: - os.environ.pop(key, None) - else: - os.environ[key] = value - - yield - - for key, value in old_env.items(): - if value is None: - os.environ.pop(key, None) - else: - os.environ[key] = value - - -@contextmanager -def configured(directory, config=None): - - # Ensure we've at least relocated the caches to a temp directory - if not config: - config = {} - - if not config.get('sourcedir', False): - config['sourcedir'] = os.path.join(directory, 'sources') - if not config.get('cachedir', False): - config['cachedir'] = directory - if not config.get('logdir', False): - config['logdir'] = os.path.join(directory, 'logs') - - # Dump it and yield the filename for test scripts to feed it - # to buildstream as an artument - filename = os.path.join(directory, "buildstream.conf") - _yaml.dump(config, filename) - - yield filename diff --git a/buildstream/testing/__init__.py b/buildstream/testing/__init__.py new file mode 100644 index 000000000..0dfc11f1c --- /dev/null +++ b/buildstream/testing/__init__.py @@ -0,0 +1,98 @@ +# +# Copyright (C) 2019 Codethink Limited +# Copyright (C) 2019 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 . + +""" +This package contains various utilities which make it easier to test plugins. +""" + +import os +from collections import OrderedDict +from . import _sourcetests +from .repo import Repo +from .runcli import cli, cli_integration, cli_remote_execution + +# To make use of these test utilities it is necessary to have pytest +# available. However, we don't want to have a hard dependency on +# pytest. +try: + import pytest +except ImportError: + module_name = globals()['__name__'] + msg = "Could not import pytest:\n" \ + "To use the {} module, you must have pytest installed.".format(module_name) + raise ImportError(msg) + + +ALL_REPO_KINDS = OrderedDict() + + +def create_repo(kind, directory, subdir='repo'): + """Convenience method for creating a Repo + + Args: + kind (str): The kind of repo to create (a source plugin basename). This + must have previously been registered using + `register_repo_kind` + directory (str): The path where the repo will keep a cache + + Returns: + (Repo): A new Repo object + """ + try: + constructor = ALL_REPO_KINDS[kind] + except KeyError as e: + raise AssertionError("Unsupported repo kind {}".format(kind)) from e + + return constructor(directory, subdir=subdir) + + +def register_repo_kind(kind, cls): + """Register a new repo kind. + + Registering a repo kind will allow the use of the `create_repo` + method for that kind and include that repo kind in ALL_REPO_KINDS + + In addition, repo_kinds registred prior to + `sourcetests_collection_hook` being called will be automatically + used to test the basic behaviour of their associated source + plugins using the tests in `testing._sourcetests`. + + Args: + kind (str): The kind of repo to create (a source plugin basename) + cls (cls) : A class derived from Repo. + + """ + ALL_REPO_KINDS[kind] = cls + + +def sourcetests_collection_hook(session): + """ Used to hook the templated source plugin tests into a pyest test suite. + + This should be called via the `pytest_sessionstart + hook `_. + The tests in the _sourcetests package will be collected as part of + whichever test package this hook is called from. + + Args: + session (pytest.Session): The current pytest session + """ + SOURCE_TESTS_PATH = os.path.dirname(_sourcetests.__file__) + # Add the location of the source tests to the session's + # python_files config. Without this, pytest may filter out these + # tests during collection. + session.config.addinivalue_line("python_files", os.path.join(SOURCE_TESTS_PATH, "*.py")) + session.config.args.append(SOURCE_TESTS_PATH) diff --git a/buildstream/testing/_sourcetests/__init__.py b/buildstream/testing/_sourcetests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/buildstream/testing/_sourcetests/build_checkout.py b/buildstream/testing/_sourcetests/build_checkout.py new file mode 100644 index 000000000..3619d2b7e --- /dev/null +++ b/buildstream/testing/_sourcetests/build_checkout.py @@ -0,0 +1,83 @@ +# +# Copyright (C) 2018 Codethink Limited +# Copyright (C) 2019 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 . +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import pytest + +from buildstream.testing import create_repo, ALL_REPO_KINDS +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream import _yaml + +# Project directory +TOP_DIR = os.path.dirname(os.path.realpath(__file__)) +DATA_DIR = os.path.join(TOP_DIR, 'project') + +fetch_build_checkout_combos = \ + [("strict", kind) for kind in ALL_REPO_KINDS] + \ + [("non-strict", kind) for kind in ALL_REPO_KINDS] + + +def strict_args(args, strict): + if strict != "strict": + return ['--no-strict', *args] + return args + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("strict,kind", fetch_build_checkout_combos) +def test_fetch_build_checkout(cli, tmpdir, datafiles, strict, kind): + checkout = os.path.join(cli.directory, 'checkout') + project = str(datafiles) + dev_files_path = os.path.join(project, 'files', 'dev-files') + element_path = os.path.join(project, 'elements') + element_name = 'build-test-{}.bst'.format(kind) + + # Create our repo object of the given source type with + # the dev files, and then collect the initial ref. + # + repo = create_repo(kind, str(tmpdir)) + ref = repo.create(dev_files_path) + + # Write out our test target + element = { + 'kind': 'import', + 'sources': [ + repo.source_config(ref=ref) + ] + } + _yaml.dump(element, + os.path.join(element_path, + element_name)) + + assert cli.get_element_state(project, element_name) == 'fetch needed' + result = cli.run(project=project, args=strict_args(['build', element_name], strict)) + result.assert_success() + assert cli.get_element_state(project, element_name) == 'cached' + + # Now check it out + result = cli.run(project=project, args=strict_args([ + 'artifact', 'checkout', element_name, '--directory', checkout + ], strict)) + result.assert_success() + + # Check that the pony.h include from files/dev-files exists + filename = os.path.join(checkout, 'usr', 'include', 'pony.h') + assert os.path.exists(filename) diff --git a/buildstream/testing/_sourcetests/fetch.py b/buildstream/testing/_sourcetests/fetch.py new file mode 100644 index 000000000..aaf92a14d --- /dev/null +++ b/buildstream/testing/_sourcetests/fetch.py @@ -0,0 +1,107 @@ +# +# Copyright (C) 2018 Codethink Limited +# Copyright (C) 2019 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 . +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import pytest + +from buildstream import _yaml +from .._utils import generate_junction, configure_project +from .. import create_repo, ALL_REPO_KINDS +from .. import cli # pylint: disable=unused-import + +# Project directory +TOP_DIR = os.path.dirname(os.path.realpath(__file__)) +DATA_DIR = os.path.join(TOP_DIR, 'project') + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_fetch(cli, tmpdir, datafiles, kind): + project = str(datafiles) + bin_files_path = os.path.join(project, 'files', 'bin-files') + element_path = os.path.join(project, 'elements') + element_name = 'fetch-test-{}.bst'.format(kind) + + # Create our repo object of the given source type with + # the bin files, and then collect the initial ref. + # + repo = create_repo(kind, str(tmpdir)) + ref = repo.create(bin_files_path) + + # Write out our test target + element = { + 'kind': 'import', + 'sources': [ + repo.source_config(ref=ref) + ] + } + _yaml.dump(element, + os.path.join(element_path, + element_name)) + + # Assert that a fetch is needed + assert cli.get_element_state(project, element_name) == 'fetch needed' + + # Now try to fetch it + result = cli.run(project=project, args=['source', 'fetch', element_name]) + result.assert_success() + + # Assert that we are now buildable because the source is + # now cached. + assert cli.get_element_state(project, element_name) == 'buildable' + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_fetch_cross_junction(cli, tmpdir, datafiles, ref_storage, kind): + project = str(datafiles) + subproject_path = os.path.join(project, 'files', 'sub-project') + junction_path = os.path.join(project, 'elements', 'junction.bst') + + import_etc_path = os.path.join(subproject_path, 'elements', 'import-etc-repo.bst') + etc_files_path = os.path.join(subproject_path, 'files', 'etc-files') + + repo = create_repo(kind, str(tmpdir.join('import-etc'))) + ref = repo.create(etc_files_path) + + element = { + 'kind': 'import', + 'sources': [ + repo.source_config(ref=(ref if ref_storage == 'inline' else None)) + ] + } + _yaml.dump(element, import_etc_path) + + configure_project(project, { + 'ref-storage': ref_storage + }) + + generate_junction(tmpdir, subproject_path, junction_path, store_ref=(ref_storage == 'inline')) + + if ref_storage == 'project.refs': + result = cli.run(project=project, args=['source', 'track', 'junction.bst']) + result.assert_success() + result = cli.run(project=project, args=['source', 'track', 'junction.bst:import-etc.bst']) + result.assert_success() + + result = cli.run(project=project, args=['source', 'fetch', 'junction.bst:import-etc.bst']) + result.assert_success() diff --git a/buildstream/testing/_sourcetests/mirror.py b/buildstream/testing/_sourcetests/mirror.py new file mode 100644 index 000000000..d682bb2ef --- /dev/null +++ b/buildstream/testing/_sourcetests/mirror.py @@ -0,0 +1,427 @@ +# +# Copyright (C) 2018 Codethink Limited +# Copyright (C) 2019 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 . +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import pytest + +from buildstream import _yaml +from buildstream._exceptions import ErrorDomain +from .._utils import generate_junction +from .. import create_repo, ALL_REPO_KINDS +from .. import cli # pylint: disable=unused-import + +# Project directory +TOP_DIR = os.path.dirname(os.path.realpath(__file__)) +DATA_DIR = os.path.join(TOP_DIR, 'project') + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_mirror_fetch(cli, tmpdir, datafiles, kind): + bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') + dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr') + upstream_repodir = os.path.join(str(tmpdir), 'upstream') + mirror_repodir = os.path.join(str(tmpdir), 'mirror') + project_dir = os.path.join(str(tmpdir), 'project') + os.makedirs(project_dir) + element_dir = os.path.join(project_dir, 'elements') + + # Create repo objects of the upstream and mirror + upstream_repo = create_repo(kind, upstream_repodir) + upstream_repo.create(bin_files_path) + mirror_repo = upstream_repo.copy(mirror_repodir) + upstream_ref = upstream_repo.create(dev_files_path) + + element = { + 'kind': 'import', + 'sources': [ + upstream_repo.source_config(ref=upstream_ref) + ] + } + element_name = 'test.bst' + element_path = os.path.join(element_dir, element_name) + full_repo = element['sources'][0]['url'] + upstream_map, repo_name = os.path.split(full_repo) + alias = 'foo-' + kind + aliased_repo = alias + ':' + repo_name + element['sources'][0]['url'] = aliased_repo + full_mirror = mirror_repo.source_config()['url'] + mirror_map, _ = os.path.split(full_mirror) + os.makedirs(element_dir) + _yaml.dump(element, element_path) + + project = { + 'name': 'test', + 'element-path': 'elements', + 'aliases': { + alias: upstream_map + "/" + }, + 'mirrors': [ + { + 'name': 'middle-earth', + 'aliases': { + alias: [mirror_map + "/"], + }, + }, + ] + } + project_file = os.path.join(project_dir, 'project.conf') + _yaml.dump(project, project_file) + + # No obvious ways of checking that the mirror has been fetched + # But at least we can be sure it succeeds + result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) + result.assert_success() + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_mirror_fetch_upstream_absent(cli, tmpdir, datafiles, kind): + dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr') + upstream_repodir = os.path.join(str(tmpdir), 'upstream') + mirror_repodir = os.path.join(str(tmpdir), 'mirror') + project_dir = os.path.join(str(tmpdir), 'project') + os.makedirs(project_dir) + element_dir = os.path.join(project_dir, 'elements') + + # Create repo objects of the upstream and mirror + upstream_repo = create_repo(kind, upstream_repodir) + ref = upstream_repo.create(dev_files_path) + mirror_repo = upstream_repo.copy(mirror_repodir) + + element = { + 'kind': 'import', + 'sources': [ + upstream_repo.source_config(ref=ref) + ] + } + + element_name = 'test.bst' + element_path = os.path.join(element_dir, element_name) + full_repo = element['sources'][0]['url'] + _, repo_name = os.path.split(full_repo) + alias = 'foo-' + kind + aliased_repo = alias + ':' + repo_name + element['sources'][0]['url'] = aliased_repo + full_mirror = mirror_repo.source_config()['url'] + mirror_map, _ = os.path.split(full_mirror) + os.makedirs(element_dir) + _yaml.dump(element, element_path) + + project = { + 'name': 'test', + 'element-path': 'elements', + 'aliases': { + alias: 'http://www.example.com/' + }, + 'mirrors': [ + { + 'name': 'middle-earth', + 'aliases': { + alias: [mirror_map + "/"], + }, + }, + ] + } + project_file = os.path.join(project_dir, 'project.conf') + _yaml.dump(project, project_file) + + result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) + result.assert_success() + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_mirror_from_includes(cli, tmpdir, datafiles, kind): + bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') + upstream_repodir = os.path.join(str(tmpdir), 'upstream') + mirror_repodir = os.path.join(str(tmpdir), 'mirror') + project_dir = os.path.join(str(tmpdir), 'project') + os.makedirs(project_dir) + element_dir = os.path.join(project_dir, 'elements') + + # Create repo objects of the upstream and mirror + upstream_repo = create_repo(kind, upstream_repodir) + upstream_ref = upstream_repo.create(bin_files_path) + mirror_repo = upstream_repo.copy(mirror_repodir) + + element = { + 'kind': 'import', + 'sources': [ + upstream_repo.source_config(ref=upstream_ref) + ] + } + element_name = 'test.bst' + element_path = os.path.join(element_dir, element_name) + full_repo = element['sources'][0]['url'] + upstream_map, repo_name = os.path.split(full_repo) + alias = 'foo-' + kind + aliased_repo = alias + ':' + repo_name + element['sources'][0]['url'] = aliased_repo + full_mirror = mirror_repo.source_config()['url'] + mirror_map, _ = os.path.split(full_mirror) + os.makedirs(element_dir) + _yaml.dump(element, element_path) + + config_project_dir = str(tmpdir.join('config')) + os.makedirs(config_project_dir, exist_ok=True) + config_project = { + 'name': 'config' + } + _yaml.dump(config_project, os.path.join(config_project_dir, 'project.conf')) + extra_mirrors = { + 'mirrors': [ + { + 'name': 'middle-earth', + 'aliases': { + alias: [mirror_map + "/"], + } + } + ] + } + _yaml.dump(extra_mirrors, os.path.join(config_project_dir, 'mirrors.yml')) + generate_junction(str(tmpdir.join('config_repo')), + config_project_dir, + os.path.join(element_dir, 'config.bst')) + + project = { + 'name': 'test', + 'element-path': 'elements', + 'aliases': { + alias: upstream_map + "/" + }, + '(@)': [ + 'config.bst:mirrors.yml' + ] + } + project_file = os.path.join(project_dir, 'project.conf') + _yaml.dump(project, project_file) + + # Now make the upstream unavailable. + os.rename(upstream_repo.repo, '{}.bak'.format(upstream_repo.repo)) + result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) + result.assert_success() + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_mirror_junction_from_includes(cli, tmpdir, datafiles, kind): + bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') + upstream_repodir = os.path.join(str(tmpdir), 'upstream') + mirror_repodir = os.path.join(str(tmpdir), 'mirror') + project_dir = os.path.join(str(tmpdir), 'project') + os.makedirs(project_dir) + element_dir = os.path.join(project_dir, 'elements') + + # Create repo objects of the upstream and mirror + upstream_repo = create_repo(kind, upstream_repodir) + upstream_ref = upstream_repo.create(bin_files_path) + mirror_repo = upstream_repo.copy(mirror_repodir) + + element = { + 'kind': 'junction', + 'sources': [ + upstream_repo.source_config(ref=upstream_ref) + ] + } + element_name = 'test.bst' + element_path = os.path.join(element_dir, element_name) + full_repo = element['sources'][0]['url'] + upstream_map, repo_name = os.path.split(full_repo) + alias = 'foo-' + kind + aliased_repo = alias + ':' + repo_name + element['sources'][0]['url'] = aliased_repo + full_mirror = mirror_repo.source_config()['url'] + mirror_map, _ = os.path.split(full_mirror) + os.makedirs(element_dir) + _yaml.dump(element, element_path) + + config_project_dir = str(tmpdir.join('config')) + os.makedirs(config_project_dir, exist_ok=True) + config_project = { + 'name': 'config' + } + _yaml.dump(config_project, os.path.join(config_project_dir, 'project.conf')) + extra_mirrors = { + 'mirrors': [ + { + 'name': 'middle-earth', + 'aliases': { + alias: [mirror_map + "/"], + } + } + ] + } + _yaml.dump(extra_mirrors, os.path.join(config_project_dir, 'mirrors.yml')) + generate_junction(str(tmpdir.join('config_repo')), + config_project_dir, + os.path.join(element_dir, 'config.bst')) + + project = { + 'name': 'test', + 'element-path': 'elements', + 'aliases': { + alias: upstream_map + "/" + }, + '(@)': [ + 'config.bst:mirrors.yml' + ] + } + project_file = os.path.join(project_dir, 'project.conf') + _yaml.dump(project, project_file) + + # Now make the upstream unavailable. + os.rename(upstream_repo.repo, '{}.bak'.format(upstream_repo.repo)) + result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) + result.assert_main_error(ErrorDomain.STREAM, None) + # Now make the upstream available again. + os.rename('{}.bak'.format(upstream_repo.repo), upstream_repo.repo) + result = cli.run(project=project_dir, args=['source', 'fetch', element_name]) + result.assert_success() + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_mirror_track_upstream_present(cli, tmpdir, datafiles, kind): + bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') + dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr') + upstream_repodir = os.path.join(str(tmpdir), 'upstream') + mirror_repodir = os.path.join(str(tmpdir), 'mirror') + project_dir = os.path.join(str(tmpdir), 'project') + os.makedirs(project_dir) + element_dir = os.path.join(project_dir, 'elements') + + # Create repo objects of the upstream and mirror + upstream_repo = create_repo(kind, upstream_repodir) + upstream_repo.create(bin_files_path) + mirror_repo = upstream_repo.copy(mirror_repodir) + upstream_ref = upstream_repo.create(dev_files_path) + + element = { + 'kind': 'import', + 'sources': [ + upstream_repo.source_config(ref=upstream_ref) + ] + } + + element_name = 'test.bst' + element_path = os.path.join(element_dir, element_name) + full_repo = element['sources'][0]['url'] + upstream_map, repo_name = os.path.split(full_repo) + alias = 'foo-' + kind + aliased_repo = alias + ':' + repo_name + element['sources'][0]['url'] = aliased_repo + full_mirror = mirror_repo.source_config()['url'] + mirror_map, _ = os.path.split(full_mirror) + os.makedirs(element_dir) + _yaml.dump(element, element_path) + + project = { + 'name': 'test', + 'element-path': 'elements', + 'aliases': { + alias: upstream_map + "/" + }, + 'mirrors': [ + { + 'name': 'middle-earth', + 'aliases': { + alias: [mirror_map + "/"], + }, + }, + ] + } + project_file = os.path.join(project_dir, 'project.conf') + _yaml.dump(project, project_file) + + result = cli.run(project=project_dir, args=['source', 'track', element_name]) + result.assert_success() + + # Tracking tries upstream first. Check the ref is from upstream. + new_element = _yaml.load(element_path) + source = _yaml.node_get(new_element, dict, 'sources', [0]) + if 'ref' in source: + assert _yaml.node_get(source, str, 'ref') == upstream_ref + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_mirror_track_upstream_absent(cli, tmpdir, datafiles, kind): + bin_files_path = os.path.join(str(datafiles), 'files', 'bin-files', 'usr') + dev_files_path = os.path.join(str(datafiles), 'files', 'dev-files', 'usr') + upstream_repodir = os.path.join(str(tmpdir), 'upstream') + mirror_repodir = os.path.join(str(tmpdir), 'mirror') + project_dir = os.path.join(str(tmpdir), 'project') + os.makedirs(project_dir) + element_dir = os.path.join(project_dir, 'elements') + + # Create repo objects of the upstream and mirror + upstream_repo = create_repo(kind, upstream_repodir) + upstream_ref = upstream_repo.create(bin_files_path) + mirror_repo = upstream_repo.copy(mirror_repodir) + mirror_ref = upstream_ref + upstream_ref = upstream_repo.create(dev_files_path) + + element = { + 'kind': 'import', + 'sources': [ + upstream_repo.source_config(ref=upstream_ref) + ] + } + + element_name = 'test.bst' + element_path = os.path.join(element_dir, element_name) + full_repo = element['sources'][0]['url'] + _, repo_name = os.path.split(full_repo) + alias = 'foo-' + kind + aliased_repo = alias + ':' + repo_name + element['sources'][0]['url'] = aliased_repo + full_mirror = mirror_repo.source_config()['url'] + mirror_map, _ = os.path.split(full_mirror) + os.makedirs(element_dir) + _yaml.dump(element, element_path) + + project = { + 'name': 'test', + 'element-path': 'elements', + 'aliases': { + alias: 'http://www.example.com/' + }, + 'mirrors': [ + { + 'name': 'middle-earth', + 'aliases': { + alias: [mirror_map + "/"], + }, + }, + ] + } + project_file = os.path.join(project_dir, 'project.conf') + _yaml.dump(project, project_file) + + result = cli.run(project=project_dir, args=['source', 'track', element_name]) + result.assert_success() + + # Check that tracking fell back to the mirror + new_element = _yaml.load(element_path) + source = _yaml.node_get(new_element, dict, 'sources', [0]) + if 'ref' in source: + assert _yaml.node_get(source, str, 'ref') == mirror_ref diff --git a/buildstream/testing/_sourcetests/project/elements/base.bst b/buildstream/testing/_sourcetests/project/elements/base.bst new file mode 100644 index 000000000..428afa736 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/base.bst @@ -0,0 +1,5 @@ +# elements/base.bst + +kind: stack +depends: + - base/base-alpine.bst diff --git a/buildstream/testing/_sourcetests/project/elements/base/base-alpine.bst b/buildstream/testing/_sourcetests/project/elements/base/base-alpine.bst new file mode 100644 index 000000000..c5833095d --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/base/base-alpine.bst @@ -0,0 +1,17 @@ +kind: import + +description: | + Alpine Linux base for tests + + Generated using the `tests/integration-tests/base/generate-base.sh` script. + +sources: + - kind: tar + base-dir: '' + (?): + - arch == "x86-64": + ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 + url: "alpine:integration-tests-base.v1.x86_64.tar.xz" + - arch == "aarch64": + ref: 431fb5362032ede6f172e70a3258354a8fd71fcbdeb1edebc0e20968c792329a + url: "alpine:integration-tests-base.v1.aarch64.tar.xz" diff --git a/buildstream/testing/_sourcetests/project/elements/import-bin.bst b/buildstream/testing/_sourcetests/project/elements/import-bin.bst new file mode 100644 index 000000000..a847c0c23 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/import-bin.bst @@ -0,0 +1,4 @@ +kind: import +sources: +- kind: local + path: files/bin-files diff --git a/buildstream/testing/_sourcetests/project/elements/import-dev.bst b/buildstream/testing/_sourcetests/project/elements/import-dev.bst new file mode 100644 index 000000000..152a54667 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/import-dev.bst @@ -0,0 +1,4 @@ +kind: import +sources: +- kind: local + path: files/dev-files diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/horsey.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/horsey.bst new file mode 100644 index 000000000..bd1ffae9c --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/horsey.bst @@ -0,0 +1,3 @@ +kind: autotools +depends: + - multiple_targets/dependency/pony.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/pony.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/pony.bst new file mode 100644 index 000000000..3c29b4ea1 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/pony.bst @@ -0,0 +1 @@ +kind: autotools diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/zebry.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/zebry.bst new file mode 100644 index 000000000..98447ab52 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/dependency/zebry.bst @@ -0,0 +1,3 @@ +kind: autotools +depends: + - multiple_targets/dependency/horsey.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/0.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/0.bst new file mode 100644 index 000000000..a99be06a0 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/0.bst @@ -0,0 +1,7 @@ +kind: autotools +description: Root node +depends: + - multiple_targets/order/2.bst + - multiple_targets/order/3.bst + - filename: multiple_targets/order/run.bst + type: runtime diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/1.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/1.bst new file mode 100644 index 000000000..82b507a62 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/1.bst @@ -0,0 +1,4 @@ +kind: autotools +description: Root node +depends: + - multiple_targets/order/9.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/2.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/2.bst new file mode 100644 index 000000000..ee1afae20 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/2.bst @@ -0,0 +1,4 @@ +kind: autotools +description: First dependency level +depends: + - multiple_targets/order/3.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/3.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/3.bst new file mode 100644 index 000000000..4c3a23dab --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/3.bst @@ -0,0 +1,6 @@ +kind: autotools +description: Second dependency level +depends: + - multiple_targets/order/4.bst + - multiple_targets/order/5.bst + - multiple_targets/order/6.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/4.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/4.bst new file mode 100644 index 000000000..b663a0b52 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/4.bst @@ -0,0 +1,2 @@ +kind: autotools +description: Third level dependency diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/5.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/5.bst new file mode 100644 index 000000000..b9efcf71b --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/5.bst @@ -0,0 +1,2 @@ +kind: autotools +description: Fifth level dependency diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/6.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/6.bst new file mode 100644 index 000000000..6c19d04e3 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/6.bst @@ -0,0 +1,4 @@ +kind: autotools +description: Fourth level dependency +depends: + - multiple_targets/order/5.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/7.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/7.bst new file mode 100644 index 000000000..6805b3e6d --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/7.bst @@ -0,0 +1,4 @@ +kind: autotools +description: Third level dependency +depends: + - multiple_targets/order/6.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/8.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/8.bst new file mode 100644 index 000000000..b8d8964a0 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/8.bst @@ -0,0 +1,4 @@ +kind: autotools +description: Second level dependency +depends: + - multiple_targets/order/7.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/9.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/9.bst new file mode 100644 index 000000000..cc13bf3f0 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/9.bst @@ -0,0 +1,4 @@ +kind: autotools +description: First level dependency +depends: + - multiple_targets/order/8.bst diff --git a/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/run.bst b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/run.bst new file mode 100644 index 000000000..9b3d2446c --- /dev/null +++ b/buildstream/testing/_sourcetests/project/elements/multiple_targets/order/run.bst @@ -0,0 +1,2 @@ +kind: autotools +description: Not a root node, yet built at the same time as root nodes diff --git a/buildstream/testing/_sourcetests/project/files/bar b/buildstream/testing/_sourcetests/project/files/bar new file mode 100644 index 000000000..e69de29bb diff --git a/buildstream/testing/_sourcetests/project/files/bin-files/usr/bin/hello b/buildstream/testing/_sourcetests/project/files/bin-files/usr/bin/hello new file mode 100755 index 000000000..f534a4083 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/files/bin-files/usr/bin/hello @@ -0,0 +1,3 @@ +#!/bin/bash + +echo "Hello !" diff --git a/buildstream/testing/_sourcetests/project/files/dev-files/usr/include/pony.h b/buildstream/testing/_sourcetests/project/files/dev-files/usr/include/pony.h new file mode 100644 index 000000000..40bd0c2e7 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/files/dev-files/usr/include/pony.h @@ -0,0 +1,12 @@ +#ifndef __PONY_H__ +#define __PONY_H__ + +#define PONY_BEGIN "Once upon a time, there was a pony." +#define PONY_END "And they lived happily ever after, the end." + +#define MAKE_PONY(story) \ + PONY_BEGIN \ + story \ + PONY_END + +#endif /* __PONY_H__ */ diff --git a/buildstream/testing/_sourcetests/project/files/etc-files/etc/buildstream/config b/buildstream/testing/_sourcetests/project/files/etc-files/etc/buildstream/config new file mode 100644 index 000000000..04204c7c9 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/files/etc-files/etc/buildstream/config @@ -0,0 +1 @@ +config diff --git a/buildstream/testing/_sourcetests/project/files/foo b/buildstream/testing/_sourcetests/project/files/foo new file mode 100644 index 000000000..e69de29bb diff --git a/buildstream/testing/_sourcetests/project/files/source-bundle/llamas.txt b/buildstream/testing/_sourcetests/project/files/source-bundle/llamas.txt new file mode 100644 index 000000000..f98b24871 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/files/source-bundle/llamas.txt @@ -0,0 +1 @@ +llamas diff --git a/buildstream/testing/_sourcetests/project/files/sub-project/elements/import-etc.bst b/buildstream/testing/_sourcetests/project/files/sub-project/elements/import-etc.bst new file mode 100644 index 000000000..f0171990e --- /dev/null +++ b/buildstream/testing/_sourcetests/project/files/sub-project/elements/import-etc.bst @@ -0,0 +1,4 @@ +kind: import +sources: +- kind: local + path: files/etc-files diff --git a/buildstream/testing/_sourcetests/project/files/sub-project/files/etc-files/etc/animal.conf b/buildstream/testing/_sourcetests/project/files/sub-project/files/etc-files/etc/animal.conf new file mode 100644 index 000000000..db8c36cba --- /dev/null +++ b/buildstream/testing/_sourcetests/project/files/sub-project/files/etc-files/etc/animal.conf @@ -0,0 +1 @@ +animal=Pony diff --git a/buildstream/testing/_sourcetests/project/files/sub-project/project.conf b/buildstream/testing/_sourcetests/project/files/sub-project/project.conf new file mode 100644 index 000000000..bbb8414a3 --- /dev/null +++ b/buildstream/testing/_sourcetests/project/files/sub-project/project.conf @@ -0,0 +1,4 @@ +# Project config for frontend build test +name: subtest + +element-path: elements diff --git a/buildstream/testing/_sourcetests/project/project.conf b/buildstream/testing/_sourcetests/project/project.conf new file mode 100644 index 000000000..05b68bfeb --- /dev/null +++ b/buildstream/testing/_sourcetests/project/project.conf @@ -0,0 +1,27 @@ +# Project config for frontend build test +name: test +element-path: elements +aliases: + alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ + project_dir: file://{project_dir} +options: + linux: + type: bool + description: Whether to expect a linux platform + default: True + arch: + type: arch + description: Current architecture + values: + - x86-64 + - aarch64 +split-rules: + test: + - | + /tests + - | + /tests/* + +fatal-warnings: +- bad-element-suffix +- bad-characters-in-name diff --git a/buildstream/testing/_sourcetests/source_determinism.py b/buildstream/testing/_sourcetests/source_determinism.py new file mode 100644 index 000000000..8597a7072 --- /dev/null +++ b/buildstream/testing/_sourcetests/source_determinism.py @@ -0,0 +1,118 @@ +# +# Copyright (C) 2018 Codethink Limited +# Copyright (C) 2019 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 . +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import pytest + +from buildstream import _yaml +from .._utils.site import HAVE_SANDBOX +from .. import create_repo, ALL_REPO_KINDS +from .. import cli # pylint: disable=unused-import + +# Project directory +TOP_DIR = os.path.dirname(os.path.realpath(__file__)) +DATA_DIR = os.path.join(TOP_DIR, 'project') + + +def create_test_file(*path, mode=0o644, content='content\n'): + path = os.path.join(*path) + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, 'w') as f: + f.write(content) + os.fchmod(f.fileno(), mode) + + +def create_test_directory(*path, mode=0o644): + create_test_file(*path, '.keep', content='') + path = os.path.join(*path) + os.chmod(path, mode) + + +@pytest.mark.integration +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", ['local', *ALL_REPO_KINDS]) +@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +def test_deterministic_source_umask(cli, tmpdir, datafiles, kind): + project = str(datafiles) + element_name = 'list.bst' + element_path = os.path.join(project, 'elements', element_name) + repodir = os.path.join(str(tmpdir), 'repo') + sourcedir = os.path.join(project, 'source') + + create_test_file(sourcedir, 'a.txt', mode=0o700) + create_test_file(sourcedir, 'b.txt', mode=0o755) + create_test_file(sourcedir, 'c.txt', mode=0o600) + create_test_file(sourcedir, 'd.txt', mode=0o400) + create_test_file(sourcedir, 'e.txt', mode=0o644) + create_test_file(sourcedir, 'f.txt', mode=0o4755) + create_test_file(sourcedir, 'g.txt', mode=0o2755) + create_test_file(sourcedir, 'h.txt', mode=0o1755) + create_test_directory(sourcedir, 'dir-a', mode=0o0700) + create_test_directory(sourcedir, 'dir-c', mode=0o0755) + create_test_directory(sourcedir, 'dir-d', mode=0o4755) + create_test_directory(sourcedir, 'dir-e', mode=0o2755) + create_test_directory(sourcedir, 'dir-f', mode=0o1755) + + if kind == 'local': + source = {'kind': 'local', + 'path': 'source'} + else: + repo = create_repo(kind, repodir) + ref = repo.create(sourcedir) + source = repo.source_config(ref=ref) + element = { + 'kind': 'manual', + 'depends': [ + { + 'filename': 'base.bst', + 'type': 'build' + } + ], + 'sources': [ + source + ], + 'config': { + 'install-commands': [ + 'ls -l >"%{install-root}/ls-l"' + ] + } + } + _yaml.dump(element, element_path) + + def get_value_for_umask(umask): + checkoutdir = os.path.join(str(tmpdir), 'checkout-{}'.format(umask)) + + old_umask = os.umask(umask) + + try: + result = cli.run(project=project, args=['build', element_name]) + result.assert_success() + + result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkoutdir]) + result.assert_success() + + with open(os.path.join(checkoutdir, 'ls-l'), 'r') as f: + return f.read() + finally: + os.umask(old_umask) + cli.remove_artifact_from_cache(project, element_name) + + assert get_value_for_umask(0o022) == get_value_for_umask(0o077) diff --git a/buildstream/testing/_sourcetests/track.py b/buildstream/testing/_sourcetests/track.py new file mode 100644 index 000000000..668ea29e5 --- /dev/null +++ b/buildstream/testing/_sourcetests/track.py @@ -0,0 +1,420 @@ +# +# Copyright (C) 2018 Codethink Limited +# Copyright (C) 2019 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 . +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import pytest + +from buildstream import _yaml +from buildstream._exceptions import ErrorDomain +from .._utils import generate_junction, configure_project +from .. import create_repo, ALL_REPO_KINDS +from .. import cli # pylint: disable=unused-import + +# Project directory +TOP_DIR = os.path.dirname(os.path.realpath(__file__)) +DATA_DIR = os.path.join(TOP_DIR, 'project') + + +def generate_element(repo, element_path, dep_name=None): + element = { + 'kind': 'import', + 'sources': [ + repo.source_config() + ] + } + if dep_name: + element['depends'] = [dep_name] + + _yaml.dump(element, element_path) + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_track(cli, tmpdir, datafiles, ref_storage, kind): + project = str(datafiles) + dev_files_path = os.path.join(project, 'files', 'dev-files') + element_path = os.path.join(project, 'elements') + element_name = 'track-test-{}.bst'.format(kind) + + configure_project(project, { + 'ref-storage': ref_storage + }) + + # Create our repo object of the given source type with + # the dev files, and then collect the initial ref. + # + repo = create_repo(kind, str(tmpdir)) + repo.create(dev_files_path) + + # Generate the element + generate_element(repo, os.path.join(element_path, element_name)) + + # Assert that a fetch is needed + assert cli.get_element_state(project, element_name) == 'no reference' + + # Now first try to track it + result = cli.run(project=project, args=['source', 'track', element_name]) + result.assert_success() + + # And now fetch it: The Source has probably already cached the + # latest ref locally, but it is not required to have cached + # the associated content of the latest ref at track time, that + # is the job of fetch. + result = cli.run(project=project, args=['source', 'fetch', element_name]) + result.assert_success() + + # Assert that we are now buildable because the source is + # now cached. + assert cli.get_element_state(project, element_name) == 'buildable' + + # Assert there was a project.refs created, depending on the configuration + if ref_storage == 'project.refs': + assert os.path.exists(os.path.join(project, 'project.refs')) + else: + assert not os.path.exists(os.path.join(project, 'project.refs')) + + +# NOTE: +# +# This test checks that recursive tracking works by observing +# element states after running a recursive tracking operation. +# +# However, this test is ALSO valuable as it stresses the source +# plugins in a situation where many source plugins are operating +# at once on the same backing repository. +# +# Do not change this test to use a separate 'Repo' per element +# as that would defeat the purpose of the stress test, otherwise +# please refactor that aspect into another test. +# +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("amount", [(1), (10)]) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_track_recurse(cli, tmpdir, datafiles, kind, amount): + project = str(datafiles) + dev_files_path = os.path.join(project, 'files', 'dev-files') + element_path = os.path.join(project, 'elements') + + # Try to actually launch as many fetch jobs as possible at the same time + # + # This stresses the Source plugins and helps to ensure that + # they handle concurrent access to the store correctly. + cli.configure({ + 'scheduler': { + 'fetchers': amount, + } + }) + + # Create our repo object of the given source type with + # the dev files, and then collect the initial ref. + # + repo = create_repo(kind, str(tmpdir)) + repo.create(dev_files_path) + + # Write out our test targets + element_names = [] + last_element_name = None + for i in range(amount + 1): + element_name = 'track-test-{}-{}.bst'.format(kind, i + 1) + filename = os.path.join(element_path, element_name) + + element_names.append(element_name) + + generate_element(repo, filename, dep_name=last_element_name) + last_element_name = element_name + + # Assert that a fetch is needed + states = cli.get_element_states(project, [last_element_name]) + for element_name in element_names: + assert states[element_name] == 'no reference' + + # Now first try to track it + result = cli.run(project=project, args=[ + 'source', 'track', '--deps', 'all', + last_element_name]) + result.assert_success() + + # And now fetch it: The Source has probably already cached the + # latest ref locally, but it is not required to have cached + # the associated content of the latest ref at track time, that + # is the job of fetch. + result = cli.run(project=project, args=[ + 'source', 'fetch', '--deps', 'all', + last_element_name]) + result.assert_success() + + # Assert that the base is buildable and the rest are waiting + states = cli.get_element_states(project, [last_element_name]) + for element_name in element_names: + if element_name == element_names[0]: + assert states[element_name] == 'buildable' + else: + assert states[element_name] == 'waiting' + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_track_recurse_except(cli, tmpdir, datafiles, kind): + project = str(datafiles) + dev_files_path = os.path.join(project, 'files', 'dev-files') + element_path = os.path.join(project, 'elements') + element_dep_name = 'track-test-dep-{}.bst'.format(kind) + element_target_name = 'track-test-target-{}.bst'.format(kind) + + # Create our repo object of the given source type with + # the dev files, and then collect the initial ref. + # + repo = create_repo(kind, str(tmpdir)) + repo.create(dev_files_path) + + # Write out our test targets + generate_element(repo, os.path.join(element_path, element_dep_name)) + generate_element(repo, os.path.join(element_path, element_target_name), + dep_name=element_dep_name) + + # Assert that a fetch is needed + states = cli.get_element_states(project, [element_target_name]) + assert states[element_dep_name] == 'no reference' + assert states[element_target_name] == 'no reference' + + # Now first try to track it + result = cli.run(project=project, args=[ + 'source', 'track', '--deps', 'all', '--except', element_dep_name, + element_target_name]) + result.assert_success() + + # And now fetch it: The Source has probably already cached the + # latest ref locally, but it is not required to have cached + # the associated content of the latest ref at track time, that + # is the job of fetch. + result = cli.run(project=project, args=[ + 'source', 'fetch', '--deps', 'none', + element_target_name]) + result.assert_success() + + # Assert that the dependency is buildable and the target is waiting + states = cli.get_element_states(project, [element_target_name]) + assert states[element_dep_name] == 'no reference' + assert states[element_target_name] == 'waiting' + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_cross_junction(cli, tmpdir, datafiles, ref_storage, kind): + project = str(datafiles) + subproject_path = os.path.join(project, 'files', 'sub-project') + junction_path = os.path.join(project, 'elements', 'junction.bst') + etc_files = os.path.join(subproject_path, 'files', 'etc-files') + repo_element_path = os.path.join(subproject_path, 'elements', + 'import-etc-repo.bst') + + configure_project(project, { + 'ref-storage': ref_storage + }) + + repo = create_repo(kind, str(tmpdir.join('element_repo'))) + repo.create(etc_files) + + generate_element(repo, repo_element_path) + + generate_junction(str(tmpdir.join('junction_repo')), + subproject_path, junction_path, store_ref=False) + + # Track the junction itself first. + result = cli.run(project=project, args=['source', 'track', 'junction.bst']) + result.assert_success() + + assert cli.get_element_state(project, 'junction.bst:import-etc-repo.bst') == 'no reference' + + # Track the cross junction element. -J is not given, it is implied. + result = cli.run(project=project, args=['source', 'track', 'junction.bst:import-etc-repo.bst']) + + if ref_storage == 'inline': + # This is not allowed to track cross junction without project.refs. + result.assert_main_error(ErrorDomain.PIPELINE, 'untrackable-sources') + else: + result.assert_success() + + assert cli.get_element_state(project, 'junction.bst:import-etc-repo.bst') == 'buildable' + + assert os.path.exists(os.path.join(project, 'project.refs')) + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_track_include(cli, tmpdir, datafiles, ref_storage, kind): + project = str(datafiles) + dev_files_path = os.path.join(project, 'files', 'dev-files') + element_path = os.path.join(project, 'elements') + element_name = 'track-test-{}.bst'.format(kind) + + configure_project(project, { + 'ref-storage': ref_storage + }) + + # Create our repo object of the given source type with + # the dev files, and then collect the initial ref. + # + repo = create_repo(kind, str(tmpdir)) + ref = repo.create(dev_files_path) + + # Generate the element + element = { + 'kind': 'import', + '(@)': ['elements/sources.yml'] + } + sources = { + 'sources': [ + repo.source_config() + ] + } + + _yaml.dump(element, os.path.join(element_path, element_name)) + _yaml.dump(sources, os.path.join(element_path, 'sources.yml')) + + # Assert that a fetch is needed + assert cli.get_element_state(project, element_name) == 'no reference' + + # Now first try to track it + result = cli.run(project=project, args=['source', 'track', element_name]) + result.assert_success() + + # And now fetch it: The Source has probably already cached the + # latest ref locally, but it is not required to have cached + # the associated content of the latest ref at track time, that + # is the job of fetch. + result = cli.run(project=project, args=['source', 'fetch', element_name]) + result.assert_success() + + # Assert that we are now buildable because the source is + # now cached. + assert cli.get_element_state(project, element_name) == 'buildable' + + # Assert there was a project.refs created, depending on the configuration + if ref_storage == 'project.refs': + assert os.path.exists(os.path.join(project, 'project.refs')) + else: + assert not os.path.exists(os.path.join(project, 'project.refs')) + + new_sources = _yaml.load(os.path.join(element_path, 'sources.yml')) + + # Get all of the sources + assert 'sources' in new_sources + sources_list = _yaml.node_get(new_sources, list, 'sources') + assert len(sources_list) == 1 + + # Get the first source from the sources list + new_source = _yaml.node_get(new_sources, dict, 'sources', indices=[0]) + assert 'ref' in new_source + assert ref == _yaml.node_get(new_source, str, 'ref') + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_track_include_junction(cli, tmpdir, datafiles, ref_storage, kind): + project = str(datafiles) + dev_files_path = os.path.join(project, 'files', 'dev-files') + element_path = os.path.join(project, 'elements') + element_name = 'track-test-{}.bst'.format(kind) + subproject_path = os.path.join(project, 'files', 'sub-project') + sub_element_path = os.path.join(subproject_path, 'elements') + junction_path = os.path.join(element_path, 'junction.bst') + + configure_project(project, { + 'ref-storage': ref_storage + }) + + # Create our repo object of the given source type with + # the dev files, and then collect the initial ref. + # + repo = create_repo(kind, str(tmpdir.join('element_repo'))) + repo.create(dev_files_path) + + # Generate the element + element = { + 'kind': 'import', + '(@)': ['junction.bst:elements/sources.yml'] + } + sources = { + 'sources': [ + repo.source_config() + ] + } + + _yaml.dump(element, os.path.join(element_path, element_name)) + _yaml.dump(sources, os.path.join(sub_element_path, 'sources.yml')) + + generate_junction(str(tmpdir.join('junction_repo')), + subproject_path, junction_path, store_ref=True) + + result = cli.run(project=project, args=['source', 'track', 'junction.bst']) + result.assert_success() + + # Assert that a fetch is needed + assert cli.get_element_state(project, element_name) == 'no reference' + + # Now first try to track it + result = cli.run(project=project, args=['source', 'track', element_name]) + + # Assert there was a project.refs created, depending on the configuration + if ref_storage == 'inline': + # FIXME: We should expect an error. But only a warning is emitted + # result.assert_main_error(ErrorDomain.SOURCE, 'tracking-junction-fragment') + + assert 'junction.bst:elements/sources.yml: Cannot track source in a fragment from a junction' in result.stderr + else: + assert os.path.exists(os.path.join(project, 'project.refs')) + + # And now fetch it: The Source has probably already cached the + # latest ref locally, but it is not required to have cached + # the associated content of the latest ref at track time, that + # is the job of fetch. + result = cli.run(project=project, args=['source', 'fetch', element_name]) + result.assert_success() + + # Assert that we are now buildable because the source is + # now cached. + assert cli.get_element_state(project, element_name) == 'buildable' + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')]) +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_track_junction_included(cli, tmpdir, datafiles, ref_storage, kind): + project = str(datafiles) + element_path = os.path.join(project, 'elements') + subproject_path = os.path.join(project, 'files', 'sub-project') + junction_path = os.path.join(element_path, 'junction.bst') + + configure_project(project, { + 'ref-storage': ref_storage, + '(@)': ['junction.bst:test.yml'] + }) + + generate_junction(str(tmpdir.join('junction_repo')), + subproject_path, junction_path, store_ref=False) + + result = cli.run(project=project, args=['source', 'track', 'junction.bst']) + result.assert_success() diff --git a/buildstream/testing/_sourcetests/track_cross_junction.py b/buildstream/testing/_sourcetests/track_cross_junction.py new file mode 100644 index 000000000..ece3e0b8f --- /dev/null +++ b/buildstream/testing/_sourcetests/track_cross_junction.py @@ -0,0 +1,186 @@ +# +# Copyright (C) 2018 Codethink Limited +# Copyright (C) 2019 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 . +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import pytest + +from buildstream import _yaml +from .._utils import generate_junction +from .. import create_repo, ALL_REPO_KINDS +from .. import cli # pylint: disable=unused-import + +# Project directory +TOP_DIR = os.path.dirname(os.path.realpath(__file__)) +DATA_DIR = os.path.join(TOP_DIR, 'project') + + +def generate_element(repo, element_path, dep_name=None): + element = { + 'kind': 'import', + 'sources': [ + repo.source_config() + ] + } + if dep_name: + element['depends'] = [dep_name] + + _yaml.dump(element, element_path) + + +def generate_import_element(tmpdir, kind, project, name): + element_name = 'import-{}.bst'.format(name) + repo_element_path = os.path.join(project, 'elements', element_name) + files = str(tmpdir.join("imported_files_{}".format(name))) + os.makedirs(files) + + with open(os.path.join(files, '{}.txt'.format(name)), 'w') as f: + f.write(name) + + repo = create_repo(kind, str(tmpdir.join('element_{}_repo'.format(name)))) + repo.create(files) + + generate_element(repo, repo_element_path) + + return element_name + + +def generate_project(tmpdir, name, config=None): + if config is None: + config = {} + + project_name = 'project-{}'.format(name) + subproject_path = os.path.join(str(tmpdir.join(project_name))) + os.makedirs(os.path.join(subproject_path, 'elements')) + + project_conf = { + 'name': name, + 'element-path': 'elements' + } + project_conf.update(config) + _yaml.dump(project_conf, os.path.join(subproject_path, 'project.conf')) + + return project_name, subproject_path + + +def generate_simple_stack(project, name, dependencies): + element_name = '{}.bst'.format(name) + element_path = os.path.join(project, 'elements', element_name) + element = { + 'kind': 'stack', + 'depends': dependencies + } + _yaml.dump(element, element_path) + + return element_name + + +def generate_cross_element(project, subproject_name, import_name): + basename, _ = os.path.splitext(import_name) + return generate_simple_stack(project, 'import-{}-{}'.format(subproject_name, basename), + [{ + 'junction': '{}.bst'.format(subproject_name), + 'filename': import_name + }]) + + +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_cross_junction_multiple_projects(cli, tmpdir, kind): + tmpdir = tmpdir.join(kind) + + # Generate 3 projects: main, a, b + _, project = generate_project(tmpdir, 'main', {'ref-storage': 'project.refs'}) + project_a, project_a_path = generate_project(tmpdir, 'a') + project_b, project_b_path = generate_project(tmpdir, 'b') + + # Generate an element with a trackable source for each project + element_a = generate_import_element(tmpdir, kind, project_a_path, 'a') + element_b = generate_import_element(tmpdir, kind, project_b_path, 'b') + element_c = generate_import_element(tmpdir, kind, project, 'c') + + # Create some indirections to the elements with dependencies to test --deps + stack_a = generate_simple_stack(project_a_path, 'stack-a', [element_a]) + stack_b = generate_simple_stack(project_b_path, 'stack-b', [element_b]) + + # Create junctions for projects a and b in main. + junction_a = '{}.bst'.format(project_a) + junction_a_path = os.path.join(project, 'elements', junction_a) + generate_junction(tmpdir.join('repo_a'), project_a_path, junction_a_path, store_ref=False) + + junction_b = '{}.bst'.format(project_b) + junction_b_path = os.path.join(project, 'elements', junction_b) + generate_junction(tmpdir.join('repo_b'), project_b_path, junction_b_path, store_ref=False) + + # Track the junctions. + result = cli.run(project=project, args=['source', 'track', junction_a, junction_b]) + result.assert_success() + + # Import elements from a and b in to main. + imported_a = generate_cross_element(project, project_a, stack_a) + imported_b = generate_cross_element(project, project_b, stack_b) + + # Generate a top level stack depending on everything + all_bst = generate_simple_stack(project, 'all', [imported_a, imported_b, element_c]) + + # Track without following junctions. But explicitly also track the elements in project a. + result = cli.run(project=project, args=['source', 'track', + '--deps', 'all', + all_bst, + '{}:{}'.format(junction_a, stack_a)]) + result.assert_success() + + # Elements in project b should not be tracked. But elements in project a and main should. + expected = [element_c, + '{}:{}'.format(junction_a, element_a)] + assert set(result.get_tracked_elements()) == set(expected) + + +@pytest.mark.parametrize("kind", [(kind) for kind in ALL_REPO_KINDS]) +def test_track_exceptions(cli, tmpdir, kind): + tmpdir = tmpdir.join(kind) + + _, project = generate_project(tmpdir, 'main', {'ref-storage': 'project.refs'}) + project_a, project_a_path = generate_project(tmpdir, 'a') + + element_a = generate_import_element(tmpdir, kind, project_a_path, 'a') + element_b = generate_import_element(tmpdir, kind, project_a_path, 'b') + + all_bst = generate_simple_stack(project_a_path, 'all', [element_a, + element_b]) + + junction_a = '{}.bst'.format(project_a) + junction_a_path = os.path.join(project, 'elements', junction_a) + generate_junction(tmpdir.join('repo_a'), project_a_path, junction_a_path, store_ref=False) + + result = cli.run(project=project, args=['source', 'track', junction_a]) + result.assert_success() + + imported_b = generate_cross_element(project, project_a, element_b) + indirection = generate_simple_stack(project, 'indirection', [imported_b]) + + result = cli.run(project=project, + args=['source', 'track', '--deps', 'all', + '--except', indirection, + '{}:{}'.format(junction_a, all_bst), imported_b]) + result.assert_success() + + expected = ['{}:{}'.format(junction_a, element_a), + '{}:{}'.format(junction_a, element_b)] + assert set(result.get_tracked_elements()) == set(expected) diff --git a/buildstream/testing/_sourcetests/workspace.py b/buildstream/testing/_sourcetests/workspace.py new file mode 100644 index 000000000..5218f8f1e --- /dev/null +++ b/buildstream/testing/_sourcetests/workspace.py @@ -0,0 +1,161 @@ +# +# Copyright (C) 2018 Codethink Limited +# Copyright (C) 2019 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 . +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import shutil +import pytest + +from buildstream import _yaml +from .. import create_repo, ALL_REPO_KINDS +from .. import cli # pylint: disable=unused-import + +# Project directory +TOP_DIR = os.path.dirname(os.path.realpath(__file__)) +DATA_DIR = os.path.join(TOP_DIR, 'project') + + +class WorkspaceCreator(): + def __init__(self, cli, tmpdir, datafiles, project_path=None): + self.cli = cli + self.tmpdir = tmpdir + self.datafiles = datafiles + + if not project_path: + project_path = str(datafiles) + else: + shutil.copytree(str(datafiles), project_path) + + self.project_path = project_path + self.bin_files_path = os.path.join(project_path, 'files', 'bin-files') + + self.workspace_cmd = os.path.join(self.project_path, 'workspace_cmd') + + def create_workspace_element(self, kind, track, suffix='', workspace_dir=None, + element_attrs=None): + element_name = 'workspace-test-{}{}.bst'.format(kind, suffix) + element_path = os.path.join(self.project_path, 'elements') + if not workspace_dir: + workspace_dir = os.path.join(self.workspace_cmd, element_name) + if workspace_dir[-4:] == '.bst': + workspace_dir = workspace_dir[:-4] + + # Create our repo object of the given source type with + # the bin files, and then collect the initial ref. + repo = create_repo(kind, str(self.tmpdir)) + ref = repo.create(self.bin_files_path) + if track: + ref = None + + # Write out our test target + element = { + 'kind': 'import', + 'sources': [ + repo.source_config(ref=ref) + ] + } + if element_attrs: + element = {**element, **element_attrs} + _yaml.dump(element, + os.path.join(element_path, + element_name)) + return element_name, element_path, workspace_dir + + def create_workspace_elements(self, kinds, track, suffixs=None, workspace_dir_usr=None, + element_attrs=None): + + element_tuples = [] + + if suffixs is None: + suffixs = ['', ] * len(kinds) + else: + if len(suffixs) != len(kinds): + raise "terable error" + + for suffix, kind in zip(suffixs, kinds): + element_name, _, workspace_dir = \ + self.create_workspace_element(kind, track, suffix, workspace_dir_usr, + element_attrs) + element_tuples.append((element_name, workspace_dir)) + + # Assert that there is no reference, a track & fetch is needed + states = self.cli.get_element_states(self.project_path, [ + e for e, _ in element_tuples + ]) + if track: + assert not any(states[e] != 'no reference' for e, _ in element_tuples) + else: + assert not any(states[e] != 'fetch needed' for e, _ in element_tuples) + + return element_tuples + + def open_workspaces(self, kinds, track, suffixs=None, workspace_dir=None, + element_attrs=None, no_checkout=False): + + element_tuples = self.create_workspace_elements(kinds, track, suffixs, workspace_dir, + element_attrs) + os.makedirs(self.workspace_cmd, exist_ok=True) + + # Now open the workspace, this should have the effect of automatically + # tracking & fetching the source from the repo. + args = ['workspace', 'open'] + if track: + args.append('--track') + if no_checkout: + args.append('--no-checkout') + if workspace_dir is not None: + assert len(element_tuples) == 1, "test logic error" + _, workspace_dir = element_tuples[0] + args.extend(['--directory', workspace_dir]) + + args.extend([element_name for element_name, workspace_dir_suffix in element_tuples]) + result = self.cli.run(cwd=self.workspace_cmd, project=self.project_path, args=args) + + result.assert_success() + + if not no_checkout: + # Assert that we are now buildable because the source is now cached. + states = self.cli.get_element_states(self.project_path, [ + e for e, _ in element_tuples + ]) + assert not any(states[e] != 'buildable' for e, _ in element_tuples) + + # Check that the executable hello file is found in each workspace + for _, workspace in element_tuples: + filename = os.path.join(workspace, 'usr', 'bin', 'hello') + assert os.path.exists(filename) + + return element_tuples + + +def open_workspace(cli, tmpdir, datafiles, kind, track, suffix='', workspace_dir=None, + project_path=None, element_attrs=None, no_checkout=False): + workspace_object = WorkspaceCreator(cli, tmpdir, datafiles, project_path) + workspaces = workspace_object.open_workspaces((kind, ), track, (suffix, ), workspace_dir, + element_attrs, no_checkout) + assert len(workspaces) == 1 + element_name, workspace = workspaces[0] + return element_name, workspace_object.project_path, workspace + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("kind", ALL_REPO_KINDS) +def test_open(cli, tmpdir, datafiles, kind): + open_workspace(cli, tmpdir, datafiles, kind, False) diff --git a/buildstream/testing/_utils/__init__.py b/buildstream/testing/_utils/__init__.py new file mode 100644 index 000000000..b419d72b7 --- /dev/null +++ b/buildstream/testing/_utils/__init__.py @@ -0,0 +1,10 @@ +import os + +from buildstream import _yaml +from .junction import generate_junction + + +def configure_project(path, config): + config['name'] = 'test' + config['element-path'] = 'elements' + _yaml.dump(config, os.path.join(path, 'project.conf')) diff --git a/buildstream/testing/_utils/junction.py b/buildstream/testing/_utils/junction.py new file mode 100644 index 000000000..ca059eb8b --- /dev/null +++ b/buildstream/testing/_utils/junction.py @@ -0,0 +1,83 @@ +import subprocess +import pytest + +from buildstream import _yaml +from .. import Repo +from .site import HAVE_GIT, GIT, GIT_ENV + + +# generate_junction() +# +# Generates a junction element with a git repository +# +# Args: +# tmpdir: The tmpdir fixture, for storing the generated git repo +# subproject_path: The path for the subproject, to add to the git repo +# junction_path: The location to store the generated junction element +# store_ref: Whether to store the ref in the junction.bst file +# +# Returns: +# (str): The ref +# +def generate_junction(tmpdir, subproject_path, junction_path, *, store_ref=True): + # Create a repo to hold the subproject and generate + # a junction element for it + # + repo = _SimpleGit(str(tmpdir)) + source_ref = ref = repo.create(subproject_path) + if not store_ref: + source_ref = None + + element = { + 'kind': 'junction', + 'sources': [ + repo.source_config(ref=source_ref) + ] + } + _yaml.dump(element, junction_path) + + return ref + + +# A barebones Git Repo class to use for generating junctions +class _SimpleGit(Repo): + def __init__(self, directory, subdir='repo'): + if not HAVE_GIT: + pytest.skip('git is not available') + super().__init__(directory, subdir) + + def create(self, directory): + self.copy_directory(directory, self.repo) + self._run_git('init', '.') + self._run_git('add', '.') + self._run_git('commit', '-m', 'Initial commit') + return self.latest_commit() + + def latest_commit(self): + return self._run_git( + 'rev-parse', 'HEAD', + stdout=subprocess.PIPE, + universal_newlines=True, + ).stdout.strip() + + def source_config(self, ref=None, checkout_submodules=None): + config = { + 'kind': 'git', + 'url': 'file://' + self.repo, + 'track': 'master' + } + if ref is not None: + config['ref'] = ref + if checkout_submodules is not None: + config['checkout-submodules'] = checkout_submodules + + return config + + def _run_git(self, *args, **kwargs): + argv = [GIT] + argv.extend(args) + if 'env' not in kwargs: + kwargs['env'] = dict(GIT_ENV, PWD=self.repo) + kwargs.setdefault('cwd', self.repo) + kwargs.setdefault('check', True) + return subprocess.run(argv, **kwargs) diff --git a/buildstream/testing/_utils/site.py b/buildstream/testing/_utils/site.py new file mode 100644 index 000000000..54c5b467b --- /dev/null +++ b/buildstream/testing/_utils/site.py @@ -0,0 +1,46 @@ +# Some things resolved about the execution site, +# so we dont have to repeat this everywhere +# +import os +import sys +import platform + +from buildstream import _site, utils, ProgramNotFoundError + + +try: + GIT = utils.get_host_tool('git') + HAVE_GIT = True + GIT_ENV = { + 'GIT_AUTHOR_DATE': '1320966000 +0200', + 'GIT_AUTHOR_NAME': 'tomjon', + 'GIT_AUTHOR_EMAIL': 'tom@jon.com', + 'GIT_COMMITTER_DATE': '1320966000 +0200', + 'GIT_COMMITTER_NAME': 'tomjon', + 'GIT_COMMITTER_EMAIL': 'tom@jon.com' + } +except ProgramNotFoundError: + GIT = None + HAVE_GIT = False + GIT_ENV = dict() + +try: + utils.get_host_tool('bwrap') + HAVE_BWRAP = True + HAVE_BWRAP_JSON_STATUS = _site.get_bwrap_version() >= (0, 3, 2) +except ProgramNotFoundError: + HAVE_BWRAP = False + HAVE_BWRAP_JSON_STATUS = False + +IS_LINUX = os.getenv('BST_FORCE_BACKEND', sys.platform).startswith('linux') +IS_WSL = (IS_LINUX and 'Microsoft' in platform.uname().release) +IS_WINDOWS = (os.name == 'nt') + +if not IS_LINUX: + HAVE_SANDBOX = True # fallback to a chroot sandbox on unix +elif IS_WSL: + HAVE_SANDBOX = False # Sandboxes are inoperable under WSL due to lack of FUSE +elif IS_LINUX and HAVE_BWRAP: + HAVE_SANDBOX = True +else: + HAVE_SANDBOX = False diff --git a/buildstream/testing/integration.py b/buildstream/testing/integration.py new file mode 100644 index 000000000..e29f480ea --- /dev/null +++ b/buildstream/testing/integration.py @@ -0,0 +1,51 @@ +# +# Copyright (C) 2017 Codethink Limited +# Copyright (C) 2018 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 . +""" +Integration - tools for inspecting the output of plugin integration tests +========================================================================= + +This module contains utilities for inspecting the artifacts produced during +integration tests. +""" + +import os + + +# Return a list of files relative to the given directory +def walk_dir(root): + for dirname, dirnames, filenames in os.walk(root): + # ensure consistent traversal order, needed for consistent + # handling of symlinks. + dirnames.sort() + filenames.sort() + + # print path to all subdirectories first. + for subdirname in dirnames: + yield os.path.join(dirname, subdirname)[len(root):] + + # print path to all filenames. + for filename in filenames: + yield os.path.join(dirname, filename)[len(root):] + + +# Ensure that a directory contains the given filenames. +def assert_contains(directory, expected): + missing = set(expected) + missing.difference_update(walk_dir(directory)) + if missing: + raise AssertionError("Missing {} expected elements from list: {}" + .format(len(missing), missing)) diff --git a/buildstream/testing/repo.py b/buildstream/testing/repo.py new file mode 100644 index 000000000..c1538685d --- /dev/null +++ b/buildstream/testing/repo.py @@ -0,0 +1,109 @@ +# +# Copyright (C) 2016-2018 Codethink Limited +# Copyright (C) 2019 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 . + +""" +Repo - Utility class for testing source plugins +=============================================== + + +""" +import os +import shutil + + +class Repo(): + """Repo() + + Abstract class providing scaffolding for generating data to be + used with various sources. Subclasses of Repo may be registered to + run through the suite of generic source plugin tests provided in + buildstream.testing. + + Args: + directory (str): The base temp directory for the test + subdir (str): The subdir for the repo, in case there is more than one + + """ + def __init__(self, directory, subdir='repo'): + + # The working directory for the repo object + # + self.directory = os.path.abspath(directory) + + # The directory the actual repo will be stored in + self.repo = os.path.join(self.directory, subdir) + + os.makedirs(self.repo, exist_ok=True) + + def create(self, directory): + """Create a repository in self.directory and add the initial content + + Args: + directory: A directory with content to commit + + Returns: + (smth): A new ref corresponding to this commit, which can + be passed as the ref in the Repo.source_config() API. + """ + raise NotImplementedError("create method has not been implemeted") + + def source_config(self, ref=None): + """ + Args: + ref (smth): An optional abstract ref object, usually a string. + + Returns: + (dict): A configuration which can be serialized as a + source when generating an element file on the fly + + """ + raise NotImplementedError("source_config method has not been implemeted") + + def copy_directory(self, src, dest): + """ Copies the content of src to the directory dest + + Like shutil.copytree(), except dest is expected + to exist. + + Args: + src (str): The source directory + dest (str): The destination directory + """ + for filename in os.listdir(src): + src_path = os.path.join(src, filename) + dest_path = os.path.join(dest, filename) + if os.path.isdir(src_path): + shutil.copytree(src_path, dest_path) + else: + shutil.copy2(src_path, dest_path) + + def copy(self, dest): + """Creates a copy of this repository in the specified destination. + + Args: + dest (str): The destination directory + + Returns: + (Repo): A Repo object for the new repository. + """ + subdir = self.repo[len(self.directory):].lstrip(os.sep) + new_dir = os.path.join(dest, subdir) + os.makedirs(new_dir, exist_ok=True) + self.copy_directory(self.repo, new_dir) + repo_type = type(self) + new_repo = repo_type(dest, subdir) + return new_repo diff --git a/buildstream/testing/runcli.py b/buildstream/testing/runcli.py new file mode 100644 index 000000000..72bdce09e --- /dev/null +++ b/buildstream/testing/runcli.py @@ -0,0 +1,857 @@ +# +# Copyright (C) 2017 Codethink Limited +# Copyright (C) 2018 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 . +""" +runcli - Test fixtures used for running BuildStream commands +============================================================ + +:function:'cli' Use result = cli.run([arg1, arg2]) to run buildstream commands + +:function:'cli_integration' A variant of the main fixture that keeps persistent + artifact and source caches. It also does not use + the click test runner to avoid deadlock issues when + running `bst shell`, but unfortunately cannot produce + nice stacktraces. + +""" + + +import os +import re +import sys +import shutil +import tempfile +import itertools +import traceback +from contextlib import contextmanager, ExitStack +from ruamel import yaml +import pytest + +# XXX Using pytest private internals here +# +# We use pytest internals to capture the stdout/stderr during +# a run of the buildstream CLI. We do this because click's +# CliRunner convenience API (click.testing module) does not support +# separation of stdout/stderr. +# +from _pytest.capture import MultiCapture, FDCapture, FDCaptureBinary + +# Import the main cli entrypoint +from buildstream._frontend import cli as bst_cli +from buildstream import _yaml +from buildstream._cas import CASCache + +# Special private exception accessor, for test case purposes +from buildstream._exceptions import BstError, get_last_exception, get_last_task_error + + +# Wrapper for the click.testing result +class Result(): + + def __init__(self, + exit_code=None, + exception=None, + exc_info=None, + output=None, + stderr=None): + self.exit_code = exit_code + self.exc = exception + self.exc_info = exc_info + self.output = output + self.stderr = stderr + self.unhandled_exception = False + + # The last exception/error state is stored at exception + # creation time in BstError(), but this breaks down with + # recoverable errors where code blocks ignore some errors + # and fallback to alternative branches. + # + # For this reason, we just ignore the exception and errors + # in the case that the exit code reported is 0 (success). + # + if self.exit_code != 0: + + # Check if buildstream failed to handle an + # exception, topevel CLI exit should always + # be a SystemExit exception. + # + if not isinstance(exception, SystemExit): + self.unhandled_exception = True + + self.exception = get_last_exception() + self.task_error_domain, \ + self.task_error_reason = get_last_task_error() + else: + self.exception = None + self.task_error_domain = None + self.task_error_reason = None + + # assert_success() + # + # Asserts that the buildstream session completed successfully + # + # Args: + # fail_message (str): An optional message to override the automatic + # assertion error messages + # Raises: + # (AssertionError): If the session did not complete successfully + # + def assert_success(self, fail_message=''): + assert self.exit_code == 0, fail_message + assert self.exc is None, fail_message + assert self.exception is None, fail_message + assert self.unhandled_exception is False + + # assert_main_error() + # + # Asserts that the buildstream session failed, and that + # the main process error report is as expected + # + # Args: + # error_domain (ErrorDomain): The domain of the error which occurred + # error_reason (any): The reason field of the error which occurred + # fail_message (str): An optional message to override the automatic + # assertion error messages + # debug (bool): If true, prints information regarding the exit state of the result() + # Raises: + # (AssertionError): If any of the assertions fail + # + def assert_main_error(self, + error_domain, + error_reason, + fail_message='', + *, debug=False): + if debug: + print( + """ + Exit code: {} + Exception: {} + Domain: {} + Reason: {} + """.format( + self.exit_code, + self.exception, + self.exception.domain, + self.exception.reason + )) + assert self.exit_code == -1, fail_message + assert self.exc is not None, fail_message + assert self.exception is not None, fail_message + assert isinstance(self.exception, BstError), fail_message + assert self.unhandled_exception is False + + assert self.exception.domain == error_domain, fail_message + assert self.exception.reason == error_reason, fail_message + + # assert_task_error() + # + # Asserts that the buildstream session failed, and that + # the child task error which caused buildstream to exit + # is as expected. + # + # Args: + # error_domain (ErrorDomain): The domain of the error which occurred + # error_reason (any): The reason field of the error which occurred + # fail_message (str): An optional message to override the automatic + # assertion error messages + # Raises: + # (AssertionError): If any of the assertions fail + # + def assert_task_error(self, + error_domain, + error_reason, + fail_message=''): + + assert self.exit_code == -1, fail_message + assert self.exc is not None, fail_message + assert self.exception is not None, fail_message + assert isinstance(self.exception, BstError), fail_message + assert self.unhandled_exception is False + + assert self.task_error_domain == error_domain, fail_message + assert self.task_error_reason == error_reason, fail_message + + # assert_shell_error() + # + # Asserts that the buildstream created a shell and that the task in the + # shell failed. + # + # Args: + # fail_message (str): An optional message to override the automatic + # assertion error messages + # Raises: + # (AssertionError): If any of the assertions fail + # + def assert_shell_error(self, fail_message=''): + assert self.exit_code == 1, fail_message + + # get_start_order() + # + # Gets the list of elements processed in a given queue, in the + # order of their first appearances in the session. + # + # Args: + # activity (str): The queue activity name (like 'fetch') + # + # Returns: + # (list): A list of element names in the order which they first appeared in the result + # + def get_start_order(self, activity): + results = re.findall(r'\[\s*{}:(\S+)\s*\]\s*START\s*.*\.log'.format(activity), self.stderr) + if results is None: + return [] + return list(results) + + # get_tracked_elements() + # + # Produces a list of element names on which tracking occurred + # during the session. + # + # This is done by parsing the buildstream stderr log + # + # Returns: + # (list): A list of element names + # + def get_tracked_elements(self): + tracked = re.findall(r'\[\s*track:(\S+)\s*]', self.stderr) + if tracked is None: + return [] + + return list(tracked) + + def get_pushed_elements(self): + pushed = re.findall(r'\[\s*push:(\S+)\s*\]\s*INFO\s*Pushed artifact', self.stderr) + if pushed is None: + return [] + + return list(pushed) + + def get_pulled_elements(self): + pulled = re.findall(r'\[\s*pull:(\S+)\s*\]\s*INFO\s*Pulled artifact', self.stderr) + if pulled is None: + return [] + + return list(pulled) + + +class Cli(): + + def __init__(self, directory, verbose=True, default_options=None): + self.directory = directory + self.config = None + self.verbose = verbose + self.artifact = TestArtifact() + + if default_options is None: + default_options = [] + + self.default_options = default_options + + # configure(): + # + # Serializes a user configuration into a buildstream.conf + # to use for this test cli. + # + # Args: + # config (dict): The user configuration to use + # + def configure(self, config): + if self.config is None: + self.config = {} + + for key, val in config.items(): + self.config[key] = val + + # remove_artifact_from_cache(): + # + # Remove given element artifact from artifact cache + # + # Args: + # project (str): The project path under test + # element_name (str): The name of the element artifact + # cache_dir (str): Specific cache dir to remove artifact from + # + def remove_artifact_from_cache(self, project, element_name, + *, cache_dir=None): + # Read configuration to figure out where artifacts are stored + if not cache_dir: + default = os.path.join(project, 'cache') + + if self.config is not None: + cache_dir = self.config.get('cachedir', default) + else: + cache_dir = default + + self.artifact.remove_artifact_from_cache(cache_dir, element_name) + + # run(): + # + # Runs buildstream with the given arguments, additionally + # also passes some global options to buildstream in order + # to stay contained in the testing environment. + # + # Args: + # configure (bool): Whether to pass a --config argument + # project (str): An optional path to a project + # silent (bool): Whether to pass --no-verbose + # env (dict): Environment variables to temporarily set during the test + # args (list): A list of arguments to pass buildstream + # binary_capture (bool): Whether to capture the stdout/stderr as binary + # + def run(self, configure=True, project=None, silent=False, env=None, + cwd=None, options=None, args=None, binary_capture=False): + if args is None: + args = [] + if options is None: + options = [] + + # We may have been passed e.g. pathlib.Path or py.path + args = [str(x) for x in args] + project = str(project) + + options = self.default_options + options + + with ExitStack() as stack: + bst_args = ['--no-colors'] + + if silent: + bst_args += ['--no-verbose'] + + if configure: + config_file = stack.enter_context( + configured(self.directory, self.config) + ) + bst_args += ['--config', config_file] + + if project: + bst_args += ['--directory', project] + + for option, value in options: + bst_args += ['--option', option, value] + + bst_args += args + + if cwd is not None: + stack.enter_context(chdir(cwd)) + + if env is not None: + stack.enter_context(environment(env)) + + # Ensure we have a working stdout - required to work + # around a bug that appears to cause AIX to close + # sys.__stdout__ after setup.py + try: + sys.__stdout__.fileno() + except ValueError: + sys.__stdout__ = open('/dev/stdout', 'w') + + result = self._invoke(bst_cli, bst_args, binary_capture=binary_capture) + + # Some informative stdout we can observe when anything fails + if self.verbose: + command = "bst " + " ".join(bst_args) + print("BuildStream exited with code {} for invocation:\n\t{}" + .format(result.exit_code, command)) + if result.output: + print("Program output was:\n{}".format(result.output)) + if result.stderr: + print("Program stderr was:\n{}".format(result.stderr)) + + if result.exc_info and result.exc_info[0] != SystemExit: + traceback.print_exception(*result.exc_info) + + return result + + def _invoke(self, cli_object, args=None, binary_capture=False): + exc_info = None + exception = None + exit_code = 0 + + # Temporarily redirect sys.stdin to /dev/null to ensure that + # Popen doesn't attempt to read pytest's dummy stdin. + old_stdin = sys.stdin + with open(os.devnull) as devnull: + sys.stdin = devnull + capture_kind = FDCaptureBinary if binary_capture else FDCapture + capture = MultiCapture(out=True, err=True, in_=False, Capture=capture_kind) + capture.start_capturing() + + try: + cli_object.main(args=args or (), prog_name=cli_object.name) + except SystemExit as e: + if e.code != 0: + exception = e + + exc_info = sys.exc_info() + + exit_code = e.code + if not isinstance(exit_code, int): + sys.stdout.write('Program exit code was not an integer: ') + sys.stdout.write(str(exit_code)) + sys.stdout.write('\n') + exit_code = 1 + except Exception as e: # pylint: disable=broad-except + exception = e + exit_code = -1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + + sys.stdin = old_stdin + out, err = capture.readouterr() + capture.stop_capturing() + + return Result(exit_code=exit_code, + exception=exception, + exc_info=exc_info, + output=out, + stderr=err) + + # Fetch an element state by name by + # invoking bst show on the project with the CLI + # + # If you need to get the states of multiple elements, + # then use get_element_states(s) instead. + # + def get_element_state(self, project, element_name): + result = self.run(project=project, silent=True, args=[ + 'show', + '--deps', 'none', + '--format', '%{state}', + element_name + ]) + result.assert_success() + return result.output.strip() + + # Fetch the states of elements for a given target / deps + # + # Returns a dictionary with the element names as keys + # + def get_element_states(self, project, targets, deps='all'): + result = self.run(project=project, silent=True, args=[ + 'show', + '--deps', deps, + '--format', '%{name}||%{state}', + *targets + ]) + result.assert_success() + lines = result.output.splitlines() + states = {} + for line in lines: + split = line.split(sep='||') + states[split[0]] = split[1] + return states + + # Fetch an element's cache key by invoking bst show + # on the project with the CLI + # + def get_element_key(self, project, element_name): + result = self.run(project=project, silent=True, args=[ + 'show', + '--deps', 'none', + '--format', '%{full-key}', + element_name + ]) + result.assert_success() + return result.output.strip() + + # Get the decoded config of an element. + # + def get_element_config(self, project, element_name): + result = self.run(project=project, silent=True, args=[ + 'show', + '--deps', 'none', + '--format', '%{config}', + element_name + ]) + + result.assert_success() + return yaml.safe_load(result.output) + + # Fetch the elements that would be in the pipeline with the given + # arguments. + # + def get_pipeline(self, project, elements, except_=None, scope='plan'): + if except_ is None: + except_ = [] + + args = ['show', '--deps', scope, '--format', '%{name}'] + args += list(itertools.chain.from_iterable(zip(itertools.repeat('--except'), except_))) + + result = self.run(project=project, silent=True, args=args + elements) + result.assert_success() + return result.output.splitlines() + + +class CliIntegration(Cli): + + # run() + # + # This supports the same arguments as Cli.run() and additionally + # it supports the project_config keyword argument. + # + # This will first load the project.conf file from the specified + # project directory ('project' keyword argument) and perform substitutions + # of any {project_dir} specified in the existing project.conf. + # + # If the project_config parameter is specified, it is expected to + # be a dictionary of additional project configuration options, and + # will be composited on top of the already loaded project.conf + # + def run(self, *args, project_config=None, **kwargs): + + # First load the project.conf and substitute {project_dir} + # + # Save the original project.conf, because we will run more than + # once in the same temp directory + # + project_directory = kwargs['project'] + project_filename = os.path.join(project_directory, 'project.conf') + project_backup = os.path.join(project_directory, 'project.conf.backup') + project_load_filename = project_filename + + if not os.path.exists(project_backup): + shutil.copy(project_filename, project_backup) + else: + project_load_filename = project_backup + + with open(project_load_filename) as f: + config = f.read() + config = config.format(project_dir=project_directory) + + if project_config is not None: + + # If a custom project configuration dictionary was + # specified, composite it on top of the already + # substituted base project configuration + # + base_config = _yaml.load_data(config) + + # In order to leverage _yaml.composite_dict(), both + # dictionaries need to be loaded via _yaml.load_data() first + # + with tempfile.TemporaryDirectory(dir=project_directory) as scratchdir: + + temp_project = os.path.join(scratchdir, 'project.conf') + with open(temp_project, 'w') as f: + yaml.safe_dump(project_config, f) + + project_config = _yaml.load(temp_project) + + _yaml.composite_dict(base_config, project_config) + + base_config = _yaml.node_sanitize(base_config) + _yaml.dump(base_config, project_filename) + + else: + + # Otherwise, just dump it as is + with open(project_filename, 'w') as f: + f.write(config) + + return super().run(*args, **kwargs) + + +class CliRemote(CliIntegration): + + # ensure_services(): + # + # Make sure that required services are configured and that + # non-required ones are not. + # + # Args: + # actions (bool): Whether to use the 'action-cache' service + # artifacts (bool): Whether to use the 'artifact-cache' service + # execution (bool): Whether to use the 'execution' service + # sources (bool): Whether to use the 'source-cache' service + # storage (bool): Whether to use the 'storage' service + # + # Returns a list of configured services (by names). + # + def ensure_services(self, actions=True, execution=True, storage=True, + artifacts=False, sources=False): + # Build a list of configured services by name: + configured_services = [] + if not self.config: + return configured_services + + if 'remote-execution' in self.config: + rexec_config = self.config['remote-execution'] + + if 'action-cache-service' in rexec_config: + if actions: + configured_services.append('action-cache') + else: + rexec_config.pop('action-cache-service') + + if 'execution-service' in rexec_config: + if execution: + configured_services.append('execution') + else: + rexec_config.pop('execution-service') + + if 'storage-service' in rexec_config: + if storage: + configured_services.append('storage') + else: + rexec_config.pop('storage-service') + + if 'artifacts' in self.config: + if artifacts: + configured_services.append('artifact-cache') + else: + self.config.pop('artifacts') + + if 'source-caches' in self.config: + if sources: + configured_services.append('source-cache') + else: + self.config.pop('source-caches') + + return configured_services + + +class TestArtifact(): + + # remove_artifact_from_cache(): + # + # Remove given element artifact from artifact cache + # + # Args: + # cache_dir (str): Specific cache dir to remove artifact from + # element_name (str): The name of the element artifact + # + def remove_artifact_from_cache(self, cache_dir, element_name): + + cache_dir = os.path.join(cache_dir, 'cas', 'refs', 'heads') + + cache_dir = os.path.splitext(os.path.join(cache_dir, 'test', element_name))[0] + shutil.rmtree(cache_dir) + + # is_cached(): + # + # Check if given element has a cached artifact + # + # Args: + # cache_dir (str): Specific cache dir to check + # element (Element): The element object + # element_key (str): The element's cache key + # + # Returns: + # (bool): If the cache contains the element's artifact + # + def is_cached(self, cache_dir, element, element_key): + + cas = CASCache(str(cache_dir)) + artifact_ref = element.get_artifact_name(element_key) + return cas.contains(artifact_ref) + + # get_digest(): + # + # Get the digest for a given element's artifact + # + # Args: + # cache_dir (str): Specific cache dir to check + # element (Element): The element object + # element_key (str): The element's cache key + # + # Returns: + # (Digest): The digest stored in the ref + # + def get_digest(self, cache_dir, element, element_key): + + cas = CASCache(str(cache_dir)) + artifact_ref = element.get_artifact_name(element_key) + digest = cas.resolve_ref(artifact_ref) + return digest + + # extract_buildtree(): + # + # Context manager for extracting an elements artifact buildtree for + # inspection. + # + # Args: + # tmpdir (LocalPath): pytest fixture for the tests tmp dir + # digest (Digest): The element directory digest to extract + # + # Yields: + # (str): path to extracted buildtree directory, does not guarantee + # existence. + @contextmanager + def extract_buildtree(self, tmpdir, digest): + with self._extract_subdirectory(tmpdir, digest, 'buildtree') as extract: + yield extract + + # _extract_subdirectory(): + # + # Context manager for extracting an element artifact for inspection, + # providing an expected path for a given subdirectory + # + # Args: + # tmpdir (LocalPath): pytest fixture for the tests tmp dir + # digest (Digest): The element directory digest to extract + # subdir (str): Subdirectory to path + # + # Yields: + # (str): path to extracted subdir directory, does not guarantee + # existence. + @contextmanager + def _extract_subdirectory(self, tmpdir, digest, subdir): + with tempfile.TemporaryDirectory() as extractdir: + try: + cas = CASCache(str(tmpdir)) + cas.checkout(extractdir, digest) + yield os.path.join(extractdir, subdir) + except FileNotFoundError: + yield None + + +# Main fixture +# +# Use result = cli.run([arg1, arg2]) to run buildstream commands +# +@pytest.fixture() +def cli(tmpdir): + directory = os.path.join(str(tmpdir), 'cache') + os.makedirs(directory) + return Cli(directory) + + +# A variant of the main fixture that keeps persistent artifact and +# source caches. +# +# It also does not use the click test runner to avoid deadlock issues +# when running `bst shell`, but unfortunately cannot produce nice +# stacktraces. +@pytest.fixture() +def cli_integration(tmpdir, integration_cache): + directory = os.path.join(str(tmpdir), 'cache') + os.makedirs(directory) + + if os.environ.get('BST_FORCE_BACKEND') == 'unix': + fixture = CliIntegration(directory, default_options=[('linux', 'False')]) + else: + fixture = CliIntegration(directory) + + # We want to cache sources for integration tests more permanently, + # to avoid downloading the huge base-sdk repeatedly + fixture.configure({ + 'cachedir': integration_cache.cachedir, + 'sourcedir': integration_cache.sources, + }) + + yield fixture + + # remove following folders if necessary + try: + shutil.rmtree(os.path.join(integration_cache.cachedir, 'build')) + except FileNotFoundError: + pass + try: + shutil.rmtree(os.path.join(integration_cache.cachedir, 'tmp')) + except FileNotFoundError: + pass + + +# A variant of the main fixture that is configured for remote-execution. +# +# It also does not use the click test runner to avoid deadlock issues +# when running `bst shell`, but unfortunately cannot produce nice +# stacktraces. +@pytest.fixture() +def cli_remote_execution(tmpdir, remote_services): + directory = os.path.join(str(tmpdir), 'cache') + os.makedirs(directory) + + fixture = CliRemote(directory) + + if remote_services.artifact_service: + fixture.configure({'artifacts': [{ + 'url': remote_services.artifact_service, + }]}) + + remote_execution = {} + if remote_services.action_service: + remote_execution['action-cache-service'] = { + 'url': remote_services.action_service, + } + if remote_services.exec_service: + remote_execution['execution-service'] = { + 'url': remote_services.exec_service, + } + if remote_services.storage_service: + remote_execution['storage-service'] = { + 'url': remote_services.storage_service, + } + if remote_execution: + fixture.configure({'remote-execution': remote_execution}) + + if remote_services.source_service: + fixture.configure({'source-caches': [{ + 'url': remote_services.source_service, + }]}) + + return fixture + + +@contextmanager +def chdir(directory): + old_dir = os.getcwd() + os.chdir(directory) + yield + os.chdir(old_dir) + + +@contextmanager +def environment(env): + + old_env = {} + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value + + yield + + for key, value in old_env.items(): + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value + + +@contextmanager +def configured(directory, config=None): + + # Ensure we've at least relocated the caches to a temp directory + if not config: + config = {} + + if not config.get('sourcedir', False): + config['sourcedir'] = os.path.join(directory, 'sources') + if not config.get('cachedir', False): + config['cachedir'] = directory + if not config.get('logdir', False): + config['logdir'] = os.path.join(directory, 'logs') + + # Dump it and yield the filename for test scripts to feed it + # to buildstream as an artument + filename = os.path.join(directory, "buildstream.conf") + _yaml.dump(config, filename) + + yield filename diff --git a/doc/source/core_framework.rst b/doc/source/core_framework.rst index 127b5b2c6..fe2a59c5c 100644 --- a/doc/source/core_framework.rst +++ b/doc/source/core_framework.rst @@ -20,4 +20,4 @@ useful for working on BuildStream itself. buildstream.scriptelement buildstream.sandbox.sandbox buildstream.utils - buildstream.plugintestutils + buildstream.testing diff --git a/tests/artifactcache/cache_size.py b/tests/artifactcache/cache_size.py index c6ba15794..09f0f25d0 100644 --- a/tests/artifactcache/cache_size.py +++ b/tests/artifactcache/cache_size.py @@ -4,7 +4,7 @@ from unittest import mock from buildstream import _yaml from buildstream._cas.cascache import CACHE_SIZE_FILE from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli +from buildstream.testing import cli from tests.testutils import create_element_size diff --git a/tests/artifactcache/config.py b/tests/artifactcache/config.py index c3b09bfb1..8adbb3274 100644 --- a/tests/artifactcache/config.py +++ b/tests/artifactcache/config.py @@ -10,7 +10,7 @@ from buildstream.utils import _deduplicate from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli +from buildstream.testing.runcli import cli DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/artifactcache/expiry.py b/tests/artifactcache/expiry.py index 7ada656ab..6aa518f1a 100644 --- a/tests/artifactcache/expiry.py +++ b/tests/artifactcache/expiry.py @@ -24,7 +24,7 @@ from unittest import mock import pytest from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils import cli +from buildstream.testing import cli from tests.testutils import create_element_size, update_element_size, wait_for_cache_granularity diff --git a/tests/artifactcache/junctions.py b/tests/artifactcache/junctions.py index 9b7ebe2ec..1eb67b659 100644 --- a/tests/artifactcache/junctions.py +++ b/tests/artifactcache/junctions.py @@ -3,7 +3,7 @@ import shutil import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli +from buildstream.testing import cli from tests.testutils import create_artifact_share diff --git a/tests/artifactcache/pull.py b/tests/artifactcache/pull.py index d75b6d3c7..96fdef812 100644 --- a/tests/artifactcache/pull.py +++ b/tests/artifactcache/pull.py @@ -8,7 +8,7 @@ from buildstream import _yaml, _signals, utils from buildstream._context import Context from buildstream._project import Project from buildstream._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 -from buildstream.plugintestutils import cli +from buildstream.testing import cli from tests.testutils import create_artifact_share diff --git a/tests/artifactcache/push.py b/tests/artifactcache/push.py index 426d22f24..7c117e344 100644 --- a/tests/artifactcache/push.py +++ b/tests/artifactcache/push.py @@ -9,7 +9,7 @@ from buildstream._context import Context from buildstream._project import Project from buildstream._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 from buildstream.storage._casbaseddirectory import CasBasedDirectory -from buildstream.plugintestutils import cli +from buildstream.testing import cli from tests.testutils import create_artifact_share diff --git a/tests/cachekey/cachekey.py b/tests/cachekey/cachekey.py index 3fc5d73ee..671bbb3a1 100644 --- a/tests/cachekey/cachekey.py +++ b/tests/cachekey/cachekey.py @@ -44,7 +44,7 @@ import os import pytest -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import from buildstream.plugin import CoreWarnings from buildstream import _yaml from tests.testutils.site import HAVE_BZR, HAVE_GIT, HAVE_OSTREE, IS_LINUX, MACHINE_ARCH diff --git a/tests/cachekey/update.py b/tests/cachekey/update.py index 3e0924925..68016ed6a 100755 --- a/tests/cachekey/update.py +++ b/tests/cachekey/update.py @@ -13,7 +13,7 @@ # import os import tempfile -from buildstream.plugintestutils.runcli import Cli +from buildstream.testing.runcli import Cli # This weird try / except is needed, because this will be imported differently # when pytest runner imports them vs when you run the updater directly from diff --git a/tests/conftest.py b/tests/conftest.py index fefd2f755..9b8ff2414 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,7 +24,7 @@ import shutil import tempfile import pytest from buildstream._platform.platform import Platform -from buildstream.plugintestutils import register_repo_kind, sourcetests_collection_hook +from buildstream.testing import register_repo_kind, sourcetests_collection_hook from tests.testutils.repo.git import Git from tests.testutils.repo.bzr import Bzr @@ -177,6 +177,6 @@ register_repo_kind('zip', Zip) # This hook enables pytest to collect the templated source tests from -# buildstream.plugintestutils +# buildstream.testing def pytest_sessionstart(session): sourcetests_collection_hook(session) diff --git a/tests/elements/filter.py b/tests/elements/filter.py index 2c5ca88b7..d89c834e0 100644 --- a/tests/elements/filter.py +++ b/tests/elements/filter.py @@ -6,8 +6,8 @@ import shutil import pytest -from buildstream.plugintestutils import create_repo -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import create_repo +from buildstream.testing import cli # pylint: disable=unused-import from buildstream._exceptions import ErrorDomain from buildstream import _yaml diff --git a/tests/examples/autotools.py b/tests/examples/autotools.py index 0c86e3afe..03b98a5f4 100644 --- a/tests/examples/autotools.py +++ b/tests/examples/autotools.py @@ -4,8 +4,8 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli # pylint: disable=unused-import -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli # pylint: disable=unused-import +from buildstream.testing.integration import assert_contains from tests.testutils.site import HAVE_BWRAP, IS_LINUX, MACHINE_ARCH pytestmark = pytest.mark.integration diff --git a/tests/examples/developing.py b/tests/examples/developing.py index 5c8c4a8ea..6e17a7bc1 100644 --- a/tests/examples/developing.py +++ b/tests/examples/developing.py @@ -4,8 +4,8 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli # pylint: disable=unused-import -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli # pylint: disable=unused-import +from buildstream.testing.integration import assert_contains import tests.testutils.patch as patch from tests.testutils.site import HAVE_BWRAP, IS_LINUX, MACHINE_ARCH diff --git a/tests/examples/first-project.py b/tests/examples/first-project.py index 4d1e29d31..d95c8293c 100644 --- a/tests/examples/first-project.py +++ b/tests/examples/first-project.py @@ -4,8 +4,8 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli # pylint: disable=unused-import -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli # pylint: disable=unused-import +from buildstream.testing.integration import assert_contains from tests.testutils.site import IS_LINUX diff --git a/tests/examples/flatpak-autotools.py b/tests/examples/flatpak-autotools.py index 4ee7f37d6..8af564eb1 100644 --- a/tests/examples/flatpak-autotools.py +++ b/tests/examples/flatpak-autotools.py @@ -4,8 +4,8 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli # pylint: disable=unused-import -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli # pylint: disable=unused-import +from buildstream.testing.integration import assert_contains from tests.testutils.site import HAVE_OSTREE, IS_LINUX, MACHINE_ARCH diff --git a/tests/examples/integration-commands.py b/tests/examples/integration-commands.py index 44bbf041a..ad5119600 100644 --- a/tests/examples/integration-commands.py +++ b/tests/examples/integration-commands.py @@ -4,7 +4,7 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli # pylint: disable=unused-import +from buildstream.testing import cli_integration as cli # pylint: disable=unused-import from tests.testutils.site import HAVE_BWRAP, IS_LINUX, MACHINE_ARCH diff --git a/tests/examples/junctions.py b/tests/examples/junctions.py index 26fa8c350..695bfe8c8 100644 --- a/tests/examples/junctions.py +++ b/tests/examples/junctions.py @@ -4,7 +4,7 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli # pylint: disable=unused-import +from buildstream.testing import cli_integration as cli # pylint: disable=unused-import from tests.testutils.site import IS_LINUX, HAVE_BWRAP, MACHINE_ARCH pytestmark = pytest.mark.integration diff --git a/tests/examples/running-commands.py b/tests/examples/running-commands.py index 41920ce73..7089f143e 100644 --- a/tests/examples/running-commands.py +++ b/tests/examples/running-commands.py @@ -4,7 +4,7 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli # pylint: disable=unused-import +from buildstream.testing import cli_integration as cli # pylint: disable=unused-import from tests.testutils.site import IS_LINUX, HAVE_BWRAP, MACHINE_ARCH diff --git a/tests/external_plugins.py b/tests/external_plugins.py index 50b016b74..f6f67707b 100644 --- a/tests/external_plugins.py +++ b/tests/external_plugins.py @@ -78,13 +78,7 @@ if __name__ == "__main__": # directly to it's pytest invocations _, tmpdir, *pytest_args = sys.argv - ALL_EXTERNAL_PLUGINS = [ - ExternalPluginRepo( - name='bst-plugins-template', - url='https://gitlab.com/BuildStream/bst-plugins-template.git', - ref='master' - ), - ] + ALL_EXTERNAL_PLUGINS = [] exit_code = 0 for plugin in ALL_EXTERNAL_PLUGINS: diff --git a/tests/format/assertion.py b/tests/format/assertion.py index cfab105b6..7e87977cb 100644 --- a/tests/format/assertion.py +++ b/tests/format/assertion.py @@ -4,7 +4,7 @@ import os import pytest from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.join( diff --git a/tests/format/dependencies.py b/tests/format/dependencies.py index 780eac73e..15b6e1f4a 100644 --- a/tests/format/dependencies.py +++ b/tests/format/dependencies.py @@ -5,7 +5,7 @@ import os import pytest from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/format/include.py b/tests/format/include.py index 73f8092c5..bfadce7ed 100644 --- a/tests/format/include.py +++ b/tests/format/include.py @@ -6,8 +6,8 @@ import textwrap import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo from tests.testutils import generate_junction diff --git a/tests/format/junctions.py b/tests/format/junctions.py index 94562cb4e..30891433b 100644 --- a/tests/format/junctions.py +++ b/tests/format/junctions.py @@ -8,8 +8,8 @@ import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo from tests.testutils.site import HAVE_GIT diff --git a/tests/format/listdirectiveerrors.py b/tests/format/listdirectiveerrors.py index ec2429dd5..269b521a4 100644 --- a/tests/format/listdirectiveerrors.py +++ b/tests/format/listdirectiveerrors.py @@ -4,7 +4,7 @@ import os import pytest from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/format/optionarch.py b/tests/format/optionarch.py index a7d38c8be..a35ac685d 100644 --- a/tests/format/optionarch.py +++ b/tests/format/optionarch.py @@ -7,7 +7,7 @@ import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import from tests.testutils import override_platform_uname diff --git a/tests/format/optionbool.py b/tests/format/optionbool.py index 40f8dfb33..0d1ee601e 100644 --- a/tests/format/optionbool.py +++ b/tests/format/optionbool.py @@ -5,7 +5,7 @@ import os import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/format/optioneltmask.py b/tests/format/optioneltmask.py index 08f002b58..d33b5771c 100644 --- a/tests/format/optioneltmask.py +++ b/tests/format/optioneltmask.py @@ -5,7 +5,7 @@ import os import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/format/optionenum.py b/tests/format/optionenum.py index d729cbfee..b8a96b0c2 100644 --- a/tests/format/optionenum.py +++ b/tests/format/optionenum.py @@ -5,7 +5,7 @@ import os import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/format/optionexports.py b/tests/format/optionexports.py index 8ad063e74..5df7522b5 100644 --- a/tests/format/optionexports.py +++ b/tests/format/optionexports.py @@ -4,7 +4,7 @@ import os import pytest from buildstream import _yaml -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/format/optionflags.py b/tests/format/optionflags.py index fe61870d7..e28c54236 100644 --- a/tests/format/optionflags.py +++ b/tests/format/optionflags.py @@ -5,7 +5,7 @@ import os import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/format/optionos.py b/tests/format/optionos.py index 6856fd69c..57277b106 100644 --- a/tests/format/optionos.py +++ b/tests/format/optionos.py @@ -7,7 +7,7 @@ import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import from tests.testutils import override_platform_uname diff --git a/tests/format/optionoverrides.py b/tests/format/optionoverrides.py index 2e4d26a16..60d02b3a3 100644 --- a/tests/format/optionoverrides.py +++ b/tests/format/optionoverrides.py @@ -4,7 +4,7 @@ import os import pytest from buildstream import _yaml -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/format/options.py b/tests/format/options.py index c9f29d64d..3a8210dc3 100644 --- a/tests/format/options.py +++ b/tests/format/options.py @@ -5,7 +5,7 @@ import os import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.join( diff --git a/tests/format/project.py b/tests/format/project.py index 5813bb389..fbb742d47 100644 --- a/tests/format/project.py +++ b/tests/format/project.py @@ -5,7 +5,7 @@ import os import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils import filetypegenerator diff --git a/tests/format/projectoverrides.py b/tests/format/projectoverrides.py index 60851b9d5..4b0c3f4d0 100644 --- a/tests/format/projectoverrides.py +++ b/tests/format/projectoverrides.py @@ -4,7 +4,7 @@ import os import pytest from buildstream import _yaml -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.join( diff --git a/tests/format/variables.py b/tests/format/variables.py index b62de7460..87e3b2903 100644 --- a/tests/format/variables.py +++ b/tests/format/variables.py @@ -8,7 +8,7 @@ import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory diff --git a/tests/frontend/artifact.py b/tests/frontend/artifact.py index 887649f96..10cb4f513 100644 --- a/tests/frontend/artifact.py +++ b/tests/frontend/artifact.py @@ -24,7 +24,7 @@ import os import pytest -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils import create_artifact_share diff --git a/tests/frontend/buildcheckout.py b/tests/frontend/buildcheckout.py index 5145b1222..b181e29bb 100644 --- a/tests/frontend/buildcheckout.py +++ b/tests/frontend/buildcheckout.py @@ -11,7 +11,7 @@ import pytest from tests.testutils.site import IS_WINDOWS from tests.testutils import generate_junction, yaml_file_get_provenance -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason diff --git a/tests/frontend/buildtrack.py b/tests/frontend/buildtrack.py index d4dc93aac..404882beb 100644 --- a/tests/frontend/buildtrack.py +++ b/tests/frontend/buildtrack.py @@ -9,8 +9,8 @@ import itertools import pytest from buildstream import _yaml -from buildstream.plugintestutils import create_repo -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import create_repo +from buildstream.testing import cli # pylint: disable=unused-import from buildstream._exceptions import ErrorDomain from . import configure_project diff --git a/tests/frontend/completions.py b/tests/frontend/completions.py index 3876dcf15..773eec040 100644 --- a/tests/frontend/completions.py +++ b/tests/frontend/completions.py @@ -3,7 +3,7 @@ import os import pytest -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.join( diff --git a/tests/frontend/compose_splits.py b/tests/frontend/compose_splits.py index adce813bd..f1f9b73be 100644 --- a/tests/frontend/compose_splits.py +++ b/tests/frontend/compose_splits.py @@ -3,7 +3,7 @@ import os import pytest -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.join( diff --git a/tests/frontend/configurable_warnings.py b/tests/frontend/configurable_warnings.py index fe522f33a..c63a0b673 100644 --- a/tests/frontend/configurable_warnings.py +++ b/tests/frontend/configurable_warnings.py @@ -8,7 +8,7 @@ import pytest from buildstream.plugin import CoreWarnings from buildstream._exceptions import ErrorDomain from buildstream import _yaml -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import TOP_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), diff --git a/tests/frontend/cross_junction_workspace.py b/tests/frontend/cross_junction_workspace.py index a93d5c132..81fd43487 100644 --- a/tests/frontend/cross_junction_workspace.py +++ b/tests/frontend/cross_junction_workspace.py @@ -2,8 +2,8 @@ # pylint: disable=redefined-outer-name import os -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo from buildstream import _yaml diff --git a/tests/frontend/fetch.py b/tests/frontend/fetch.py index cea7ff129..e330cd407 100644 --- a/tests/frontend/fetch.py +++ b/tests/frontend/fetch.py @@ -5,8 +5,8 @@ import os import pytest from tests.testutils import generate_junction, yaml_file_get_provenance -from buildstream.plugintestutils import create_repo -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import create_repo +from buildstream.testing import cli # pylint: disable=unused-import from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason diff --git a/tests/frontend/help.py b/tests/frontend/help.py index b52ace302..3bc18499b 100644 --- a/tests/frontend/help.py +++ b/tests/frontend/help.py @@ -2,7 +2,7 @@ # pylint: disable=redefined-outer-name import pytest -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import def assert_help(cli_output): diff --git a/tests/frontend/init.py b/tests/frontend/init.py index 06babce43..fa4623c63 100644 --- a/tests/frontend/init.py +++ b/tests/frontend/init.py @@ -3,7 +3,7 @@ import os import pytest -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from buildstream import _yaml from buildstream._frontend.app import App diff --git a/tests/frontend/logging.py b/tests/frontend/logging.py index 49e3a70c6..e7279ac4c 100644 --- a/tests/frontend/logging.py +++ b/tests/frontend/logging.py @@ -6,11 +6,11 @@ import re import pytest -from buildstream.plugintestutils import create_repo +from buildstream.testing import create_repo from buildstream import _yaml from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.join( diff --git a/tests/frontend/mirror.py b/tests/frontend/mirror.py index 47f94289f..f7427cf88 100644 --- a/tests/frontend/mirror.py +++ b/tests/frontend/mirror.py @@ -5,8 +5,8 @@ import os import pytest from buildstream import _yaml -from buildstream.plugintestutils import create_repo -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import create_repo +from buildstream.testing import cli # pylint: disable=unused-import # Project directory diff --git a/tests/frontend/order.py b/tests/frontend/order.py index 5eb5b299d..7f354c88e 100644 --- a/tests/frontend/order.py +++ b/tests/frontend/order.py @@ -4,8 +4,8 @@ import os import pytest -from buildstream.plugintestutils import create_repo -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import create_repo +from buildstream.testing import cli # pylint: disable=unused-import from buildstream import _yaml # Project directory diff --git a/tests/frontend/overlaps.py b/tests/frontend/overlaps.py index 57a8ce630..6f6ab26a3 100644 --- a/tests/frontend/overlaps.py +++ b/tests/frontend/overlaps.py @@ -3,7 +3,7 @@ import os import pytest -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import from buildstream._exceptions import ErrorDomain from buildstream import _yaml from buildstream.plugin import CoreWarnings diff --git a/tests/frontend/pull.py b/tests/frontend/pull.py index 2a1006ea2..cc62afe92 100644 --- a/tests/frontend/pull.py +++ b/tests/frontend/pull.py @@ -6,7 +6,7 @@ import shutil import stat import pytest from buildstream import utils -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils import create_artifact_share, generate_junction diff --git a/tests/frontend/push.py b/tests/frontend/push.py index b2991ada7..67c53f2bb 100644 --- a/tests/frontend/push.py +++ b/tests/frontend/push.py @@ -27,7 +27,7 @@ import os import pytest from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils import create_artifact_share, create_element_size, generate_junction, wait_for_cache_granularity diff --git a/tests/frontend/rebuild.py b/tests/frontend/rebuild.py index cf517f3c1..1cdb45d11 100644 --- a/tests/frontend/rebuild.py +++ b/tests/frontend/rebuild.py @@ -3,7 +3,7 @@ import os import pytest -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import # Project directory DATA_DIR = os.path.join( diff --git a/tests/frontend/remote-caches.py b/tests/frontend/remote-caches.py index 3e3e226fc..e3f10e6f7 100644 --- a/tests/frontend/remote-caches.py +++ b/tests/frontend/remote-caches.py @@ -23,7 +23,7 @@ import os import shutil import pytest -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from buildstream import _yaml from tests.testutils import create_artifact_share, create_element_size diff --git a/tests/frontend/show.py b/tests/frontend/show.py index 7e5ebfb77..5acb99392 100644 --- a/tests/frontend/show.py +++ b/tests/frontend/show.py @@ -7,7 +7,7 @@ import shutil import itertools import pytest from tests.testutils import generate_junction, yaml_file_get_provenance -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason diff --git a/tests/frontend/source_checkout.py b/tests/frontend/source_checkout.py index c43bea6bd..0bedfe1e2 100644 --- a/tests/frontend/source_checkout.py +++ b/tests/frontend/source_checkout.py @@ -6,7 +6,7 @@ import tarfile import pytest -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from buildstream import utils, _yaml from buildstream._exceptions import ErrorDomain diff --git a/tests/frontend/track.py b/tests/frontend/track.py index 17d0cd827..808bf0593 100644 --- a/tests/frontend/track.py +++ b/tests/frontend/track.py @@ -5,8 +5,8 @@ import stat import os import pytest -from buildstream.plugintestutils import create_repo -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import create_repo +from buildstream.testing import cli # pylint: disable=unused-import from buildstream._exceptions import ErrorDomain, LoadErrorReason from buildstream import _yaml from tests.testutils import generate_junction, yaml_file_get_provenance diff --git a/tests/frontend/version.py b/tests/frontend/version.py index f9228c0bc..e7db19915 100644 --- a/tests/frontend/version.py +++ b/tests/frontend/version.py @@ -1,7 +1,7 @@ # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import # For utils.get_bst_version() diff --git a/tests/frontend/workspace.py b/tests/frontend/workspace.py index 522bbffab..1db23ccc4 100644 --- a/tests/frontend/workspace.py +++ b/tests/frontend/workspace.py @@ -36,8 +36,8 @@ import pytest from tests.testutils import create_artifact_share, create_element_size, wait_for_cache_granularity -from buildstream.plugintestutils import create_repo, ALL_REPO_KINDS -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import create_repo, ALL_REPO_KINDS +from buildstream.testing import cli # pylint: disable=unused-import from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason from buildstream._workspaces import BST_WORKSPACE_FORMAT_VERSION diff --git a/tests/integration/artifact.py b/tests/integration/artifact.py index 0718de893..a5e1f4d77 100644 --- a/tests/integration/artifact.py +++ b/tests/integration/artifact.py @@ -25,7 +25,7 @@ import shutil import tempfile from buildstream import utils -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils import create_artifact_share from tests.testutils.site import HAVE_SANDBOX from buildstream._cas import CASCache diff --git a/tests/integration/autotools.py b/tests/integration/autotools.py index d9dcc10ed..80365b8ee 100644 --- a/tests/integration/autotools.py +++ b/tests/integration/autotools.py @@ -1,8 +1,8 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli +from buildstream.testing.integration import assert_contains from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/build-uid.py b/tests/integration/build-uid.py index bb2e013c2..f2e50a3c7 100644 --- a/tests/integration/build-uid.py +++ b/tests/integration/build-uid.py @@ -1,7 +1,7 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_BWRAP, IS_LINUX, HAVE_SANDBOX diff --git a/tests/integration/cachedfail.py b/tests/integration/cachedfail.py index 34849f8bc..28174353d 100644 --- a/tests/integration/cachedfail.py +++ b/tests/integration/cachedfail.py @@ -3,7 +3,7 @@ import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.conftest import clean_platform_cache from tests.testutils import create_artifact_share diff --git a/tests/integration/cmake.py b/tests/integration/cmake.py index ddd8ce95d..a69b9cd80 100644 --- a/tests/integration/cmake.py +++ b/tests/integration/cmake.py @@ -1,8 +1,8 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli +from buildstream.testing.integration import assert_contains from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/compose-symlinks.py b/tests/integration/compose-symlinks.py index 6f9e0ea7d..e510d9573 100644 --- a/tests/integration/compose-symlinks.py +++ b/tests/integration/compose-symlinks.py @@ -1,7 +1,7 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli pytestmark = pytest.mark.integration diff --git a/tests/integration/compose.py b/tests/integration/compose.py index e0de38655..dab05fd9f 100644 --- a/tests/integration/compose.py +++ b/tests/integration/compose.py @@ -3,8 +3,8 @@ import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli -from buildstream.plugintestutils.integration import walk_dir +from buildstream.testing import cli_integration as cli +from buildstream.testing.integration import walk_dir from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/import.py b/tests/integration/import.py index 501f8aa9e..26c8cf6a0 100644 --- a/tests/integration/import.py +++ b/tests/integration/import.py @@ -3,8 +3,8 @@ import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli -from buildstream.plugintestutils.integration import walk_dir +from buildstream.testing import cli_integration as cli +from buildstream.testing.integration import walk_dir pytestmark = pytest.mark.integration diff --git a/tests/integration/make.py b/tests/integration/make.py index 581d919e7..f59c2b0d6 100644 --- a/tests/integration/make.py +++ b/tests/integration/make.py @@ -1,8 +1,8 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli +from buildstream.testing.integration import assert_contains from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/manual.py b/tests/integration/manual.py index 76ea1dc66..0ba650bde 100644 --- a/tests/integration/manual.py +++ b/tests/integration/manual.py @@ -3,7 +3,7 @@ import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/messages.py b/tests/integration/messages.py index 3697d0bf7..2211ea468 100644 --- a/tests/integration/messages.py +++ b/tests/integration/messages.py @@ -22,7 +22,7 @@ import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/pip_element.py b/tests/integration/pip_element.py index a3f363bc8..9ef163125 100644 --- a/tests/integration/pip_element.py +++ b/tests/integration/pip_element.py @@ -4,8 +4,8 @@ import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli +from buildstream.testing.integration import assert_contains from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/pip_source.py b/tests/integration/pip_source.py index 6ee76005c..645dad444 100644 --- a/tests/integration/pip_source.py +++ b/tests/integration/pip_source.py @@ -3,8 +3,8 @@ import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_integration as cli +from buildstream.testing.integration import assert_contains from tests.testutils.python_repo import setup_pypi_repo from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/pullbuildtrees.py b/tests/integration/pullbuildtrees.py index bdbcad911..91acff4a3 100644 --- a/tests/integration/pullbuildtrees.py +++ b/tests/integration/pullbuildtrees.py @@ -8,7 +8,7 @@ from tests.testutils import create_artifact_share from tests.testutils.site import HAVE_SANDBOX from buildstream import utils -from buildstream.plugintestutils import cli, cli_integration as cli2 +from buildstream.testing import cli, cli_integration as cli2 from buildstream._cas import CASCache from buildstream._exceptions import ErrorDomain, LoadErrorReason diff --git a/tests/integration/sandbox-bwrap.py b/tests/integration/sandbox-bwrap.py index 29227501c..549de4ed0 100644 --- a/tests/integration/sandbox-bwrap.py +++ b/tests/integration/sandbox-bwrap.py @@ -3,7 +3,7 @@ import pytest from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_BWRAP, HAVE_BWRAP_JSON_STATUS diff --git a/tests/integration/script.py b/tests/integration/script.py index 1500af724..acc752f48 100644 --- a/tests/integration/script.py +++ b/tests/integration/script.py @@ -2,7 +2,7 @@ import os import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/shell.py b/tests/integration/shell.py index fb01f45ea..a9fb4855a 100644 --- a/tests/integration/shell.py +++ b/tests/integration/shell.py @@ -2,7 +2,7 @@ import os import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/shellbuildtrees.py b/tests/integration/shellbuildtrees.py index c12a0d404..3d59c78b9 100644 --- a/tests/integration/shellbuildtrees.py +++ b/tests/integration/shellbuildtrees.py @@ -4,7 +4,7 @@ import shutil from tests.testutils import create_artifact_share from tests.testutils.site import HAVE_SANDBOX -from buildstream.plugintestutils import cli, cli_integration +from buildstream.testing import cli, cli_integration from buildstream._exceptions import ErrorDomain diff --git a/tests/integration/sockets.py b/tests/integration/sockets.py index 17c3becea..af09c391b 100644 --- a/tests/integration/sockets.py +++ b/tests/integration/sockets.py @@ -1,7 +1,7 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/source-determinism.py b/tests/integration/source-determinism.py index 2f63a4af9..5bb0941d1 100644 --- a/tests/integration/source-determinism.py +++ b/tests/integration/source-determinism.py @@ -2,8 +2,8 @@ import os import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli_integration as cli +from buildstream.testing import create_repo from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/stack.py b/tests/integration/stack.py index e75c6a450..9cc917e0e 100644 --- a/tests/integration/stack.py +++ b/tests/integration/stack.py @@ -1,7 +1,7 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/symlinks.py b/tests/integration/symlinks.py index 5752281de..02238aebc 100644 --- a/tests/integration/symlinks.py +++ b/tests/integration/symlinks.py @@ -1,7 +1,7 @@ import os import pytest -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/integration/workspace.py b/tests/integration/workspace.py index 8ec6db3c8..4ee0050d7 100644 --- a/tests/integration/workspace.py +++ b/tests/integration/workspace.py @@ -2,7 +2,7 @@ import os import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli_integration as cli +from buildstream.testing import cli_integration as cli from tests.testutils.site import HAVE_SANDBOX diff --git a/tests/plugins/deprecationwarnings/deprecationwarnings.py b/tests/plugins/deprecationwarnings/deprecationwarnings.py index 0f61ce271..75652bf06 100644 --- a/tests/plugins/deprecationwarnings/deprecationwarnings.py +++ b/tests/plugins/deprecationwarnings/deprecationwarnings.py @@ -1,6 +1,6 @@ import pytest import os -from buildstream.plugintestutils import cli +from buildstream.testing import cli DATA_DIR = os.path.join( diff --git a/tests/remoteexecution/partial.py b/tests/remoteexecution/partial.py index 907fc507d..ee0fc25b4 100644 --- a/tests/remoteexecution/partial.py +++ b/tests/remoteexecution/partial.py @@ -2,8 +2,8 @@ import os import pytest from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli_remote_execution as cli -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_remote_execution as cli +from buildstream.testing.integration import assert_contains pytestmark = pytest.mark.remoteexecution diff --git a/tests/remoteexecution/simple.py b/tests/remoteexecution/simple.py index 152a40e31..53f4c65a4 100644 --- a/tests/remoteexecution/simple.py +++ b/tests/remoteexecution/simple.py @@ -1,8 +1,8 @@ import os import pytest -from buildstream.plugintestutils import cli_remote_execution as cli -from buildstream.plugintestutils.integration import assert_contains +from buildstream.testing import cli_remote_execution as cli +from buildstream.testing.integration import assert_contains pytestmark = pytest.mark.remoteexecution diff --git a/tests/sandboxes/missing-command.py b/tests/sandboxes/missing-command.py index ee6d11280..8156eac9b 100644 --- a/tests/sandboxes/missing-command.py +++ b/tests/sandboxes/missing-command.py @@ -3,7 +3,7 @@ import pytest from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli +from buildstream.testing import cli DATA_DIR = os.path.join( diff --git a/tests/sandboxes/missing_dependencies.py b/tests/sandboxes/missing_dependencies.py index 850fddaa3..22fecaa6e 100644 --- a/tests/sandboxes/missing_dependencies.py +++ b/tests/sandboxes/missing_dependencies.py @@ -1,6 +1,6 @@ import os import pytest -from buildstream.plugintestutils import cli +from buildstream.testing import cli from tests.testutils.site import IS_LINUX from buildstream import _yaml diff --git a/tests/sandboxes/remote-exec-config.py b/tests/sandboxes/remote-exec-config.py index 31ba11678..82943e4b1 100644 --- a/tests/sandboxes/remote-exec-config.py +++ b/tests/sandboxes/remote-exec-config.py @@ -5,7 +5,7 @@ import os from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli +from buildstream.testing.runcli import cli DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), diff --git a/tests/sourcecache/config.py b/tests/sourcecache/config.py index 9f166d054..7c33adbe0 100644 --- a/tests/sourcecache/config.py +++ b/tests/sourcecache/config.py @@ -27,7 +27,7 @@ import pytest from buildstream import _yaml from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import DATA_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/sourcecache/fetch.py b/tests/sourcecache/fetch.py index 7f82388ab..bc025cbd5 100644 --- a/tests/sourcecache/fetch.py +++ b/tests/sourcecache/fetch.py @@ -27,8 +27,8 @@ from buildstream._exceptions import ErrorDomain from buildstream._context import Context from buildstream._project import Project from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo from tests.testutils import create_artifact_share DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project") diff --git a/tests/sourcecache/push.py b/tests/sourcecache/push.py index 825d2614e..abab1616c 100644 --- a/tests/sourcecache/push.py +++ b/tests/sourcecache/push.py @@ -27,8 +27,8 @@ from buildstream._context import Context from buildstream._exceptions import ErrorDomain from buildstream._project import Project from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo from tests.testutils import create_artifact_share diff --git a/tests/sourcecache/source-checkout.py b/tests/sourcecache/source-checkout.py index 517a28108..15c80fde9 100644 --- a/tests/sourcecache/source-checkout.py +++ b/tests/sourcecache/source-checkout.py @@ -27,7 +27,7 @@ import shutil import pytest from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import from tests.testutils.element_generators import create_element_size diff --git a/tests/sourcecache/staging.py b/tests/sourcecache/staging.py index 5c1c4f358..318285292 100644 --- a/tests/sourcecache/staging.py +++ b/tests/sourcecache/staging.py @@ -28,7 +28,7 @@ import pytest from buildstream._context import Context from buildstream._project import Project -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import from tests.testutils.element_generators import create_element_size diff --git a/tests/sourcecache/workspace.py b/tests/sourcecache/workspace.py index 8bde39ee6..22316c4fe 100644 --- a/tests/sourcecache/workspace.py +++ b/tests/sourcecache/workspace.py @@ -26,7 +26,7 @@ import shutil import pytest -from buildstream.plugintestutils.runcli import cli # pylint: disable=unused-import +from buildstream.testing.runcli import cli # pylint: disable=unused-import from tests.testutils.artifactshare import create_artifact_share from tests.testutils.element_generators import create_element_size diff --git a/tests/sources/bzr.py b/tests/sources/bzr.py index bf022236b..a11932a49 100644 --- a/tests/sources/bzr.py +++ b/tests/sources/bzr.py @@ -6,8 +6,8 @@ import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo from tests.testutils.site import HAVE_BZR DATA_DIR = os.path.join( diff --git a/tests/sources/deb.py b/tests/sources/deb.py index 2538efe3b..69b7c78df 100644 --- a/tests/sources/deb.py +++ b/tests/sources/deb.py @@ -8,7 +8,7 @@ import pytest from buildstream._exceptions import ErrorDomain from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils.site import HAVE_ARPY from . import list_dir_contents diff --git a/tests/sources/git.py b/tests/sources/git.py index 0f97b77a3..b7b175ee6 100644 --- a/tests/sources/git.py +++ b/tests/sources/git.py @@ -32,8 +32,8 @@ import pytest from buildstream._exceptions import ErrorDomain from buildstream import _yaml from buildstream.plugin import CoreWarnings -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo from tests.testutils.site import HAVE_GIT, HAVE_OLD_GIT diff --git a/tests/sources/keytest.py b/tests/sources/keytest.py index 989f4f1b0..dbfe97ab5 100644 --- a/tests/sources/keytest.py +++ b/tests/sources/keytest.py @@ -25,7 +25,7 @@ import os import pytest from buildstream._exceptions import ErrorDomain -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project_key_test") diff --git a/tests/sources/local.py b/tests/sources/local.py index e5f6d4cdf..94d45b3e9 100644 --- a/tests/sources/local.py +++ b/tests/sources/local.py @@ -5,7 +5,7 @@ import os import pytest from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils import filetypegenerator DATA_DIR = os.path.join( diff --git a/tests/sources/no_fetch_cached.py b/tests/sources/no_fetch_cached.py index 4a3f71932..69d28615a 100644 --- a/tests/sources/no_fetch_cached.py +++ b/tests/sources/no_fetch_cached.py @@ -6,8 +6,8 @@ import pytest from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo from tests.testutils.site import HAVE_GIT DATA_DIR = os.path.join( diff --git a/tests/sources/ostree.py b/tests/sources/ostree.py index e75a64663..aefad68fa 100644 --- a/tests/sources/ostree.py +++ b/tests/sources/ostree.py @@ -25,8 +25,8 @@ import pytest from buildstream._exceptions import ErrorDomain from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import -from buildstream.plugintestutils import create_repo +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing import create_repo DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), diff --git a/tests/sources/patch.py b/tests/sources/patch.py index 49c6594dc..4f9db815b 100644 --- a/tests/sources/patch.py +++ b/tests/sources/patch.py @@ -5,7 +5,7 @@ import os import pytest from buildstream._exceptions import ErrorDomain, LoadErrorReason -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils import filetypegenerator DATA_DIR = os.path.join( diff --git a/tests/sources/pip.py b/tests/sources/pip.py index 0a0a846ef..cf6ea5498 100644 --- a/tests/sources/pip.py +++ b/tests/sources/pip.py @@ -7,7 +7,7 @@ import pytest from buildstream._exceptions import ErrorDomain from buildstream import _yaml from buildstream.plugins.sources.pip import _match_package_name -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), diff --git a/tests/sources/previous_source_access.py b/tests/sources/previous_source_access.py index 29d1f3023..a0661ca98 100644 --- a/tests/sources/previous_source_access.py +++ b/tests/sources/previous_source_access.py @@ -4,7 +4,7 @@ import os import pytest -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), diff --git a/tests/sources/remote.py b/tests/sources/remote.py index 2a23895ac..8b57151b2 100644 --- a/tests/sources/remote.py +++ b/tests/sources/remote.py @@ -7,7 +7,7 @@ import pytest from buildstream._exceptions import ErrorDomain from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils.file_server import create_file_server DATA_DIR = os.path.join( diff --git a/tests/sources/tar.py b/tests/sources/tar.py index 7a516459d..8446f485f 100644 --- a/tests/sources/tar.py +++ b/tests/sources/tar.py @@ -12,7 +12,7 @@ import pytest from buildstream._exceptions import ErrorDomain from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils.file_server import create_file_server from tests.testutils.site import HAVE_LZIP from . import list_dir_contents diff --git a/tests/sources/zip.py b/tests/sources/zip.py index 1c86e56d7..cb7f440f1 100644 --- a/tests/sources/zip.py +++ b/tests/sources/zip.py @@ -8,7 +8,7 @@ import pytest from buildstream._exceptions import ErrorDomain from buildstream import _yaml -from buildstream.plugintestutils import cli # pylint: disable=unused-import +from buildstream.testing import cli # pylint: disable=unused-import from tests.testutils.file_server import create_file_server from . import list_dir_contents diff --git a/tests/testutils/element_generators.py b/tests/testutils/element_generators.py index ec1b6bd85..38bafc6b6 100644 --- a/tests/testutils/element_generators.py +++ b/tests/testutils/element_generators.py @@ -2,7 +2,7 @@ import os from buildstream import _yaml from buildstream import utils -from buildstream.plugintestutils import create_repo +from buildstream.testing import create_repo # create_element_size() diff --git a/tests/testutils/junction.py b/tests/testutils/junction.py index 49bc692ed..8132e291c 100644 --- a/tests/testutils/junction.py +++ b/tests/testutils/junction.py @@ -1,5 +1,5 @@ from buildstream import _yaml -from buildstream.plugintestutils import create_repo +from buildstream.testing import create_repo # generate_junction() diff --git a/tests/testutils/repo/bzr.py b/tests/testutils/repo/bzr.py index c006c3fce..53bacad17 100644 --- a/tests/testutils/repo/bzr.py +++ b/tests/testutils/repo/bzr.py @@ -2,7 +2,7 @@ import os import subprocess import pytest -from buildstream.plugintestutils import Repo +from buildstream.testing import Repo from .. import site diff --git a/tests/testutils/repo/git.py b/tests/testutils/repo/git.py index 5c27be3ee..8ffd09c2f 100644 --- a/tests/testutils/repo/git.py +++ b/tests/testutils/repo/git.py @@ -4,7 +4,7 @@ import subprocess import pytest -from buildstream.plugintestutils import Repo +from buildstream.testing import Repo from .. import site diff --git a/tests/testutils/repo/ostree.py b/tests/testutils/repo/ostree.py index d0cef8b88..c43f39ef2 100644 --- a/tests/testutils/repo/ostree.py +++ b/tests/testutils/repo/ostree.py @@ -2,7 +2,7 @@ import subprocess import pytest -from buildstream.plugintestutils import Repo +from buildstream.testing import Repo from .. import site diff --git a/tests/testutils/repo/tar.py b/tests/testutils/repo/tar.py index 6fdf58ae0..63231fa4b 100644 --- a/tests/testutils/repo/tar.py +++ b/tests/testutils/repo/tar.py @@ -3,7 +3,7 @@ import tarfile from buildstream.utils import sha256sum -from buildstream.plugintestutils import Repo +from buildstream.testing import Repo class Tar(Repo): diff --git a/tests/testutils/repo/zip.py b/tests/testutils/repo/zip.py index c57e09e7d..df3f834a9 100644 --- a/tests/testutils/repo/zip.py +++ b/tests/testutils/repo/zip.py @@ -3,7 +3,7 @@ import zipfile from buildstream.utils import sha256sum -from buildstream.plugintestutils import Repo +from buildstream.testing import Repo class Zip(Repo): diff --git a/tox.ini b/tox.ini index c083ef58e..12547f262 100644 --- a/tox.ini +++ b/tox.ini @@ -136,11 +136,11 @@ whitelist_externals = * # # You should pass this the part of a test node's id after "::". For # example, to run the test -# buildstream/plugintestutils/_sourcetests/fetch.py::test_fetch_cross_junction[git-inline] +# buildstream/testing/_sourcetests/fetch.py::test_fetch_cross_junction[git-inline] # you would do tox -e sourcetests -- test_fetch_cross_junction[git-inline] # # This does rely on the fact that none of the tests in -# buildstream.plugintestutils have the same name. +# buildstream.testing have the same name. # [testenv:sourcetests] commands = pytest --basetemp {envtmpdir} --ignore tests -k "{posargs}" -- cgit v1.2.1