diff options
Diffstat (limited to 'tests/frontend/artifact_delete.py')
-rw-r--r-- | tests/frontend/artifact_delete.py | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/tests/frontend/artifact_delete.py b/tests/frontend/artifact_delete.py new file mode 100644 index 000000000..80870c81a --- /dev/null +++ b/tests/frontend/artifact_delete.py @@ -0,0 +1,265 @@ +# +# Copyright (C) 2019 Codethink Limited +# +# 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/>. +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import pytest + +from buildstream.element import _get_normal_name +from buildstream._exceptions import ErrorDomain +from buildstream.testing import cli # pylint: disable=unused-import +from tests.testutils import create_artifact_share + + +# Project directory +DATA_DIR = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "project", +) + + +# Test that we can delete the artifact of the element which corresponds +# to the current project state +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_element(cli, tmpdir, datafiles): + project = str(datafiles) + element = 'target.bst' + + # Build the element and ensure it's cached + result = cli.run(project=project, args=['build', element]) + result.assert_success() + assert cli.get_element_state(project, element) == 'cached' + + result = cli.run(project=project, args=['artifact', 'delete', element]) + result.assert_success() + assert cli.get_element_state(project, element) != 'cached' + + +# Test that we can delete an artifact by specifying its ref. +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_artifact(cli, tmpdir, datafiles): + project = str(datafiles) + element = 'target.bst' + + # Configure a local cache + local_cache = os.path.join(str(tmpdir), 'cache') + cli.configure({'cachedir': local_cache}) + + # First build an element so that we can find its artifact + result = cli.run(project=project, args=['build', element]) + result.assert_success() + + # Obtain the artifact ref + cache_key = cli.get_element_key(project, element) + artifact = os.path.join('test', os.path.splitext(element)[0], cache_key) + + # Explicitly check that the ARTIFACT exists in the cache + assert os.path.exists(os.path.join(local_cache, 'artifacts', 'refs', artifact)) + + # Delete the artifact + result = cli.run(project=project, args=['artifact', 'delete', artifact]) + result.assert_success() + + # Check that the ARTIFACT is no longer in the cache + assert not os.path.exists(os.path.join(local_cache, 'cas', 'refs', 'heads', artifact)) + + +# Test the `bst artifact delete` command with multiple, different arguments. +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_element_and_artifact(cli, tmpdir, datafiles): + project = str(datafiles) + element = 'target.bst' + dep = 'compose-all.bst' + + # Configure a local cache + local_cache = os.path.join(str(tmpdir), 'cache') + cli.configure({'cachedir': local_cache}) + + # First build an element so that we can find its artifact + result = cli.run(project=project, args=['build', element]) + result.assert_success() + assert cli.get_element_states(project, [element, dep], deps="none") == { + element: "cached", + dep: "cached", + } + + # Obtain the artifact ref + cache_key = cli.get_element_key(project, element) + artifact = os.path.join('test', os.path.splitext(element)[0], cache_key) + + # Explicitly check that the ARTIFACT exists in the cache + assert os.path.exists(os.path.join(local_cache, 'artifacts', 'refs', artifact)) + + # Delete the artifact + result = cli.run(project=project, args=['artifact', 'delete', artifact, dep]) + result.assert_success() + + # Check that the ARTIFACT is no longer in the cache + assert not os.path.exists(os.path.join(local_cache, 'artifacts', artifact)) + + # Check that the dependency ELEMENT is no longer cached + assert cli.get_element_state(project, dep) != 'cached' + + +# Test that we receive the appropriate stderr when we try to delete an artifact +# that is not present in the cache. +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_unbuilt_artifact(cli, tmpdir, datafiles): + project = str(datafiles) + element = 'target.bst' + + # delete it, just in case it's there + _ = cli.run(project=project, args=['artifact', 'delete', element]) + + # Ensure the element is not cached + assert cli.get_element_state(project, element) != 'cached' + + # Now try and remove it again (now we know its not there) + result = cli.run(project=project, args=['artifact', 'delete', element]) + + cache_key = cli.get_element_key(project, element) + artifact = os.path.join('test', os.path.splitext(element)[0], cache_key) + expected_err = "WARNING Could not find ref '{}'".format(artifact) + assert expected_err in result.stderr + + +# Test that an artifact pulled from it's remote cache (without it's buildtree) will not +# throw an Exception when trying to prune the cache. +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_pulled_artifact_without_buildtree(cli, tmpdir, datafiles): + project = str(datafiles) + element = 'target.bst' + + # Set up remote and local shares + local_cache = os.path.join(str(tmpdir), 'artifacts') + with create_artifact_share(os.path.join(str(tmpdir), 'remote')) as remote: + cli.configure({ + 'artifacts': {'url': remote.repo, 'push': True}, + 'cachedir': local_cache, + }) + + # Build the element + result = cli.run(project=project, args=['build', element]) + result.assert_success() + + # Make sure it's in the share + assert remote.has_artifact(cli.get_artifact_name(project, 'test', element)) + + # Delete and then pull the artifact (without its buildtree) + result = cli.run(project=project, args=['artifact', 'delete', element]) + result.assert_success() + assert cli.get_element_state(project, element) != 'cached' + result = cli.run(project=project, args=['artifact', 'pull', element]) + result.assert_success() + assert cli.get_element_state(project, element) == 'cached' + + # Now delete it again (it should have been pulled without the buildtree, but + # a digest of the buildtree is pointed to in the artifact's metadata + result = cli.run(project=project, args=['artifact', 'delete', element]) + result.assert_success() + assert cli.get_element_state(project, element) != 'cached' + + +# Test that we can delete the build deps of an element +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_elements_build_deps(cli, tmpdir, datafiles): + project = str(datafiles) + element = 'target.bst' + + # Build the element and ensure it's cached + result = cli.run(project=project, args=['build', element]) + result.assert_success() + + # Assert element and build deps are cached + assert cli.get_element_state(project, element) == 'cached' + bdep_states = cli.get_element_states(project, [element], deps='build') + for state in bdep_states.values(): + assert state == 'cached' + + result = cli.run(project=project, args=['artifact', 'delete', '--deps', 'build', element]) + result.assert_success() + + # Assert that the build deps have been deleted and that the artifact remains cached + assert cli.get_element_state(project, element) == 'cached' + bdep_states = cli.get_element_states(project, [element], deps='build') + for state in bdep_states.values(): + assert state != 'cached' + + +# Test that we can delete the build deps of an artifact by providing an artifact ref +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_artifacts_build_deps(cli, tmpdir, datafiles): + project = str(datafiles) + element = 'target.bst' + + # Configure a local cache + local_cache = os.path.join(str(tmpdir), 'cache') + cli.configure({'cachedir': local_cache}) + + # First build an element so that we can find its artifact + result = cli.run(project=project, args=['build', element]) + result.assert_success() + + # Obtain the artifact ref + cache_key = cli.get_element_key(project, element) + artifact = os.path.join('test', os.path.splitext(element)[0], cache_key) + + # Explicitly check that the ARTIFACT exists in the cache + assert os.path.exists(os.path.join(local_cache, 'artifacts', 'refs', artifact)) + + # get the artifact refs of the build dependencies + bdep_refs = [] + bdep_states = cli.get_element_states(project, [element], deps='build') + for bdep in bdep_states.keys(): + bdep_refs.append(os.path.join('test', _get_normal_name(bdep), cli.get_element_key(project, bdep))) + + # Assert build dependencies are cached + for ref in bdep_refs: + assert os.path.exists(os.path.join(local_cache, 'artifacts', 'refs', ref)) + + # Delete the artifact + result = cli.run(project=project, args=['artifact', 'delete', '--deps', 'build', artifact]) + result.assert_success() + + # Check that the artifact's build deps are no longer in the cache + # Assert build dependencies have been deleted and that the artifact remains + for ref in bdep_refs: + assert not os.path.exists(os.path.join(local_cache, 'artifacts', 'refs', ref)) + assert os.path.exists(os.path.join(local_cache, 'artifacts', 'refs', artifact)) + + +# Test that `--deps all` option fails if an artifact ref is specified +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_artifact_with_deps_all_fails(cli, tmpdir, datafiles): + project = str(datafiles) + element = 'target.bst' + + # First build an element so that we can find its artifact + result = cli.run(project=project, args=['build', element]) + result.assert_success() + + # Obtain the artifact ref + cache_key = cli.get_element_key(project, element) + artifact = os.path.join('test', os.path.splitext(element)[0], cache_key) + + # Try to delete the artifact with all of its dependencies + result = cli.run(project=project, args=['artifact', 'delete', '--deps', 'all', artifact]) + result.assert_main_error(ErrorDomain.STREAM, None) + + assert "Error: '--deps all' is not supported for artifact refs" in result.stderr |