summaryrefslogtreecommitdiff
path: root/tests/integration/workspace.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integration/workspace.py')
-rw-r--r--tests/integration/workspace.py157
1 files changed, 153 insertions, 4 deletions
diff --git a/tests/integration/workspace.py b/tests/integration/workspace.py
index 7e84b690b..a2ea4841a 100644
--- a/tests/integration/workspace.py
+++ b/tests/integration/workspace.py
@@ -8,6 +8,9 @@ from buildstream import _yaml
from buildstream.testing import cli_integration as cli # pylint: disable=unused-import
from buildstream.testing._utils.site import HAVE_SANDBOX
from buildstream.exceptions import ErrorDomain
+from buildstream.utils import BST_ARBITRARY_TIMESTAMP
+
+from tests.testutils import wait_for_cache_granularity
pytestmark = pytest.mark.integration
@@ -62,7 +65,6 @@ def test_workspace_mount_on_read_only_directory(cli, datafiles):
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox")
-@pytest.mark.xfail(reason="Incremental builds are currently incompatible with workspace source plugin.")
def test_workspace_commanddir(cli, datafiles):
project = str(datafiles)
workspace = os.path.join(cli.directory, "workspace")
@@ -74,8 +76,16 @@ def test_workspace_commanddir(cli, datafiles):
res = cli.run(project=project, args=["build", element_name])
assert res.exit_code == 0
- assert os.path.exists(os.path.join(cli.directory, "workspace"))
- assert os.path.exists(os.path.join(cli.directory, "workspace", "build"))
+ # Check that the object file was created in the command-subdir `build`
+ # using the cached buildtree.
+ res = cli.run(
+ project=project,
+ args=["shell", "--build", element_name, "--use-buildtree", "always", "--", "find", "..", "-mindepth", "1",],
+ )
+ res.assert_success()
+
+ files = res.output.splitlines()
+ assert "../build/hello.o" in files
@pytest.mark.datafiles(DATA_DIR)
@@ -274,7 +284,7 @@ def test_incremental_configure_commands_run_only_once(cli, datafiles):
# Then we build, and check whether the configure step succeeded
res = cli.run(project=project, args=["--cache-buildtrees", "always", "build", element_name])
res.assert_success()
- # check that the workspace was not configured
+ # check that the workspace was not configured outside the sandbox
assert not os.path.exists(os.path.join(workspace, "prepared"))
# the configure should have been run in the sandbox, so check the buildtree
@@ -288,6 +298,10 @@ def test_incremental_configure_commands_run_only_once(cli, datafiles):
assert "./prepared" in files
assert not "./prepared-again" in files
+ # Add file to workspace to trigger an (incremental) build
+ with open(os.path.join(workspace, "newfile"), "w"):
+ pass
+
# When we build again, the configure commands should not be
# called, and we should therefore exit cleanly (the configure
# commands are set to always fail after the first run)
@@ -365,3 +379,138 @@ def test_workspace_failed_logs(cli, datafiles):
fail_str = "FAILURE {}: Running build-commands".format(element_name)
batch_fail_str = "FAILURE {}: Running commands".format(element_name)
assert fail_str in log or batch_fail_str in log
+
+
+def get_buildtree_file_contents(cli, project, element_name, filename):
+ res = cli.run(
+ project=project, args=["shell", "--build", element_name, "--use-buildtree", "always", "--", "cat", filename,],
+ )
+ res.assert_success()
+ return res.output
+
+
+@pytest.mark.datafiles(DATA_DIR)
+@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox")
+def test_incremental(cli, datafiles):
+ project = str(datafiles)
+ workspace = os.path.join(cli.directory, "workspace")
+ element_path = os.path.join(project, "elements")
+ element_name = "workspace/incremental.bst"
+
+ element = {
+ "kind": "manual",
+ "depends": [{"filename": "base.bst", "type": "build"}],
+ "sources": [{"kind": "local", "path": "files/workspace-incremental"}],
+ "config": {"build-commands": ["make"]},
+ }
+ _yaml.roundtrip_dump(element, os.path.join(element_path, element_name))
+
+ # We open a workspace on the above element
+ res = cli.run(project=project, args=["workspace", "open", "--directory", workspace, element_name])
+ res.assert_success()
+
+ # Initial (non-incremental) build of the workspace
+ res = cli.run(project=project, args=["build", element_name])
+ res.assert_success()
+
+ # Save the random hash
+ random_hash = get_buildtree_file_contents(cli, project, element_name, "random")
+
+ # Verify the expected output file of the initial build
+ assert get_buildtree_file_contents(cli, project, element_name, "copy") == "1"
+
+ wait_for_cache_granularity()
+
+ # Replace source file contents with '2'
+ with open(os.path.join(workspace, "source"), "w") as f:
+ f.write("2")
+
+ # Perform incremental build of the workspace
+ res = cli.run(project=project, args=["build", element_name])
+ res.assert_success()
+
+ # Verify that this was an incremental build by comparing the random hash
+ assert get_buildtree_file_contents(cli, project, element_name, "random") == random_hash
+
+ # Verify that the output file matches the new source file
+ assert get_buildtree_file_contents(cli, project, element_name, "copy") == "2"
+
+ wait_for_cache_granularity()
+
+ # Replace source file contents with '3', however, set an old mtime such
+ # that `make` will not pick up the change
+ with open(os.path.join(workspace, "source"), "w") as f:
+ f.write("3")
+ os.utime(os.path.join(workspace, "source"), (BST_ARBITRARY_TIMESTAMP, BST_ARBITRARY_TIMESTAMP))
+
+ # Perform incremental build of the workspace
+ res = cli.run(project=project, args=["build", element_name])
+ res.assert_success()
+
+ # Verify that this was an incremental build by comparing the random hash
+ assert get_buildtree_file_contents(cli, project, element_name, "random") == random_hash
+
+ # Verify that the output file still matches the previous content '2'
+ assert get_buildtree_file_contents(cli, project, element_name, "copy") == "2"
+
+
+# Test incremental build after partial build / build failure
+@pytest.mark.datafiles(DATA_DIR)
+@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox")
+def test_incremental_partial(cli, datafiles):
+ project = str(datafiles)
+ workspace = os.path.join(cli.directory, "workspace")
+ element_path = os.path.join(project, "elements")
+ element_name = "workspace/incremental.bst"
+
+ element = {
+ "kind": "manual",
+ "depends": [{"filename": "base.bst", "type": "build"}],
+ "sources": [{"kind": "local", "path": "files/workspace-partial"}],
+ "config": {"build-commands": ["make random", "make copy1", "make copy2"]},
+ }
+ _yaml.roundtrip_dump(element, os.path.join(element_path, element_name))
+
+ # We open a workspace on the above element
+ res = cli.run(project=project, args=["workspace", "open", "--directory", workspace, element_name])
+ res.assert_success()
+
+ # Initial (non-incremental) build of the workspace
+ res = cli.run(project=project, args=["build", element_name])
+ res.assert_success()
+
+ # Save the random hash
+ random_hash = get_buildtree_file_contents(cli, project, element_name, "random")
+
+ # Verify the expected output files of the initial build
+ assert get_buildtree_file_contents(cli, project, element_name, "copy1") == "1"
+ assert get_buildtree_file_contents(cli, project, element_name, "copy2") == "1"
+
+ wait_for_cache_granularity()
+
+ # Delete source1 and replace source2 file contents with '2'
+ os.unlink(os.path.join(workspace, "source1"))
+ with open(os.path.join(workspace, "source2"), "w") as f:
+ f.write("2")
+
+ # Perform incremental build of the workspace
+ # This should fail because of the missing source1 file.
+ res = cli.run(project=project, args=["build", element_name])
+ res.assert_main_error(ErrorDomain.STREAM, None)
+
+ wait_for_cache_granularity()
+
+ # Recreate source1 file
+ with open(os.path.join(workspace, "source1"), "w") as f:
+ f.write("2")
+
+ # Perform incremental build of the workspace
+ res = cli.run(project=project, args=["build", element_name])
+ res.assert_success()
+
+ # Verify that this was an incremental build by comparing the random hash
+ assert get_buildtree_file_contents(cli, project, element_name, "random") == random_hash
+
+ # Verify that both files got rebuilt
+ assert get_buildtree_file_contents(cli, project, element_name, "copy1") == "2"
+ assert get_buildtree_file_contents(cli, project, element_name, "copy2") == "2"