blob: 78dad8747ed69c8b2f24aa9cfd948851ac73e053 [file] [log] [blame]
# 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.testing import cli, cli_integration # pylint: disable=unused-import
from buildstream._exceptions import ErrorDomain
from buildstream.testing._utils.site import HAVE_SANDBOX
from tests.testutils import create_artifact_share
pytestmark = pytest.mark.integration
DATA_DIR = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"project"
)
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox')
def test_buildtree_staged(cli_integration, datafiles):
# We can only test the non interacitve case
# The non interactive case defaults to not using buildtrees
# for `bst shell --build`
project = str(datafiles)
element_name = 'build-shell/buildtree.bst'
res = cli_integration.run(project=project, args=['--cache-buildtrees', 'always', 'build', element_name])
res.assert_success()
res = cli_integration.run(project=project, args=[
'shell', '--build', element_name, '--', 'cat', 'test'
])
res.assert_shell_error()
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox')
def test_buildtree_staged_forced_true(cli_integration, datafiles):
# Test that if we ask for a build tree it is there.
project = str(datafiles)
element_name = 'build-shell/buildtree.bst'
res = cli_integration.run(project=project, args=['--cache-buildtrees', 'always', 'build', element_name])
res.assert_success()
res = cli_integration.run(project=project, args=[
'shell', '--build', '--use-buildtree', 'always', element_name, '--', 'cat', 'test'
])
res.assert_success()
assert 'Hi' in res.output
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox')
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'
# Switch to a temp artifact cache dir to ensure the artifact is rebuilt,
# caching an empty buildtree
cli_integration.configure({
'cachedir': str(tmpdir)
})
res = cli_integration.run(project=project, args=['build', element_name])
res.assert_success()
res = cli_integration.run(project=project, args=[
'shell', '--build', '--use-buildtree', 'always', element_name, '--', 'cat', 'test'
])
res.assert_main_error(ErrorDomain.APP, None)
assert "Artifact was created without buildtree" in res.stderr
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox')
def test_buildtree_staged_if_available(cli_integration, datafiles):
# Test that a build tree can be correctly detected.
project = str(datafiles)
element_name = 'build-shell/buildtree.bst'
res = cli_integration.run(project=project, args=['--cache-buildtrees', 'always', 'build', element_name])
res.assert_success()
res = cli_integration.run(project=project, args=[
'shell', '--build', '--use-buildtree', 'try', element_name, '--', 'cat', 'test'
])
res.assert_success()
assert 'Hi' in res.output
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox')
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)
element_name = 'build-shell/buildtree.bst'
res = cli_integration.run(project=project, args=['--cache-buildtrees', 'always', 'build', element_name])
res.assert_success()
res = cli_integration.run(project=project, args=[
'shell', '--build', '--use-buildtree', 'never', element_name, '--', 'cat', 'test'
])
res.assert_shell_error()
assert 'Hi' not in res.output
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox')
def test_buildtree_from_failure(cli_integration, datafiles):
# Test that we can use a build tree after a failure
project = str(datafiles)
element_name = 'build-shell/buildtree-fail.bst'
res = cli_integration.run(project=project, args=['build', element_name])
res.assert_main_error(ErrorDomain.STREAM, None)
# Assert that file has expected contents
res = cli_integration.run(project=project, args=[
'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
])
res.assert_success()
assert "WARNING: using a buildtree from a failed build" in res.stderr
assert 'Hi' in res.output
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox')
def test_buildtree_from_failure_option_never(cli_integration, tmpdir, datafiles):
project = str(datafiles)
element_name = 'build-shell/buildtree-fail.bst'
# Switch to a temp artifact cache dir to ensure the artifact is rebuilt,
# caching an empty buildtree
cli_integration.configure({
'cachedir': str(tmpdir)
})
res = cli_integration.run(project=project, args=['--cache-buildtrees', 'never', 'build', element_name])
res.assert_main_error(ErrorDomain.STREAM, None)
res = cli_integration.run(project=project, args=[
'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
])
res.assert_main_error(ErrorDomain.APP, None)
assert "Artifact was created without buildtree" in res.stderr
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason='Only available with a functioning sandbox')
def test_buildtree_from_failure_option_always(cli_integration, tmpdir, datafiles):
project = str(datafiles)
element_name = 'build-shell/buildtree-fail.bst'
# build with --cache-buildtrees set to 'always', behaviour should match
# default behaviour (which is always) as the buildtree will explicitly have been
# cached with content.
cli_integration.configure({
'cachedir': str(tmpdir)
})
res = cli_integration.run(project=project, args=['--cache-buildtrees', 'always', 'build', element_name])
res.assert_main_error(ErrorDomain.STREAM, None)
res = cli_integration.run(project=project, args=[
'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
])
res.assert_success()
assert "WARNING: using a buildtree from a failed build" in res.stderr
assert 'Hi' in res.output
# Check that build shells work when pulled from a remote cache
# 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')
def test_buildtree_pulled(cli, tmpdir, datafiles):
project = str(datafiles)
element_name = 'build-shell/buildtree.bst'
with create_artifact_share(os.path.join(str(tmpdir), 'artifactshare')) as share:
# Build the element to push it to cache
cli.configure({
'artifacts': {'url': share.repo, 'push': True}
})
result = cli.run(project=project, args=['--cache-buildtrees', 'always', 'build', element_name])
result.assert_success()
assert cli.get_element_state(project, element_name) == 'cached'
# Discard the cache
shutil.rmtree(str(os.path.join(str(tmpdir), 'cache', 'cas')))
shutil.rmtree(str(os.path.join(str(tmpdir), 'cache', 'artifacts')))
assert cli.get_element_state(project, element_name) != 'cached'
# Pull from cache, ensuring cli options is set to pull the buildtree
result = cli.run(project=project,
args=['--pull-buildtrees', 'artifact', 'pull', '--deps', 'all', element_name])
result.assert_success()
# Check it's using the cached build tree
res = cli.run(project=project, args=[
'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
])
res.assert_success()
# 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')
def test_buildtree_options(cli, tmpdir, datafiles):
project = str(datafiles)
element_name = 'build-shell/buildtree.bst'
with create_artifact_share(os.path.join(str(tmpdir), 'artifactshare')) as share:
# Build the element to push it to cache
cli.configure({
'artifacts': {'url': share.repo, 'push': True}
})
result = cli.run(project=project, args=['--cache-buildtrees', 'always', 'build', element_name])
result.assert_success()
assert cli.get_element_state(project, element_name) == 'cached'
assert share.has_artifact(cli.get_artifact_name(project, 'test', element_name))
# Discard the cache
shutil.rmtree(str(os.path.join(str(tmpdir), 'cache', 'cas')))
shutil.rmtree(str(os.path.join(str(tmpdir), 'cache', 'artifacts')))
assert cli.get_element_state(project, element_name) != 'cached'
# Pull from cache, but do not include buildtrees.
result = cli.run(project=project, args=['artifact', 'pull', '--deps', 'all', element_name])
result.assert_success()
# Check it's not using the cached build tree
res = cli.run(project=project, args=[
'shell', '--build', element_name, '--use-buildtree', 'never', '--', 'cat', 'test'
])
res.assert_shell_error()
assert 'Hi' not in res.output
# Check it's not using the cached build tree, default is to ask, and fall back to not
# for non interactive behavior
res = cli.run(project=project, args=[
'shell', '--build', element_name, '--', 'cat', 'test'
])
res.assert_shell_error()
assert 'Hi' not in res.output
# Check correctly handling the lack of buildtree, with 'try' not attempting to
# pull the buildtree as the user context is by default set to not pull them
res = cli.run(project=project, args=[
'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
])
assert 'Hi' not in res.output
assert 'Attempting to fetch missing artifact buildtrees' not in res.stderr
assert """Buildtree is not cached locally or in available remotes,
shell will be loaded without it"""
# Check correctly handling the lack of buildtree, with 'try' attempting and succeeding
# to pull the buildtree as the user context allow the pulling of buildtrees and it is
# available in the remote
res = cli.run(project=project, args=[
'--pull-buildtrees', 'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
])
assert 'Attempting to fetch missing artifact buildtree' in res.stderr
assert 'Hi' in res.output
shutil.rmtree(os.path.join(os.path.join(str(tmpdir), 'cache', 'cas')))
shutil.rmtree(os.path.join(os.path.join(str(tmpdir), 'cache', 'artifacts')))
assert cli.get_element_state(project, element_name) != 'cached'
# Check it's not loading the shell at all with always set for the buildtree, when the
# user context does not allow for buildtree pulling
result = cli.run(project=project, args=['artifact', 'pull', '--deps', 'all', element_name])
result.assert_success()
res = cli.run(project=project, args=[
'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
])
res.assert_main_error(ErrorDomain.PROG_NOT_FOUND, None)
assert 'Buildtree is not cached locally or in available remotes' in res.stderr
assert 'Hi' not in res.output
assert 'Attempting to fetch missing artifact buildtree' not in res.stderr
# Check that when user context is set to pull buildtrees and a remote has the buildtree,
# 'always' will attempt and succeed at pulling the missing buildtree.
res = cli.run(project=project, args=[
'--pull-buildtrees', 'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
])
assert 'Hi' in res.output
assert 'Attempting to fetch missing artifact buildtree' in res.stderr