diff options
author | bst-marge-bot <marge-bot@buildstream.build> | 2019-07-25 14:22:25 +0000 |
---|---|---|
committer | bst-marge-bot <marge-bot@buildstream.build> | 2019-07-25 14:22:25 +0000 |
commit | 41b4eb57df05f52140c68c27b5e6dd3d1b461d32 (patch) | |
tree | 7622421b4722fe37ba01f102409d7a69bb7a6be7 | |
parent | 64c1ceed084b71304180738f9104d637410433ed (diff) | |
parent | b2328f913cb571692e342a9cd7f98b57fa882c5f (diff) | |
download | buildstream-41b4eb57df05f52140c68c27b5e6dd3d1b461d32.tar.gz |
Merge branch 'buildbox' into 'master'
Add BuildBox backend for sandboxing
See merge request BuildStream/buildstream!951
45 files changed, 842 insertions, 34 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bcf7f37ce..009999065 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -100,6 +100,38 @@ tests-unix: # Since the unix platform is required to run as root, no user change required - ${TEST_COMMAND} +tests-buildbox: + image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-fedora:29-master-47052095 + <<: *tests + variables: + BST_FORCE_SANDBOX: "buildbox" + + script: + + # We remove the Bubblewrap package here so that we catch any + # codepaths that try to use them. + - dnf install -y fuse3 + - dnf erase -y bubblewrap + + # Before buildbox is a first class citizen we need a good install story for users and this test + # should mirror that story, for now we build in the test as it is quick and easy. + + # Build and install buildbox + - dnf install -y fuse3-devel glibc-static grpc-plugins grpc-devel protobuf-devel cmake gcc gcc-c++ libuuid-devel + - git clone https://gitlab.com/BuildGrid/buildbox/buildbox-fuse.git + - cd buildbox-fuse + # Pin a specific commit so that any changes to buildbox do not result in unexpected/unannounced buildstream failures + - git checkout cdd2b00842c39a8f7162c2ae55bf2cefb925e339 + - cmake -B build + - cmake --build build + - cmake --build build --target install + - cd .. + + - useradd -Um buildstream + - chown -R buildstream:buildstream . + + - su buildstream -c "${TEST_COMMAND}" + tests-fedora-missing-deps: # Ensure that tests behave nicely while missing bwrap and ostree image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-fedora:29-master-47052095 @@ -2,6 +2,10 @@ buildstream 1.3.1 ================= + o Added Basic support for the BuildBox sandbox. The sand box will only be used if the + environment variable BST_FORCE_SANDBOX is set to `buildbox`. This is the first step in + transitioning to only using BuildBox for local sandboxing. + o BREAKING CHANGE: The yaml API has been rewritten entirely. When accessing configuration from YAML, please use the new `Node` classes exposed in the `buildstream` package. See the documentation for how to use it. diff --git a/src/buildstream/_platform/linux.py b/src/buildstream/_platform/linux.py index b69dd456e..b400bfaac 100644 --- a/src/buildstream/_platform/linux.py +++ b/src/buildstream/_platform/linux.py @@ -24,6 +24,7 @@ from .. import utils from ..sandbox import SandboxDummy from .platform import Platform +from .._exceptions import PlatformError class Linux(Platform): @@ -31,6 +32,7 @@ class Linux(Platform): def _setup_sandbox(self, force_sandbox): sandbox_setups = { 'bwrap': self._setup_bwrap_sandbox, + 'buildbox': self._setup_buildbox_sandbox, 'chroot': self._setup_chroot_sandbox, 'dummy': self._setup_dummy_sandbox, } @@ -67,6 +69,7 @@ class Linux(Platform): # Private Methods # ################################################ + # Dummy sandbox methods @staticmethod def _check_dummy_sandbox_config(config): return True @@ -81,6 +84,7 @@ class Linux(Platform): self.create_sandbox = self._create_dummy_sandbox return True + # Bubble-wrap sandbox methods def _check_sandbox_config_bwrap(self, config): from ..sandbox._sandboxbwrap import SandboxBwrap return SandboxBwrap.check_sandbox_config(self, config) @@ -103,6 +107,7 @@ class Linux(Platform): self.create_sandbox = self._create_bwrap_sandbox return True + # Chroot sandbox methods def _check_sandbox_config_chroot(self, config): from ..sandbox._sandboxchroot import SandboxChroot return SandboxChroot.check_sandbox_config(self, config) @@ -118,3 +123,23 @@ class Linux(Platform): self.check_sandbox_config = self._check_sandbox_config_chroot self.create_sandbox = Linux._create_chroot_sandbox return True + + # Buildbox sandbox methods + def _check_sandbox_config_buildbox(self, config): + from ..sandbox._sandboxbuildbox import SandboxBuildBox + return SandboxBuildBox.check_sandbox_config(self, config) + + @staticmethod + def _create_buildbox_sandbox(*args, **kwargs): + from ..sandbox._sandboxbuildbox import SandboxBuildBox + if kwargs.get('allow_real_directory'): + raise PlatformError("The BuildBox Sandbox does not support real directories.", + reason="You are using BuildBox sandbox because BST_FORCE_SANBOX=buildbox") + return SandboxBuildBox(*args, **kwargs) + + def _setup_buildbox_sandbox(self): + from ..sandbox._sandboxbuildbox import SandboxBuildBox + self._check_sandbox(SandboxBuildBox) + self.check_sandbox_config = self._check_sandbox_config_buildbox + self.create_sandbox = self._create_buildbox_sandbox + return True diff --git a/src/buildstream/sandbox/_sandboxbuildbox.py b/src/buildstream/sandbox/_sandboxbuildbox.py new file mode 100644 index 000000000..417d2224d --- /dev/null +++ b/src/buildstream/sandbox/_sandboxbuildbox.py @@ -0,0 +1,247 @@ +# +# Copyright (C) 2018 Bloomberg LP +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +import os +import sys +import signal +import subprocess +from contextlib import ExitStack + +import psutil + +from .. import utils, _signals, ProgramNotFoundError +from . import Sandbox, SandboxFlags +from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 +from ..storage._casbaseddirectory import CasBasedDirectory +from .._exceptions import SandboxError + + +# SandboxBuidBox() +# +# BuildBox-based sandbox implementation. +# +class SandboxBuildBox(Sandbox): + + def __init__(self, context, project, directory, **kwargs): + if kwargs.get('allow_real_directory'): + raise SandboxError("BuildBox does not support real directories") + else: + kwargs['allow_real_directory'] = False + super().__init__(context, project, directory, **kwargs) + + @classmethod + def check_available(cls): + try: + utils.get_host_tool('buildbox') + except utils.ProgramNotFoundError as Error: + cls._dummy_reasons += ["buildbox not found"] + raise SandboxError(" and ".join(cls._dummy_reasons), + reason="unavailable-local-sandbox") from Error + + @classmethod + def check_sandbox_config(cls, platform, config): + # Report error for elements requiring non-0 UID/GID + # TODO + if config.build_uid != 0 or config.build_gid != 0: + return False + + # Check host os and architecture match + if config.build_os != platform.get_host_os(): + raise SandboxError("Configured and host OS don't match.") + elif config.build_arch != platform.get_host_arch(): + raise SandboxError("Configured and host architecture don't match.") + + return True + + def _run(self, command, flags, *, cwd, env): + stdout, stderr = self._get_output() + + root_directory = self.get_virtual_directory() + scratch_directory = self._get_scratch_directory() + + if not self._has_command(command[0], env): + raise SandboxError("Staged artifacts do not provide command " + "'{}'".format(command[0]), + reason='missing-command') + + # Grab the full path of the buildbox binary + try: + buildbox_command = [utils.get_host_tool('buildbox')] + except ProgramNotFoundError as Err: + raise SandboxError(("BuildBox not on path, you are using the BuildBox sandbox because " + "BST_FORCE_SANDBOX=buildbox")) from Err + + for mark in self._get_marked_directories(): + path = mark['directory'] + assert path.startswith('/') and len(path) > 1 + root_directory.descend(*path[1:].split(os.path.sep), create=True) + + digest = root_directory._get_digest() + with open(os.path.join(scratch_directory, 'in'), 'wb') as input_digest_file: + input_digest_file.write(digest.SerializeToString()) + + buildbox_command += ["--local=" + root_directory.cas_cache.casdir] + buildbox_command += ["--input-digest=in"] + buildbox_command += ["--output-digest=out"] + + common_details = ("BuildBox is a experimental sandbox and does not support the requested feature.\n" + "You are using this feature because BST_FORCE_SANDBOX=buildbox.") + + if not flags & SandboxFlags.NETWORK_ENABLED: + # TODO + self._issue_warning( + "BuildBox sandbox does not have Networking yet", + detail=common_details + ) + + if cwd is not None: + buildbox_command += ['--chdir=' + cwd] + + # In interactive mode, we want a complete devpts inside + # the container, so there is a /dev/console and such. In + # the regular non-interactive sandbox, we want to hand pick + # a minimal set of devices to expose to the sandbox. + # + if flags & SandboxFlags.INTERACTIVE: + # TODO + self._issue_warning( + "BuildBox sandbox does not fully support BuildStream shells yet", + detail=common_details + ) + + if flags & SandboxFlags.ROOT_READ_ONLY: + # TODO + self._issue_warning( + "BuildBox sandbox does not fully support BuildStream `Read only Root`", + detail=common_details + ) + + # Set UID and GID + if not flags & SandboxFlags.INHERIT_UID: + # TODO + self._issue_warning( + "BuildBox sandbox does not fully support BuildStream Inherit UID", + detail=common_details + ) + + os.makedirs(os.path.join(scratch_directory, 'mnt'), exist_ok=True) + buildbox_command += ['mnt'] + + # Add the command + buildbox_command += command + + # Use the MountMap context manager to ensure that any redirected + # mounts through fuse layers are in context and ready for buildbox + # to mount them from. + # + with ExitStack() as stack: + # Ensure the cwd exists + if cwd is not None and len(cwd) > 1: + assert cwd.startswith('/') + root_directory.descend(*cwd[1:].split(os.path.sep), create=True) + + # If we're interactive, we want to inherit our stdin, + # otherwise redirect to /dev/null, ensuring process + # disconnected from terminal. + if flags & SandboxFlags.INTERACTIVE: + stdin = sys.stdin + else: + stdin = stack.enter_context(open(os.devnull, "r")) + + # Run buildbox ! + exit_code = self.run_buildbox(buildbox_command, stdin, stdout, stderr, env, + interactive=(flags & SandboxFlags.INTERACTIVE), + cwd=scratch_directory) + + if exit_code == 0: + with open(os.path.join(scratch_directory, 'out'), 'rb') as output_digest_file: + output_digest = remote_execution_pb2.Digest() + output_digest.ParseFromString(output_digest_file.read()) + self._vdir = CasBasedDirectory(root_directory.cas_cache, digest=output_digest) + + return exit_code + + def run_buildbox(self, argv, stdin, stdout, stderr, env, *, interactive, cwd): + def kill_proc(): + if process: + # First attempt to gracefully terminate + proc = psutil.Process(process.pid) + proc.terminate() + + try: + proc.wait(20) + except psutil.TimeoutExpired: + utils._kill_process_tree(process.pid) + + def suspend_proc(): + group_id = os.getpgid(process.pid) + os.killpg(group_id, signal.SIGSTOP) + + def resume_proc(): + group_id = os.getpgid(process.pid) + os.killpg(group_id, signal.SIGCONT) + + with _signals.suspendable(suspend_proc, resume_proc), _signals.terminator(kill_proc): + process = subprocess.Popen( + argv, + close_fds=True, + env=env, + stdin=stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + start_new_session=interactive + ) + + # Wait for the child process to finish, ensuring that + # a SIGINT has exactly the effect the user probably + # expects (i.e. let the child process handle it). + try: + while True: + try: + _, status = os.waitpid(process.pid, 0) + # If the process exits due to a signal, we + # brutally murder it to avoid zombies + if not os.WIFEXITED(status): + utils._kill_process_tree(process.pid) + + # Unlike in the bwrap case, here only the main + # process seems to receive the SIGINT. We pass + # on the signal to the child and then continue + # to wait. + except KeyboardInterrupt: + process.send_signal(signal.SIGINT) + continue + + break + # If we can't find the process, it has already died of + # its own accord, and therefore we don't need to check + # or kill anything. + except psutil.NoSuchProcess: + pass + + # Return the exit code - see the documentation for + # os.WEXITSTATUS to see why this is required. + if os.WIFEXITED(status): + exit_code = os.WEXITSTATUS(status) + else: + exit_code = -1 + + return exit_code + + def _use_cas_based_directory(self): + # Always use CasBasedDirectory for BuildBox + return True diff --git a/src/buildstream/sandbox/_sandboxbwrap.py b/src/buildstream/sandbox/_sandboxbwrap.py index 1155793c6..81e9f34de 100644 --- a/src/buildstream/sandbox/_sandboxbwrap.py +++ b/src/buildstream/sandbox/_sandboxbwrap.py @@ -336,7 +336,7 @@ class SandboxBwrap(Sandbox): # The only message relevant to us now is the exit-code of the subprocess. for line in json_status_file: with suppress(json.decoder.JSONDecodeError): - o = json.loads(line) + o = json.loads(line.decode()) if isinstance(o, collections.abc.Mapping) and 'exit-code' in o: child_exit_code = o['exit-code'] break diff --git a/src/buildstream/sandbox/sandbox.py b/src/buildstream/sandbox/sandbox.py index 4cab7d6b8..ece15c949 100644 --- a/src/buildstream/sandbox/sandbox.py +++ b/src/buildstream/sandbox/sandbox.py @@ -548,13 +548,17 @@ class Sandbox(): # Returns: # (bool): Whether a command exists inside the sandbox. def _has_command(self, command, env=None): + vroot = self.get_virtual_directory() + command_as_parts = command.lstrip(os.sep).split(os.sep) if os.path.isabs(command): - return os.path.lexists(os.path.join( - self._root, command.lstrip(os.sep))) + return vroot._exists(*command_as_parts, follow_symlinks=True) + + if len(command_as_parts) > 1: + return False for path in env.get('PATH').split(':'): - if os.path.lexists(os.path.join( - self._root, path.lstrip(os.sep), command)): + path_as_parts = path.lstrip(os.sep).split(os.sep) + if vroot._exists(*path_as_parts, command, follow_symlinks=True): return True return False @@ -609,6 +613,22 @@ class Sandbox(): self._build_directory = directory self._build_directory_always = always + # _issue_warning() + # + # Issue warning with __context that is not available with subclasses + # + # Args: + # message (str): A message to issue + # details (str): optional, more detatils + def _issue_warning(self, message, detail=None): + self.__context.messenger.message( + Message(None, + MessageType.WARN, + message, + detail=detail + ) + ) + # _SandboxBatch() # diff --git a/src/buildstream/storage/_casbaseddirectory.py b/src/buildstream/storage/_casbaseddirectory.py index 2c5d751e4..424b7ef63 100644 --- a/src/buildstream/storage/_casbaseddirectory.py +++ b/src/buildstream/storage/_casbaseddirectory.py @@ -28,6 +28,9 @@ See also: :ref:`sandboxing`. """ import os +import stat +import tarfile as tarfilelib +from io import StringIO from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 from .directory import Directory, VirtualDirectoryError, _FileType @@ -156,7 +159,15 @@ class CasBasedDirectory(Directory): self.__invalidate_digest() - def descend(self, *paths, create=False): + def find_root(self): + """ Finds the root of this directory tree by following 'parent' until there is + no parent. """ + if self.parent: + return self.parent.find_root() + else: + return self + + def descend(self, *paths, create=False, follow_symlinks=False): """Descend one or more levels of directory hierarchy and return a new Directory object for that directory. @@ -174,6 +185,7 @@ class CasBasedDirectory(Directory): """ current_dir = self + paths = list(paths) for path in paths: # Skip empty path segments @@ -181,20 +193,37 @@ class CasBasedDirectory(Directory): continue entry = current_dir.index.get(path) + if entry: if entry.type == _FileType.DIRECTORY: current_dir = entry.get_directory(current_dir) + elif follow_symlinks and entry.type == _FileType.SYMLINK: + linklocation = entry.target + newpaths = linklocation.split(os.path.sep) + if os.path.isabs(linklocation): + current_dir = current_dir.find_root().descend(*newpaths, follow_symlinks=True) + else: + current_dir = current_dir.descend(*newpaths, follow_symlinks=True) else: error = "Cannot descend into {}, which is a '{}' in the directory {}" raise VirtualDirectoryError(error.format(path, current_dir.index[path].type, - current_dir)) + current_dir), + reason="not-a-directory") else: - if create: + if path == '.': + continue + elif path == '..': + if current_dir.parent is not None: + current_dir = current_dir.parent + # In POSIX /.. == / so just stay at the root dir + continue + elif create: current_dir = current_dir._add_directory(path) else: error = "'{}' not found in {}" - raise VirtualDirectoryError(error.format(path, str(current_dir))) + raise VirtualDirectoryError(error.format(path, str(current_dir)), + reason="directory-not-found") return current_dir @@ -270,8 +299,10 @@ class CasBasedDirectory(Directory): self._copy_link_from_filesystem(source_directory, direntry.name) result.files_written.append(relative_pathname) - def _partial_import_cas_into_cas(self, source_directory, filter_callback, *, path_prefix="", result): + def _partial_import_cas_into_cas(self, source_directory, filter_callback, *, path_prefix="", origin=None, result): """ Import files from a CAS-based directory. """ + if origin is None: + origin = self for name, entry in source_directory.index.items(): # The destination filename, relative to the root where the import started @@ -307,6 +338,8 @@ class CasBasedDirectory(Directory): subdir.__add_files_to_result(path_prefix=relative_pathname, result=result) else: src_subdir = source_directory.descend(name) + if src_subdir == origin: + continue try: dest_subdir = self.descend(name, create=create_subdir) @@ -316,7 +349,8 @@ class CasBasedDirectory(Directory): .format(filetype, relative_pathname)) dest_subdir._partial_import_cas_into_cas(src_subdir, filter_callback, - path_prefix=relative_pathname, result=result) + path_prefix=relative_pathname, + origin=origin, result=result) if filter_callback and not filter_callback(relative_pathname): if is_dir and create_subdir and dest_subdir.is_empty(): @@ -408,7 +442,33 @@ class CasBasedDirectory(Directory): self.cas_cache.checkout(to_directory, self._get_digest(), can_link=can_link) def export_to_tar(self, tarfile, destination_dir, mtime=BST_ARBITRARY_TIMESTAMP): - raise NotImplementedError() + for filename, entry in self.index.items(): + arcname = os.path.join(destination_dir, filename) + if entry.type == _FileType.DIRECTORY: + tarinfo = tarfilelib.TarInfo(arcname) + tarinfo.mtime = mtime + tarinfo.type = tarfilelib.DIRTYPE + tarinfo.mode = 0o755 + tarfile.addfile(tarinfo) + self.descend(filename).export_to_tar(tarfile, arcname, mtime) + elif entry.type == _FileType.REGULAR_FILE: + source_name = self.cas_cache.objpath(entry.digest) + tarinfo = tarfilelib.TarInfo(arcname) + tarinfo.mtime = mtime + tarinfo.mode |= entry.is_executable & stat.S_IXUSR + tarinfo.size = os.path.getsize(source_name) + with open(source_name, "rb") as f: + tarfile.addfile(tarinfo, f) + elif entry.type == _FileType.SYMLINK: + tarinfo = tarfilelib.TarInfo(arcname) + tarinfo.mtime = mtime + tarinfo.mode |= entry.is_executable & stat.S_IXUSR + tarinfo.linkname = entry.target + tarinfo.type = tarfilelib.SYMTYPE + f = StringIO(entry.target) + tarfile.addfile(tarinfo, f) + else: + raise VirtualDirectoryError("can not export file type {} to tar".format(entry.type)) def _mark_changed(self): """ It should not be possible to externally modify a CAS-based @@ -584,6 +644,23 @@ class CasBasedDirectory(Directory): return self.__digest + def _exists(self, *path, follow_symlinks=False): + try: + subdir = self.descend(*path[:-1], follow_symlinks=follow_symlinks) + target = subdir.index.get(path[-1]) + if target is not None: + if target.type == _FileType.REGULAR_FILE: + return True + elif follow_symlinks and target.type == _FileType.SYMLINK: + linklocation = target.target + newpath = linklocation.split(os.path.sep) + if os.path.isabs(linklocation): + return subdir.find_root()._exists(*newpath, follow_symlinks=True) + return subdir._exists(*newpath, follow_symlinks=True) + return False + except VirtualDirectoryError: + return False + def __invalidate_digest(self): if self.__digest: self.__digest = None diff --git a/src/buildstream/storage/_filebaseddirectory.py b/src/buildstream/storage/_filebaseddirectory.py index 8c55819c9..07c23c192 100644 --- a/src/buildstream/storage/_filebaseddirectory.py +++ b/src/buildstream/storage/_filebaseddirectory.py @@ -37,6 +37,7 @@ from .. import utils from ..utils import link_files, copy_files, list_relative_paths, _get_link_mtime, BST_ARBITRARY_TIMESTAMP from ..utils import _set_deterministic_user, _set_deterministic_mtime from ..utils import FileListResult +from .._exceptions import ImplError # FileBasedDirectory intentionally doesn't call its superclass constuctor, # which is meant to be unimplemented. @@ -47,9 +48,12 @@ class FileBasedDirectory(Directory): def __init__(self, external_directory=None): self.external_directory = external_directory - def descend(self, *paths, create=False): + def descend(self, *paths, create=False, follow_symlinks=False): """ See superclass Directory for arguments """ + if follow_symlinks: + ImplError("FileBasedDirectory.Decend dose not implement follow_symlinks=True") + current_dir = self for path in paths: @@ -281,3 +285,12 @@ class FileBasedDirectory(Directory): assert entry.type == _FileType.SYMLINK os.symlink(entry.target, dest_path) result.files_written.append(relative_pathname) + + def _exists(self, *path, follow_symlinks=False): + """This is very simple but mirrors the cas based storage were it is less trivial""" + if follow_symlinks: + # The lexists is not ideal as it cant spot broken symlinks but this is a long + # standing bug in buildstream as exists follow absolute syslinks to real root + # and incorrectly thinks they are broken the new casbaseddirectory dose not have this bug. + return os.path.lexists(os.path.join(self.external_directory, *path)) + raise ImplError("_exists can only follow symlinks in filebaseddirectory") diff --git a/src/buildstream/storage/directory.py b/src/buildstream/storage/directory.py index c9906b058..d32ac0063 100644 --- a/src/buildstream/storage/directory.py +++ b/src/buildstream/storage/directory.py @@ -52,7 +52,7 @@ class Directory(): def __init__(self, external_directory=None): raise NotImplementedError() - def descend(self, *paths, create=False): + def descend(self, *paths, create=False, follow_symlinks=False): """Descend one or more levels of directory hierarchy and return a new Directory object for that directory. diff --git a/src/buildstream/testing/_sourcetests/source_determinism.py b/src/buildstream/testing/_sourcetests/source_determinism.py index fc8ad9893..d46d38b33 100644 --- a/src/buildstream/testing/_sourcetests/source_determinism.py +++ b/src/buildstream/testing/_sourcetests/source_determinism.py @@ -50,6 +50,7 @@ def create_test_directory(*path, mode=0o644): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.parametrize("kind", [*ALL_REPO_KINDS]) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.skipif(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox, Must Fix') def test_deterministic_source_umask(cli, tmpdir, datafiles, kind): project = str(datafiles) element_name = 'list.bst' diff --git a/tests/elements/filter.py b/tests/elements/filter.py index db20529bc..d8370c6bb 100644 --- a/tests/elements/filter.py +++ b/tests/elements/filter.py @@ -8,6 +8,7 @@ import pytest from buildstream.testing import create_repo from buildstream.testing import cli # pylint: disable=unused-import +from buildstream.testing._utils.site import HAVE_SANDBOX from buildstream._exceptions import ErrorDomain from buildstream import _yaml @@ -30,6 +31,7 @@ def test_filter_include(datafiles, cli, tmpdir): assert not os.path.exists(os.path.join(checkout, "bar")) +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) @pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic')) def test_filter_include_dynamic(datafiles, cli, tmpdir): project = str(datafiles) diff --git a/tests/examples/autotools.py b/tests/examples/autotools.py index ca311c4bb..e55f33493 100644 --- a/tests/examples/autotools.py +++ b/tests/examples/autotools.py @@ -6,7 +6,7 @@ import pytest from buildstream.testing import cli_integration as cli # pylint: disable=unused-import from buildstream.testing.integration import assert_contains -from buildstream.testing._utils.site import HAVE_BWRAP, IS_LINUX, MACHINE_ARCH +from buildstream.testing._utils.site import IS_LINUX, MACHINE_ARCH, HAVE_SANDBOX pytestmark = pytest.mark.integration @@ -18,7 +18,10 @@ DATA_DIR = os.path.join( # Tests a build of the autotools amhello project on a alpine-linux base runtime @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with sandbox') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This test is not meant to work with chroot sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI @pytest.mark.datafiles(DATA_DIR) def test_autotools_build(cli, datafiles): project = str(datafiles) @@ -41,7 +44,9 @@ def test_autotools_build(cli, datafiles): # Test running an executable built with autotools. @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This test is not meant to work with chroot sandbox') @pytest.mark.datafiles(DATA_DIR) def test_autotools_run(cli, datafiles): project = str(datafiles) diff --git a/tests/examples/developing.py b/tests/examples/developing.py index c2b950c01..58416414e 100644 --- a/tests/examples/developing.py +++ b/tests/examples/developing.py @@ -6,7 +6,7 @@ import pytest from buildstream.testing import cli_integration as cli # pylint: disable=unused-import from buildstream.testing.integration import assert_contains -from buildstream.testing._utils.site import HAVE_BWRAP, IS_LINUX, MACHINE_ARCH +from buildstream.testing._utils.site import IS_LINUX, MACHINE_ARCH, HAVE_SANDBOX import tests.testutils.patch as patch pytestmark = pytest.mark.integration @@ -19,7 +19,10 @@ DATA_DIR = os.path.join( # Test that the project builds successfully @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with SANDBOX') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This is not meant to work with chroot') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI @pytest.mark.datafiles(DATA_DIR) def test_autotools_build(cli, datafiles): project = str(datafiles) @@ -40,7 +43,10 @@ def test_autotools_build(cli, datafiles): # Test the unmodified hello command works as expected. @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with SANDBOX') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This is not meant to work with chroot') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI @pytest.mark.datafiles(DATA_DIR) def test_run_unmodified_hello(cli, datafiles): project = str(datafiles) @@ -73,7 +79,9 @@ def test_open_workspace(cli, tmpdir, datafiles): # Test making a change using the workspace @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with SANDBOX') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This is not meant to work with chroot') @pytest.mark.datafiles(DATA_DIR) def test_make_change_in_workspace(cli, tmpdir, datafiles): project = str(datafiles) diff --git a/tests/examples/integration-commands.py b/tests/examples/integration-commands.py index ad794351f..146ccdb80 100644 --- a/tests/examples/integration-commands.py +++ b/tests/examples/integration-commands.py @@ -5,7 +5,7 @@ import os import pytest from buildstream.testing import cli_integration as cli # pylint: disable=unused-import -from buildstream.testing._utils.site import HAVE_BWRAP, IS_LINUX, MACHINE_ARCH +from buildstream.testing._utils.site import IS_LINUX, MACHINE_ARCH, HAVE_SANDBOX pytestmark = pytest.mark.integration @@ -16,7 +16,10 @@ DATA_DIR = os.path.join( @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with sandbox') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This test is not meant to work with chroot sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI @pytest.mark.datafiles(DATA_DIR) def test_integration_commands_build(cli, datafiles): project = str(datafiles) @@ -28,7 +31,10 @@ def test_integration_commands_build(cli, datafiles): # Test running the executable @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with sandbox') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This test is not meant to work with chroot sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI @pytest.mark.datafiles(DATA_DIR) def test_integration_commands_run(cli, datafiles): project = str(datafiles) diff --git a/tests/examples/junctions.py b/tests/examples/junctions.py index bb88e5068..b77fcba8a 100644 --- a/tests/examples/junctions.py +++ b/tests/examples/junctions.py @@ -5,7 +5,7 @@ import os import pytest from buildstream.testing import cli_integration as cli # pylint: disable=unused-import -from buildstream.testing._utils.site import HAVE_BWRAP, IS_LINUX, MACHINE_ARCH +from buildstream.testing._utils.site import IS_LINUX, MACHINE_ARCH, HAVE_SANDBOX pytestmark = pytest.mark.integration @@ -17,7 +17,10 @@ DATA_DIR = os.path.join( # Test that the project builds successfully @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This test is not meant to work with chroot sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI @pytest.mark.datafiles(DATA_DIR) def test_build(cli, datafiles): project = str(datafiles) @@ -29,7 +32,10 @@ def test_build(cli, datafiles): # Test the callHello script works as expected. @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This test is not meant to work with chroot sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI @pytest.mark.datafiles(DATA_DIR) def test_shell_call_hello(cli, datafiles): project = str(datafiles) diff --git a/tests/examples/running-commands.py b/tests/examples/running-commands.py index 23b3e6467..ce81da0c8 100644 --- a/tests/examples/running-commands.py +++ b/tests/examples/running-commands.py @@ -5,7 +5,7 @@ import os import pytest from buildstream.testing import cli_integration as cli # pylint: disable=unused-import -from buildstream.testing._utils.site import HAVE_BWRAP, IS_LINUX, MACHINE_ARCH +from buildstream.testing._utils.site import IS_LINUX, MACHINE_ARCH, HAVE_SANDBOX pytestmark = pytest.mark.integration @@ -16,8 +16,11 @@ DATA_DIR = os.path.join( @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') @pytest.mark.datafiles(DATA_DIR) +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with sandbox') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This test is not meant to work with chroot sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_running_commands_build(cli, datafiles): project = str(datafiles) @@ -28,7 +31,10 @@ def test_running_commands_build(cli, datafiles): # Test running the executable @pytest.mark.skipif(MACHINE_ARCH != 'x86-64', reason='Examples are written for x86-64') -@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +@pytest.mark.skipif(not IS_LINUX or not HAVE_SANDBOX, reason='Only available on linux with sandbox') +@pytest.mark.skipif(HAVE_SANDBOX == 'chroot', reason='This test is not meant to work with chroot sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI @pytest.mark.datafiles(DATA_DIR) def test_running_commands_run(cli, datafiles): project = str(datafiles) diff --git a/tests/frontend/buildcheckout.py b/tests/frontend/buildcheckout.py index a3f68c031..dd4a461ea 100644 --- a/tests/frontend/buildcheckout.py +++ b/tests/frontend/buildcheckout.py @@ -425,6 +425,49 @@ def test_build_checkout_tarball_is_deterministic(datafiles, cli): @pytest.mark.datafiles(DATA_DIR) +def test_build_checkout_tarball_links(datafiles, cli): + project = str(datafiles) + checkout = os.path.join(cli.directory, 'checkout.tar') + extract = os.path.join(cli.directory, 'extract') + + result = cli.run(project=project, args=['build', 'import-links.bst']) + result.assert_success() + + builddir = os.path.join(cli.directory, 'build') + assert os.path.isdir(builddir) + assert not os.listdir(builddir) + + checkout_args = ['artifact', 'checkout', '--tar', checkout, 'import-links.bst'] + + result = cli.run(project=project, args=checkout_args) + result.assert_success() + + tar = tarfile.open(name=checkout, mode="r:") + tar.extractall(extract) + assert open(os.path.join(extract, 'basicfolder', 'basicsymlink')).read() == "file contents\n" + + +@pytest.mark.datafiles(DATA_DIR) +def test_build_checkout_links(datafiles, cli): + project = str(datafiles) + checkout = os.path.join(cli.directory, 'checkout') + + result = cli.run(project=project, args=['build', 'import-links.bst']) + result.assert_success() + + builddir = os.path.join(cli.directory, 'build') + assert os.path.isdir(builddir) + assert not os.listdir(builddir) + + checkout_args = ['artifact', 'checkout', '--directory', checkout, 'import-links.bst'] + + result = cli.run(project=project, args=checkout_args) + result.assert_success() + + assert open(os.path.join(checkout, 'basicfolder', 'basicsymlink')).read() == "file contents\n" + + +@pytest.mark.datafiles(DATA_DIR) @pytest.mark.parametrize("hardlinks", [("copies"), ("hardlinks")]) def test_build_checkout_nonempty(datafiles, cli, hardlinks): project = str(datafiles) diff --git a/tests/frontend/configurable_warnings.py b/tests/frontend/configurable_warnings.py index 7936b2f89..38b7f2912 100644 --- a/tests/frontend/configurable_warnings.py +++ b/tests/frontend/configurable_warnings.py @@ -9,6 +9,7 @@ from buildstream.plugin import CoreWarnings from buildstream._exceptions import ErrorDomain from buildstream import _yaml from buildstream.testing.runcli import cli # pylint: disable=unused-import +from buildstream.testing._utils.site import HAVE_SANDBOX TOP_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), @@ -45,6 +46,7 @@ def build_project(datafiles, fatal_warnings): return project_path +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') @pytest.mark.datafiles(TOP_DIR) @pytest.mark.parametrize("element_name, fatal_warnings, expect_fatal, error_domain", [ ("corewarn.bst", [CoreWarnings.OVERLAPS], True, ErrorDomain.STREAM), diff --git a/tests/frontend/project/elements/import-links.bst b/tests/frontend/project/elements/import-links.bst new file mode 100644 index 000000000..42b279ee2 --- /dev/null +++ b/tests/frontend/project/elements/import-links.bst @@ -0,0 +1,4 @@ +kind: import +sources: +- kind: local + path: files/files-and-links diff --git a/tests/frontend/project/files/files-and-links/basicfile b/tests/frontend/project/files/files-and-links/basicfile new file mode 100644 index 000000000..d03e2425c --- /dev/null +++ b/tests/frontend/project/files/files-and-links/basicfile @@ -0,0 +1 @@ +file contents diff --git a/tests/frontend/project/files/files-and-links/basicfolder/basicsymlink b/tests/frontend/project/files/files-and-links/basicfolder/basicsymlink new file mode 120000 index 000000000..e2b4f7423 --- /dev/null +++ b/tests/frontend/project/files/files-and-links/basicfolder/basicsymlink @@ -0,0 +1 @@ +../basicfile
\ No newline at end of file diff --git a/tests/frontend/project/files/files-and-links/basicfolder/subdir-file b/tests/frontend/project/files/files-and-links/basicfolder/subdir-file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/frontend/project/files/files-and-links/basicfolder/subdir-file diff --git a/tests/integration/artifact.py b/tests/integration/artifact.py index 56c516e67..6ad0f8477 100644 --- a/tests/integration/artifact.py +++ b/tests/integration/artifact.py @@ -45,8 +45,12 @@ DATA_DIR = os.path.join( # A test to capture the integration of the cachebuildtrees # behaviour, which by default is to include the buildtree # content of an element on caching. + +# Dse this really need a sandbox? @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_cache_buildtrees(cli, tmpdir, datafiles): project = str(datafiles) element_name = 'autotools/amhello.bst' diff --git a/tests/integration/autotools.py b/tests/integration/autotools.py index c4bf429f5..250ab90d1 100644 --- a/tests/integration/autotools.py +++ b/tests/integration/autotools.py @@ -22,6 +22,8 @@ DATA_DIR = os.path.join( # amhello project for this. @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_autotools_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -44,6 +46,7 @@ def test_autotools_build(cli, datafiles): # amhello project for this. @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_autotools_confroot_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -65,6 +68,8 @@ def test_autotools_confroot_build(cli, datafiles): # Test running an executable built with autotools @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_autotools_run(cli, datafiles): project = str(datafiles) element_name = 'autotools/amhello.bst' diff --git a/tests/integration/cachedfail.py b/tests/integration/cachedfail.py index a7509ab3b..4a469e21a 100644 --- a/tests/integration/cachedfail.py +++ b/tests/integration/cachedfail.py @@ -39,6 +39,8 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_build_checkout_cached_fail(cli, datafiles): project = str(datafiles) element_path = os.path.join(project, 'elements', 'element.bst') @@ -139,6 +141,7 @@ def test_build_depend_on_cached_fail(cli, datafiles): @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') @pytest.mark.datafiles(DATA_DIR) @pytest.mark.parametrize("on_error", ("continue", "quit")) def test_push_cached_fail(cli, tmpdir, datafiles, on_error): diff --git a/tests/integration/cmake.py b/tests/integration/cmake.py index 84ea96af2..3cb56ab5c 100644 --- a/tests/integration/cmake.py +++ b/tests/integration/cmake.py @@ -20,6 +20,8 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_cmake_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -36,6 +38,7 @@ def test_cmake_build(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_cmake_confroot_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -52,6 +55,8 @@ def test_cmake_confroot_build(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_cmake_run(cli, datafiles): project = str(datafiles) element_name = 'cmake/cmakehello.bst' diff --git a/tests/integration/compose.py b/tests/integration/compose.py index 3562ed94b..dc8f4f858 100644 --- a/tests/integration/compose.py +++ b/tests/integration/compose.py @@ -80,6 +80,8 @@ def create_compose_element(name, path, config=None): '/usr/share/doc/amhello/README']) ]) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_compose_include(cli, datafiles, include_domains, exclude_domains, expected): project = str(datafiles) diff --git a/tests/integration/make.py b/tests/integration/make.py index 664e7ca7a..4678c5319 100644 --- a/tests/integration/make.py +++ b/tests/integration/make.py @@ -22,6 +22,7 @@ DATA_DIR = os.path.join( # makehello project for this. @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') def test_make_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -40,6 +41,7 @@ def test_make_build(cli, datafiles): # Test running an executable built with make @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') def test_make_run(cli, datafiles): project = str(datafiles) element_name = 'make/makehello.bst' diff --git a/tests/integration/manual.py b/tests/integration/manual.py index 2ac7f74d0..bcfcaaf41 100644 --- a/tests/integration/manual.py +++ b/tests/integration/manual.py @@ -36,6 +36,8 @@ def create_manual_element(name, path, config, variables, environment): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_manual_element(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -70,6 +72,8 @@ strip @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_manual_element_environment(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -100,6 +104,8 @@ def test_manual_element_environment(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_manual_element_noparallel(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -135,6 +141,8 @@ def test_manual_element_noparallel(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_manual_element_logging(cli, datafiles): project = str(datafiles) element_path = os.path.join(project, 'elements') diff --git a/tests/integration/messages.py b/tests/integration/messages.py index 42725fc5b..0313a6347 100644 --- a/tests/integration/messages.py +++ b/tests/integration/messages.py @@ -41,6 +41,8 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_disable_message_lines(cli, datafiles): project = str(datafiles) element_path = os.path.join(project, 'elements') diff --git a/tests/integration/pip_element.py b/tests/integration/pip_element.py index da0badcb3..85a922c00 100644 --- a/tests/integration/pip_element.py +++ b/tests/integration/pip_element.py @@ -25,6 +25,8 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_pip_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -62,6 +64,8 @@ def test_pip_build(cli, datafiles): # Test running an executable built with pip @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_pip_run(cli, datafiles): # Create and build our test element test_pip_build(cli, datafiles) @@ -76,6 +80,8 @@ def test_pip_run(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_pip_element_should_install_pip_deps(cli, datafiles, setup_pypi_repo): project = str(datafiles) elements_path = os.path.join(project, 'elements') diff --git a/tests/integration/pip_source.py b/tests/integration/pip_source.py index c221910a6..c8f997800 100644 --- a/tests/integration/pip_source.py +++ b/tests/integration/pip_source.py @@ -140,6 +140,8 @@ def test_pip_source_import_requirements_files(cli, datafiles, setup_pypi_repo): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_pip_source_build(cli, datafiles, setup_pypi_repo): project = str(datafiles) element_path = os.path.join(project, 'elements') diff --git a/tests/integration/project/elements/symlinks/link-on-path-use.bst b/tests/integration/project/elements/symlinks/link-on-path-use.bst new file mode 100644 index 000000000..ce57872a6 --- /dev/null +++ b/tests/integration/project/elements/symlinks/link-on-path-use.bst @@ -0,0 +1,10 @@ +kind: manual + +depends: + - filename: symlinks/link-on-path.bst + +config: + build-commands: + - touch %{install-root}/foo + + diff --git a/tests/integration/project/elements/symlinks/link-on-path.bst b/tests/integration/project/elements/symlinks/link-on-path.bst new file mode 100644 index 000000000..d74cbb81c --- /dev/null +++ b/tests/integration/project/elements/symlinks/link-on-path.bst @@ -0,0 +1,13 @@ +kind: manual + +depends: +- filename: base.bst + type: build + +config: + install-commands: + - | + cd "%{install-root}" + cp -r /bin /lib . + mv bin altbin + ln -s altbin bin diff --git a/tests/integration/pullbuildtrees.py b/tests/integration/pullbuildtrees.py index af9186b1b..437b3e0a5 100644 --- a/tests/integration/pullbuildtrees.py +++ b/tests/integration/pullbuildtrees.py @@ -37,6 +37,8 @@ def default_state(cli, tmpdir, share): @pytest.mark.integration @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_pullbuildtrees(cli2, tmpdir, datafiles): project = str(datafiles) element_name = 'autotools/amhello.bst' diff --git a/tests/integration/script.py b/tests/integration/script.py index fc57e8744..04e70af32 100644 --- a/tests/integration/script.py +++ b/tests/integration/script.py @@ -40,6 +40,8 @@ def create_script_element(name, path, config=None, variables=None): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_script(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -68,6 +70,8 @@ def test_script(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_script_root(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -100,6 +104,7 @@ def test_script_root(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_script_no_root(cli, datafiles): project = str(datafiles) element_path = os.path.join(project, 'elements') @@ -123,6 +128,7 @@ def test_script_no_root(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_script_cwd(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -154,6 +160,8 @@ def test_script_cwd(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_script_layout(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -173,6 +181,8 @@ def test_script_layout(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_regression_cache_corruption(cli, datafiles): project = str(datafiles) checkout_original = os.path.join(cli.directory, 'checkout-original') @@ -203,6 +213,8 @@ def test_regression_cache_corruption(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_regression_tmpdir(cli, datafiles): project = str(datafiles) element_name = 'script/tmpdir.bst' @@ -213,6 +225,8 @@ def test_regression_tmpdir(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_regression_cache_corruption_2(cli, datafiles): project = str(datafiles) checkout_original = os.path.join(cli.directory, 'checkout-original') diff --git a/tests/integration/shell.py b/tests/integration/shell.py index a1f38d879..8ea5d5e69 100644 --- a/tests/integration/shell.py +++ b/tests/integration/shell.py @@ -57,6 +57,8 @@ def execute_shell(cli, project, command, *, config=None, mount=None, element='ba # executable @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_shell(cli, datafiles): project = str(datafiles) @@ -68,6 +70,8 @@ def test_shell(cli, datafiles): # Test running an executable directly @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_executable(cli, datafiles): project = str(datafiles) @@ -80,6 +84,8 @@ def test_executable(cli, datafiles): @pytest.mark.parametrize("animal", [("Horse"), ("Pony")]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_env_assign(cli, datafiles, animal): project = str(datafiles) expected = animal + '\n' @@ -100,6 +106,8 @@ def test_env_assign(cli, datafiles, animal): @pytest.mark.parametrize("animal", [("Horse"), ("Pony")]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_env_assign_expand_host_environ(cli, datafiles, animal): project = str(datafiles) expected = 'The animal is: {}\n'.format(animal) @@ -123,6 +131,8 @@ def test_env_assign_expand_host_environ(cli, datafiles, animal): @pytest.mark.parametrize("animal", [("Horse"), ("Pony")]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_env_assign_isolated(cli, datafiles, animal): project = str(datafiles) result = execute_shell(cli, project, ['/bin/sh', '-c', 'echo ${ANIMAL}'], isolate=True, config={ @@ -141,6 +151,8 @@ def test_env_assign_isolated(cli, datafiles, animal): # /bin/sh) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_no_shell(cli, datafiles): project = str(datafiles) element_path = os.path.join(project, 'elements') @@ -174,6 +186,7 @@ def test_no_shell(cli, datafiles): @pytest.mark.parametrize("path", [("/etc/pony.conf"), ("/usr/share/pony/pony.txt")]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_host_files(cli, datafiles, path): project = str(datafiles) ponyfile = os.path.join(project, 'files', 'shell-mount', 'pony.txt') @@ -195,6 +208,7 @@ def test_host_files(cli, datafiles, path): @pytest.mark.parametrize("path", [("/etc"), ("/usr/share/pony")]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_host_files_expand_environ(cli, datafiles, path): project = str(datafiles) hostpath = os.path.join(project, 'files', 'shell-mount') @@ -221,6 +235,7 @@ def test_host_files_expand_environ(cli, datafiles, path): @pytest.mark.parametrize("path", [("/etc/pony.conf"), ("/usr/share/pony/pony.txt")]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') def test_isolated_no_mount(cli, datafiles, path): project = str(datafiles) ponyfile = os.path.join(project, 'files', 'shell-mount', 'pony.txt') @@ -244,6 +259,7 @@ def test_isolated_no_mount(cli, datafiles, path): @pytest.mark.parametrize("optional", [("mandatory"), ("optional")]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') def test_host_files_missing(cli, datafiles, optional): project = str(datafiles) ponyfile = os.path.join(project, 'files', 'shell-mount', 'horsy.txt') @@ -277,6 +293,7 @@ def test_host_files_missing(cli, datafiles, optional): @pytest.mark.parametrize("path", [("/etc/pony.conf"), ("/usr/share/pony/pony.txt")]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_cli_mount(cli, datafiles, path): project = str(datafiles) ponyfile = os.path.join(project, 'files', 'shell-mount', 'pony.txt') @@ -289,6 +306,7 @@ def test_cli_mount(cli, datafiles, path): # Test that we can see the workspace files in a shell @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_workspace_visible(cli, datafiles): project = str(datafiles) workspace = os.path.join(cli.directory, 'workspace') @@ -323,6 +341,7 @@ def test_workspace_visible(cli, datafiles): # Test that '--sysroot' works @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_sysroot(cli, tmpdir, datafiles): project = str(datafiles) base_element = "base/base-alpine.bst" @@ -353,6 +372,8 @@ def test_sysroot(cli, tmpdir, datafiles): # Test system integration commands can access devices in /dev @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_integration_devices(cli, datafiles): project = str(datafiles) element_name = 'integration.bst' @@ -366,6 +387,7 @@ def test_integration_devices(cli, datafiles): @pytest.mark.parametrize("build_shell", [("build"), ("nobuild")]) @pytest.mark.parametrize("guess_element", [True, False], ids=["guess", "no-guess"]) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_integration_external_workspace(cli, tmpdir_factory, datafiles, build_shell, guess_element): tmpdir = tmpdir_factory.mktemp("") project = str(datafiles) @@ -399,6 +421,8 @@ def test_integration_external_workspace(cli, tmpdir_factory, datafiles, build_sh @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_integration_partial_artifact(cli, datafiles, tmpdir, integration_cache): project = str(datafiles) diff --git a/tests/integration/shellbuildtrees.py b/tests/integration/shellbuildtrees.py index a1eecb1eb..4f4d8b0b5 100644 --- a/tests/integration/shellbuildtrees.py +++ b/tests/integration/shellbuildtrees.py @@ -24,6 +24,8 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_buildtree_staged(cli_integration, datafiles): # We can only test the non interacitve case # The non interactive case defaults to not using buildtrees @@ -42,6 +44,8 @@ def test_buildtree_staged(cli_integration, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_buildtree_staged_forced_true(cli_integration, datafiles): # Test that if we ask for a build tree it is there. project = str(datafiles) @@ -59,8 +63,10 @@ def test_buildtree_staged_forced_true(cli_integration, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') -def test_buildtree_staged_warn_non_cached(cli_integration, tmpdir, datafiles): - # Test that if we attempt to stage a buildtree that was never cached, we warn the user. +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI +def test_buildtree_staged_warn_empty_cached(cli_integration, tmpdir, datafiles): + # Test that if we stage a cached and empty buildtree, we warn the user. project = str(datafiles) element_name = 'build-shell/buildtree.bst' @@ -90,6 +96,8 @@ def test_buildtree_staged_warn_non_cached(cli_integration, tmpdir, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_buildtree_staged_if_available(cli_integration, datafiles): # Test that a build tree can be correctly detected. project = str(datafiles) @@ -107,6 +115,8 @@ def test_buildtree_staged_if_available(cli_integration, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_buildtree_staged_forced_false(cli_integration, datafiles): # Test that if we ask not to have a build tree it is not there project = str(datafiles) @@ -125,6 +135,8 @@ def test_buildtree_staged_forced_false(cli_integration, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_buildtree_from_failure(cli_integration, datafiles): # Test that we can use a build tree after a failure project = str(datafiles) @@ -167,6 +179,8 @@ def test_buildtree_from_failure_option_never(cli_integration, tmpdir, datafiles) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_buildtree_from_failure_option_always(cli_integration, tmpdir, datafiles): project = str(datafiles) @@ -194,6 +208,8 @@ def test_buildtree_from_failure_option_always(cli_integration, tmpdir, datafiles # This is to roughly simulate remote execution @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_buildtree_pulled(cli, tmpdir, datafiles): project = str(datafiles) element_name = 'build-shell/buildtree.bst' @@ -227,6 +243,8 @@ def test_buildtree_pulled(cli, tmpdir, datafiles): # This test checks for correct behaviour if a buildtree is not present in the local cache. @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_buildtree_options(cli, tmpdir, datafiles): project = str(datafiles) element_name = 'build-shell/buildtree.bst' @@ -310,6 +328,8 @@ def test_buildtree_options(cli, tmpdir, datafiles): # Tests running pull and pull-buildtree options at the same time. @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_pull_buildtree_pulled(cli, tmpdir, datafiles): project = str(datafiles) element_name = 'build-shell/buildtree.bst' diff --git a/tests/integration/sockets.py b/tests/integration/sockets.py index 763238baf..246e48595 100644 --- a/tests/integration/sockets.py +++ b/tests/integration/sockets.py @@ -18,6 +18,8 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_builddir_socket_ignored(cli, datafiles): project = str(datafiles) element_name = 'sockets/make-builddir-socket.bst' @@ -28,6 +30,8 @@ def test_builddir_socket_ignored(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_install_root_socket_ignored(cli, datafiles): project = str(datafiles) element_name = 'sockets/make-install-root-socket.bst' diff --git a/tests/integration/source-determinism.py b/tests/integration/source-determinism.py index 70c4b79de..0ac954f4f 100644 --- a/tests/integration/source-determinism.py +++ b/tests/integration/source-determinism.py @@ -32,6 +32,8 @@ def create_test_directory(*path, mode=0o644): @pytest.mark.integration @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_deterministic_source_local(cli, tmpdir, datafiles): """Only user rights should be considered for local source. """ diff --git a/tests/integration/stack.py b/tests/integration/stack.py index 9d6b38345..c6eaba3c4 100644 --- a/tests/integration/stack.py +++ b/tests/integration/stack.py @@ -19,6 +19,8 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_stack(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') diff --git a/tests/integration/symlinks.py b/tests/integration/symlinks.py index ed6ee109c..c62bad586 100644 --- a/tests/integration/symlinks.py +++ b/tests/integration/symlinks.py @@ -19,6 +19,8 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_absolute_symlinks(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -40,6 +42,8 @@ def test_absolute_symlinks(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_disallow_overlaps_inside_symlink_with_dangling_target(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -55,6 +59,8 @@ def test_disallow_overlaps_inside_symlink_with_dangling_target(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI def test_detect_symlink_overlaps_pointing_outside_sandbox(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, 'checkout') @@ -70,3 +76,24 @@ def test_detect_symlink_overlaps_pointing_outside_sandbox(cli, datafiles): result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout]) assert result.exit_code == -1 assert 'Destination is a symlink, not a directory: /opt/escape-hatch' in result.stderr + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox') +# Not stricked xfail as only fails in CI +def test_symlink_in_sandbox_path(cli, datafiles): + project = str(datafiles) + element_name = 'symlinks/link-on-path-use.bst' + base_element_name = 'symlinks/link-on-path.bst' + # This test is inspired by how freedesktop-SDK has /bin -> /usr/bin + + # Create a element that has sh in altbin and a link from bin to altbin + result1 = cli.run(project=project, args=['build', base_element_name]) + result1.assert_success() + # Build a element that uses the element that has sh in altbin. + result2 = cli.run(project=project, args=['build', element_name]) + result2.assert_success() + # When this element is built it demonstrates that the virtual sandbox + # can detect sh across links and that the sandbox can find sh accross + # the link from its PATH. diff --git a/tests/integration/workspace.py b/tests/integration/workspace.py index 78379912c..045f8c490 100644 --- a/tests/integration/workspace.py +++ b/tests/integration/workspace.py @@ -20,6 +20,7 @@ DATA_DIR = os.path.join( @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_workspace_mount(cli, datafiles): project = str(datafiles) workspace = os.path.join(cli.directory, 'workspace') @@ -36,6 +37,7 @@ def test_workspace_mount(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_workspace_commanddir(cli, datafiles): project = str(datafiles) workspace = os.path.join(cli.directory, 'workspace') @@ -53,6 +55,7 @@ def test_workspace_commanddir(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_workspace_updated_dependency(cli, datafiles): project = str(datafiles) workspace = os.path.join(cli.directory, 'workspace') @@ -107,6 +110,7 @@ def test_workspace_updated_dependency(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_workspace_update_dependency_failed(cli, datafiles): project = str(datafiles) workspace = os.path.join(cli.directory, 'workspace') @@ -182,6 +186,7 @@ def test_workspace_update_dependency_failed(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_updated_dependency_nested(cli, datafiles): project = str(datafiles) workspace = os.path.join(cli.directory, 'workspace') @@ -235,6 +240,7 @@ def test_updated_dependency_nested(cli, datafiles): @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_incremental_configure_commands_run_only_once(cli, datafiles): project = str(datafiles) workspace = os.path.join(cli.directory, 'workspace') @@ -287,6 +293,7 @@ def test_incremental_configure_commands_run_only_once(cli, datafiles): # @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox') +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) def test_workspace_missing_last_successful(cli, datafiles): project = str(datafiles) workspace = os.path.join(cli.directory, 'workspace') diff --git a/tests/internals/storage_vdir_import.py b/tests/internals/storage_vdir_import.py index 9d42c6e8d..7c6cbe4fb 100644 --- a/tests/internals/storage_vdir_import.py +++ b/tests/internals/storage_vdir_import.py @@ -1,3 +1,18 @@ +# +# Copyright (C) 2019 Bloomberg LP +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. from hashlib import sha256 import os import random @@ -7,6 +22,7 @@ import pytest from buildstream.storage._casbaseddirectory import CasBasedDirectory from buildstream.storage._filebaseddirectory import FileBasedDirectory from buildstream._cas import CASCache +from buildstream.storage.directory import VirtualDirectoryError # These are comparitive tests that check that FileBasedDirectory and @@ -42,9 +58,13 @@ NUM_RANDOM_TESTS = 10 def generate_import_roots(rootno, directory): rootname = "root{}".format(rootno) rootdir = os.path.join(directory, "content", rootname) + generate_import_root(rootdir, root_filesets[rootno - 1]) + + +def generate_import_root(rootdir, filelist): if os.path.exists(rootdir): return - for (path, typesymbol, content) in root_filesets[rootno - 1]: + for (path, typesymbol, content) in filelist: if typesymbol == 'F': (dirnames, filename) = os.path.split(path) os.makedirs(os.path.join(rootdir, dirnames), exist_ok=True) @@ -251,3 +271,123 @@ def test_random_directory_listing(tmpdir, root): @pytest.mark.parametrize("root", [1, 2, 3, 4, 5]) def test_fixed_directory_listing(tmpdir, root): _listing_test(str(tmpdir), root, generate_import_roots) + + +# Check that the vdir is decending and readable +def test_descend(tmpdir): + cas_dir = os.path.join(str(tmpdir), 'cas') + cas_cache = CASCache(cas_dir) + d = CasBasedDirectory(cas_cache) + + Content_to_check = 'You got me' + test_dir = os.path.join(str(tmpdir), 'importfrom') + filesys_discription = [ + ('a', 'D', ''), + ('a/l', 'D', ''), + ('a/l/g', 'F', Content_to_check) + ] + generate_import_root(test_dir, filesys_discription) + + d.import_files(test_dir) + digest = d.descend('a', 'l').index['g'].get_digest() + + assert Content_to_check == open(cas_cache.objpath(digest)).read() + + +# Check symlink logic for edgecases +# Make sure the correct erros are raised when trying +# to decend in to files or links to files +def test_bad_symlinks(tmpdir): + cas_dir = os.path.join(str(tmpdir), 'cas') + cas_cache = CASCache(cas_dir) + d = CasBasedDirectory(cas_cache) + + test_dir = os.path.join(str(tmpdir), 'importfrom') + filesys_discription = [ + ('a', 'D', ''), + ('a/l', 'S', '../target'), + ('target', 'F', 'You got me') + ] + generate_import_root(test_dir, filesys_discription) + d.import_files(test_dir) + exp_reason = "not-a-directory" + + with pytest.raises(VirtualDirectoryError) as error: + d.descend('a', 'l', follow_symlinks=True) + assert error.reason == exp_reason + + with pytest.raises(VirtualDirectoryError) as error: + d.descend('a', 'l') + assert error.reason == exp_reason + + with pytest.raises(VirtualDirectoryError) as error: + d.descend('a', 'f') + assert error.reason == exp_reason + + +# Check symlink logic for edgecases +# Check decend accross relitive link +def test_relitive_symlink(tmpdir): + cas_dir = os.path.join(str(tmpdir), 'cas') + cas_cache = CASCache(cas_dir) + d = CasBasedDirectory(cas_cache) + + Content_to_check = 'You got me' + test_dir = os.path.join(str(tmpdir), 'importfrom') + filesys_discription = [ + ('a', 'D', ''), + ('a/l', 'S', '../target'), + ('target', 'D', ''), + ('target/file', 'F', Content_to_check) + ] + generate_import_root(test_dir, filesys_discription) + d.import_files(test_dir) + + digest = d.descend('a', 'l', follow_symlinks=True).index['file'].get_digest() + assert Content_to_check == open(cas_cache.objpath(digest)).read() + + +# Check symlink logic for edgecases +# Check deccend accross abs link +def test_abs_symlink(tmpdir): + cas_dir = os.path.join(str(tmpdir), 'cas') + cas_cache = CASCache(cas_dir) + d = CasBasedDirectory(cas_cache) + + Content_to_check = 'two step file' + test_dir = os.path.join(str(tmpdir), 'importfrom') + filesys_discription = [ + ('a', 'D', ''), + ('a/l', 'S', '/target'), + ('target', 'D', ''), + ('target/file', 'F', Content_to_check) + ] + generate_import_root(test_dir, filesys_discription) + d.import_files(test_dir) + + digest = d.descend('a', 'l', follow_symlinks=True).index['file'].get_digest() + + assert Content_to_check == open(cas_cache.objpath(digest)).read() + + +# Check symlink logic for edgecases +# Check symlink can not escape root +def test_bad_sym_escape(tmpdir): + cas_dir = os.path.join(str(tmpdir), 'cas') + cas_cache = CASCache(cas_dir) + d = CasBasedDirectory(cas_cache) + + test_dir = os.path.join(str(tmpdir), 'importfrom') + filesys_discription = [ + ('jail', 'D', ''), + ('jail/a', 'D', ''), + ('jail/a/l', 'S', '../../target'), + ('target', 'D', ''), + ('target/file', 'F', 'two step file') + ] + generate_import_root(test_dir, filesys_discription) + d.import_files(os.path.join(test_dir, 'jail')) + + with pytest.raises(VirtualDirectoryError) as error: + d.descend('a', 'l', follow_symlinks=True) + assert error.reason == "directory-not-found" diff --git a/tests/sourcecache/push.py b/tests/sourcecache/push.py index b0fae616e..ad9653f9d 100644 --- a/tests/sourcecache/push.py +++ b/tests/sourcecache/push.py @@ -28,7 +28,7 @@ from buildstream._project import Project from buildstream import _yaml from buildstream.testing import cli # pylint: disable=unused-import from buildstream.testing import create_repo - +from buildstream.testing._utils.site import HAVE_SANDBOX from tests.testutils import create_artifact_share, dummy_context DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project") @@ -182,6 +182,7 @@ def test_push_fail(cli, tmpdir, datafiles): assert "Pushed" not in res.stderr +@pytest.mark.xfail(HAVE_SANDBOX == 'buildbox', reason='Not working with BuildBox', strict=True) @pytest.mark.datafiles(DATA_DIR) def test_source_push_build_fail(cli, tmpdir, datafiles): project_dir = str(datafiles) |