| #!/usr/bin/env python3 |
| # |
| # Copyright (C) 2018 Codethink Limited |
| # Copyright (C) 2019 Bloomberg Finance LLP |
| # |
| # This program is free software; you can redistribute it and/or |
| # modify it under the terms of the GNU Lesser General Public |
| # License as published by the Free Software Foundation; either |
| # version 2 of the License, or (at your option) any later version. |
| # |
| # This library is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| # Lesser General Public License for more details. |
| # |
| # You should have received a copy of the GNU Lesser General Public |
| # License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| # |
| # Authors: |
| # Tristan Maat <tristan.maat@codethink.co.uk> |
| # |
| import os |
| import multiprocessing |
| import pytest |
| |
| from buildstream.testing import register_repo_kind, sourcetests_collection_hook |
| from buildstream.testing._fixtures import reset_global_node_state, thread_check # pylint: disable=unused-import |
| from buildstream.testing._forked import forked_run_report |
| from buildstream.testing.integration import integration_cache # pylint: disable=unused-import |
| |
| |
| from tests.testutils.repo.git import Git |
| from tests.testutils.repo.bzr import Bzr |
| from tests.testutils.repo.tar import Tar |
| from tests.testutils.repo.zip import Zip |
| |
| |
| # |
| # This file is loaded by pytest, we use it to add a custom |
| # `--integration` option to our test suite, and to install |
| # a session scope fixture. |
| # |
| |
| |
| ################################################# |
| # Implement pytest option # |
| ################################################# |
| def pytest_addoption(parser): |
| parser.addoption('--integration', action='store_true', default=False, |
| help='Run integration tests') |
| |
| parser.addoption('--remote-execution', action='store_true', default=False, |
| help='Run remote-execution tests only') |
| |
| |
| def pytest_runtest_setup(item): |
| # Without --integration: skip tests not marked with 'integration' |
| if not item.config.getvalue('integration'): |
| if item.get_closest_marker('integration'): |
| pytest.skip('skipping integration test') |
| |
| # With --remote-execution: only run tests marked with 'remoteexecution' |
| if item.config.getvalue('remote_execution'): |
| if not item.get_closest_marker('remoteexecution'): |
| pytest.skip('skipping non remote-execution test') |
| |
| # Without --remote-execution: skip tests marked with 'remoteexecution' |
| else: |
| if item.get_closest_marker('remoteexecution'): |
| pytest.skip('skipping remote-execution test') |
| |
| |
| ################################################# |
| # in_subprocess mark # |
| ################################################# |
| # |
| # Various issues can occur when forking the Python process and using gRPC, |
| # due to its multithreading. As BuildStream forks for parallelisation, gRPC |
| # features are restricted to child processes, so tests using them must also |
| # run as child processes. The in_subprocess mark handles this. |
| # See <https://github.com/grpc/grpc/blob/master/doc/fork_support.md>. |
| # |
| @pytest.mark.tryfirst |
| def pytest_runtest_protocol(item): |
| if item.get_closest_marker('in_subprocess') is not None: |
| reports = forked_run_report(item) |
| for rep in reports: |
| item.ihook.pytest_runtest_logreport(report=rep) |
| return True |
| else: |
| return None |
| |
| |
| ################################################# |
| # remote_services fixture # |
| ################################################# |
| # |
| # This is returned by the `remote_services` fixture |
| # |
| class RemoteServices(): |
| |
| def __init__(self, **kwargs): |
| self.action_service = kwargs.get('action_service') |
| self.artifact_service = kwargs.get('artifact_service') |
| self.exec_service = kwargs.get('exec_service') |
| self.source_service = kwargs.get('source_service') |
| self.storage_service = kwargs.get('storage_service') |
| |
| |
| @pytest.fixture(scope='session') |
| def remote_services(request): |
| kwargs = {} |
| # Look for remote services configuration in environment. |
| if 'ARTIFACT_CACHE_SERVICE' in os.environ: |
| kwargs['artifact_service'] = os.environ.get('ARTIFACT_CACHE_SERVICE') |
| |
| if 'REMOTE_EXECUTION_SERVICE' in os.environ: |
| kwargs['action_service'] = os.environ.get('REMOTE_EXECUTION_SERVICE') |
| kwargs['exec_service'] = os.environ.get('REMOTE_EXECUTION_SERVICE') |
| kwargs['storage_service'] = os.environ.get('REMOTE_EXECUTION_SERVICE') |
| |
| if 'SOURCE_CACHE_SERVICE' in os.environ: |
| kwargs['source_service'] = os.environ.get('SOURCE_CACHE_SERVICE') |
| |
| return RemoteServices(**kwargs) |
| |
| |
| ################################################# |
| # Setup for templated source tests # |
| ################################################# |
| register_repo_kind('git', Git, None) |
| register_repo_kind('bzr', Bzr, None) |
| register_repo_kind('tar', Tar, None) |
| register_repo_kind('zip', Zip, None) |
| |
| |
| # This hook enables pytest to collect the templated source tests from |
| # buildstream.testing |
| def pytest_sessionstart(session): |
| sourcetests_collection_hook(session) |
| |
| |
| ################################################# |
| # Isolated environment # |
| ################################################# |
| @pytest.fixture(scope="session", autouse=True) |
| def set_xdg_paths(pytestconfig): |
| for env_var, default in [ |
| ("HOME", "tmp"), |
| ("XDG_CACHE_HOME", "tmp/cache"), |
| ("XDG_CONFIG_HOME", "tmp/config"), |
| ("XDG_DATA_HOME", "tmp/share"), |
| ]: |
| value = os.environ.get("BST_TEST_{}".format(env_var)) |
| if value is None: |
| value = os.path.join(pytestconfig.getoption("basetemp"), default) |
| |
| os.environ[env_var] = value |
| |
| |
| def pytest_configure(config): |
| # TODO: document |
| print( |
| "Multiprocessing method:", |
| multiprocessing.get_start_method(allow_none=True), |
| ) |
| if 'BST_FORCE_START_METHOD' in os.environ: |
| start_method = os.environ['BST_FORCE_START_METHOD'] |
| multiprocessing.set_start_method(start_method) |