diff options
-rw-r--r-- | tests/integration/shellbuildtrees.py | 304 |
1 files changed, 125 insertions, 179 deletions
diff --git a/tests/integration/shellbuildtrees.py b/tests/integration/shellbuildtrees.py index 5164c0209..082e7e679 100644 --- a/tests/integration/shellbuildtrees.py +++ b/tests/integration/shellbuildtrees.py @@ -19,9 +19,12 @@ pytestmark = pytest.mark.integration DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project") +# +# Ensure that we didn't get a build tree if we didn't ask for one +# @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): +def test_buildtree_unused(cli_integration, datafiles): # We can only test the non interacitve case # The non interactive case defaults to not using buildtrees # for `bst shell --build` @@ -35,9 +38,12 @@ def test_buildtree_staged(cli_integration, datafiles): res.assert_shell_error() +# +# Ensure we can use a buildtree from a successful build +# @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): +def test_buildtree_from_success(cli_integration, datafiles): # Test that if we ask for a build tree it is there. project = str(datafiles) element_name = "build-shell/buildtree.bst" @@ -52,27 +58,9 @@ def test_buildtree_staged_forced_true(cli_integration, datafiles): 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, - # without caching a buildtree which is the default bst behaviour - 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", element_name, "--", "cat", "test"] - ) - res.assert_main_error(ErrorDomain.APP, None) - assert "Error launching shell: Artifact was created without buildtree" in res.stderr - - +# +# Ensure we can use a buildtree from a failed build +# @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): @@ -92,198 +80,151 @@ def test_buildtree_from_failure(cli_integration, datafiles): assert "Hi" in res.output +# +# Test behavior of launching a shell and requesting to use a buildtree, with +# various states of local cache (ranging from nothing cached to everything cached) +# @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, - # without caching a buildtree explicitly - 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", "--", "cat", "test"] - ) - res.assert_main_error(ErrorDomain.APP, None) - assert "Error launching shell: 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", "--", "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): +@pytest.mark.parametrize( + "pull,pull_deps,pull_buildtree,cache_buildtree", + [ + # Don't pull at all + (False, "build", False, False), + # Pull only dependencies + (True, "build", False, True), + # Pull all elements including the shell element, but without the buildtree + (True, "all", False, True), + # Pull all elements including the shell element, and pull buildtrees + (True, "all", True, True), + # Pull all elements including the shell element, and pull buildtrees, but buildtree was never cached + (True, "all", True, False), + ], + ids=["no-pull", "pull-only-deps", "pull-without-buildtree", "pull-with-buildtree", "created-without-buildtree"], +) +def test_shell_use_pulled_buildtree(cli, tmpdir, datafiles, pull, pull_deps, pull_buildtree, cache_buildtree): 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] - ) + # Build it, optionally caching the build tree + args = [] + if cache_buildtree: + args += ["--cache-buildtrees", "always"] + args += ["--on-error", "continue", "build", element_name] + result = cli.run(project=project, args=args) result.assert_success() - # Check it's using the cached build tree - res = cli.run(project=project, args=["shell", "--build", element_name, "--use-buildtree", "--", "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.get_artifact(cli.get_artifact_name(project, "test", element_name)) - # Discard the cache + # Discard the local 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() + # Optionally pull the buildtree along with `bst artifact pull` + if pull: + args = [] + if pull_buildtree: + args += ["--pull-buildtrees"] + args += ["artifact", "pull", "--deps", pull_deps, element_name] - # Check it's not using the cached build tree - res = cli.run(project=project, args=["shell", "--build", element_name, "--", "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 '--use-buildtree' attempting and succeeding - # to pull the buildtree as the user context allow the pulling of buildtrees and it is - # available in the remote and --pull given - res = cli.run( - project=project, - args=[ - "--pull-buildtrees", - "shell", - "--build", - element_name, - "--pull", - "--use-buildtree", - "--", - "cat", - "test", - ], - ) - 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" + # Pull from cache + result = cli.run(project=project, args=args) + result.assert_success() - # Check it's not loading the shell at all with `--use-buildtree`, when the - # user context does not allow for buildtree pulling and --pull is not given - 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", "--", "cat", "test"]) - res.assert_main_error(ErrorDomain.APP, None) - assert "Buildtree is not cached locally" in res.stderr - assert "Hi" not in res.output - - # Check that when user context is set to pull buildtrees and a remote has the buildtree, - # '--use-buildtree' will attempt and succeed at pulling the missing buildtree with --pull set. - res = cli.run( - project=project, - args=[ - "--pull-buildtrees", - "shell", - "--build", - element_name, - "--pull", - "--use-buildtree", - "--", - "cat", - "test", - ], + # Run the shell without asking it to pull any buildtree, just asking to use a buildtree + result = cli.run( + project=project, args=["shell", "--build", element_name, "--use-buildtree", "--", "cat", "test"] ) - assert "Hi" in res.output - assert res.get_pulled_elements() == [element_name] - -# Tests running pull and pull-buildtree options at the same time. + # If we did pull the buildtree, expect success, otherwise fail + if pull: + if pull_deps == "all": + if pull_buildtree: + + if cache_buildtree: + result.assert_success() + assert "Hi" in result.output + else: + # Sorry, a buildtree was never cached for this element + result.assert_main_error( + ErrorDomain.APP, "missing-buildtree-artifact-created-without-buildtree" + ) + else: + # We just didn't pull the buildtree + result.assert_main_error(ErrorDomain.APP, "missing-buildtree-artifact-buildtree-not-cached") + else: + # The artifact we're shelling into is missing + result.assert_main_error(ErrorDomain.APP, "missing-buildtree-artifact-not-cached") + else: + # The dependencies are missing, cannot stage anything even + result.assert_main_error(ErrorDomain.APP, "shell-missing-deps") + + +# +# Test behavior of launching a shell and requesting to use and pull a buildtree, with +# various states of local cache (ranging from nothing cached to everything cached) +# @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") -def test_pull_buildtree_pulled(cli, tmpdir, datafiles): +@pytest.mark.parametrize( + "pull,pull_deps,pull_buildtree,cache_buildtree", + [ + # Don't pull at all + (False, "build", False, True), + # Pull only dependencies + (True, "build", False, True), + # Pull all elements including the shell element, but without the buildtree + (True, "all", False, True), + # Pull all elements including the shell element, and pull buildtrees + (True, "all", True, True), + # Pull all elements including the shell element, and pull buildtrees, but buildtree was never cached + (True, "all", True, False), + ], + ids=["no-pull", "pull-only-deps", "pull-without-buildtree", "pull-with-buildtree", "created-without-buildtree"], +) +def test_shell_pull_buildtree(cli, tmpdir, datafiles, pull, pull_deps, pull_buildtree, cache_buildtree): 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]) + + # Build it, optionally caching the build tree + args = [] + if cache_buildtree: + args += ["--cache-buildtrees", "always"] + args += ["build", element_name] + result = cli.run(project=project, args=args) result.assert_success() + assert cli.get_element_state(project, element_name) == "cached" + assert share.get_artifact(cli.get_artifact_name(project, "test", element_name)) - # Discard the cache + # Discard the local 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" - # Check it's not using the cached build tree, because --pull - # and pull-buildtrees were not both set - res = cli.run( - project=project, - args=["shell", "--build", element_name, "--pull", "--use-buildtree", "--", "cat", "test",], - ) - res.assert_main_error(ErrorDomain.APP, None) - assert "Buildtree is not cached locally" in res.stderr + # Optionally pull the buildtree along with `bst artifact pull` + if pull: + args = [] + if pull_buildtree: + args += ["--pull-buildtrees"] + args += ["artifact", "pull", "--deps", pull_deps, element_name] + + # Pull from cache + result = cli.run(project=project, args=args) + result.assert_success() - # Check it's using the cached build tree, because --pull - # and pull-buildtrees were both set - res = cli.run( + # Run the shell without asking it to pull any buildtree, just asking to use a buildtree + result = cli.run( project=project, args=[ "--pull-buildtrees", @@ -297,5 +238,10 @@ def test_pull_buildtree_pulled(cli, tmpdir, datafiles): "test", ], ) - result.assert_success() - assert "Hi" in res.output + + if cache_buildtree: + result.assert_success() + assert "Hi" in result.output + else: + # Sorry, a buildtree was never cached for this element + result.assert_main_error(ErrorDomain.APP, "missing-buildtree-artifact-created-without-buildtree") |