diff --git a/tests/integration/base/generate-debian-base.sh b/tests/integration/base/generate-debian-base.sh new file mode 100755 index 000000000..cafda21d7 --- /dev/null +++ b/tests/integration/base/generate-debian-base.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generate a base sysroot for running the BuildStream integration tests. +# +# The sysroot is based off the Debian Linux distribution. + +set -eux + +DOCKER_ARCH=${DOCKER_ARCH:-amd64} + +export DOCKER_DEFAULT_PLATFORM="linux/%{DOCKER_ARCH}" + +IMAGE_NAME="integration-tests-debian-base" + +docker build --tag ${IMAGE_NAME} -f - << "EOF" +FROM debian:trixie-slim +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc libc6-dev make autoconf automake && \ + rm -rf /var/lib/apt/lists/* +EOF + +CONTAINER_NAME="$(docker create ${IMAGE_NAME})" + +docker export "${CONTAINER_NAME}" | xz > ${IMAGE_NAME}.tar.xz + +docker rm "${CONTAINER_NAME}" diff --git a/tests/integration/project/elements/base/base-alpine.bst b/tests/integration/project/elements/base/base-alpine.bst index c5833095d..4695293c4 100644 --- a/tests/integration/project/elements/base/base-alpine.bst +++ b/tests/integration/project/elements/base/base-alpine.bst @@ -11,7 +11,7 @@ sources: (?): - arch == "x86-64": ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 - url: "alpine:integration-tests-base.v1.x86_64.tar.xz" + url: "test-images:integration-tests-base.v1.x86_64.tar.xz" - arch == "aarch64": ref: 431fb5362032ede6f172e70a3258354a8fd71fcbdeb1edebc0e20968c792329a - url: "alpine:integration-tests-base.v1.aarch64.tar.xz" + url: "test-images:integration-tests-base.v1.aarch64.tar.xz" diff --git a/tests/integration/project/elements/base/base-debian.bst b/tests/integration/project/elements/base/base-debian.bst new file mode 100644 index 000000000..02c2c8ebf --- /dev/null +++ b/tests/integration/project/elements/base/base-debian.bst @@ -0,0 +1,12 @@ +kind: import + +description: | + Debian Linux base for tests + + Generated using the `tests/integration/base/generate-debian-base.sh` script. + +sources: + - kind: tar + base-dir: '' + ref: 4964ce8b12f2200a3cf1570ac93756bea27055dad771bf2be97751c7a2a0f92c + url: test-images:integration-tests-debian-base.v1.x86_64.tar.xz diff --git a/tests/integration/project/elements/recc/cacheonly.bst b/tests/integration/project/elements/recc/cacheonly.bst new file mode 100644 index 000000000..5de70248c --- /dev/null +++ b/tests/integration/project/elements/recc/cacheonly.bst @@ -0,0 +1,29 @@ +kind: autotools +description: recc test with autotools + +build-depends: +- filename: base/base-debian.bst + config: + digest-environment: RECC_REMOTE_PLATFORM_chrootRootDigest +- recc/recc.bst + +sources: +- kind: tar + url: project_dir:/files/amhello.tar.gz + ref: 534a884bc1974ffc539a9c215e35c4217b6f666a134cd729e786b9c84af99650 + +sandbox: + remote-apis-socket: + path: /tmp/casd.sock + action-cache-enable-update: true + +environment: + CC: recc gcc + RECC_LOG_LEVEL: debug + RECC_LOG_DIRECTORY: .recc-log + RECC_DEPS_GLOBAL_PATHS: 1 + RECC_NO_PATH_REWRITE: 1 + RECC_LINK: 1 + RECC_CACHE_ONLY: 1 + RECC_CACHE_UPLOAD_LOCAL_BUILD: 1 + RECC_SERVER: unix:/tmp/casd.sock diff --git a/tests/integration/project/elements/recc/recc.bst b/tests/integration/project/elements/recc/recc.bst new file mode 100644 index 000000000..c9c203010 --- /dev/null +++ b/tests/integration/project/elements/recc/recc.bst @@ -0,0 +1,8 @@ +kind: import + +sources: + - kind: tar + base-dir: '' + ref: 00795f1781fd5f80757dd0c60f6335c221dacfa852edad8209b390dd9cfb1f83 + url: buildbox:buildbox-integration/-/releases/1.3.31/downloads/recc-x86_64-linux-gnu.tgz + directory: usr/bin diff --git a/tests/integration/project/elements/recc/remoteexecution.bst b/tests/integration/project/elements/recc/remoteexecution.bst new file mode 100644 index 000000000..269895eb2 --- /dev/null +++ b/tests/integration/project/elements/recc/remoteexecution.bst @@ -0,0 +1,26 @@ +kind: autotools +description: recc test with autotools + +build-depends: +- filename: base/base-debian.bst + config: + digest-environment: RECC_REMOTE_PLATFORM_chrootRootDigest +- recc/recc.bst + +sources: +- kind: tar + url: project_dir:/files/amhello.tar.gz + ref: 534a884bc1974ffc539a9c215e35c4217b6f666a134cd729e786b9c84af99650 + +sandbox: + remote-apis-socket: + path: /tmp/casd.sock + +environment: + CC: recc gcc + RECC_LOG_LEVEL: debug + RECC_LOG_DIRECTORY: .recc-log + RECC_DEPS_GLOBAL_PATHS: 1 + RECC_NO_PATH_REWRITE: 1 + RECC_LINK: 1 + RECC_SERVER: unix:/tmp/casd.sock diff --git a/tests/integration/project/project.conf b/tests/integration/project/project.conf index e8f7af487..2d5a36e90 100644 --- a/tests/integration/project/project.conf +++ b/tests/integration/project/project.conf @@ -4,7 +4,8 @@ min-version: 2.0 element-path: elements aliases: - alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ + test-images: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ + buildbox: https://gitlab.com/BuildGrid/buildbox/ project_dir: file://{project_dir} plugins: diff --git a/tests/integration/recc.py b/tests/integration/recc.py new file mode 100644 index 000000000..f1e9e2ba5 --- /dev/null +++ b/tests/integration/recc.py @@ -0,0 +1,255 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +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_SANDBOX + +pytestmark = pytest.mark.integration + + +DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project") + + +@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") +@pytest.mark.datafiles(DATA_DIR) +def test_remote_execution(cli, datafiles): + project = str(datafiles) + checkout1 = os.path.join(cli.directory, "checkout1") + checkout2 = os.path.join(cli.directory, "checkout2") + element_name = "recc/remoteexecution.bst" + + # Always cache buildtrees to be able to check recc logs + result = cli.run(project=project, args=["--cache-buildtrees", "always", "build", element_name]) + if result.exit_code != 0: + # Output recc logs in case of failure + cli.run( + project=project, + args=[ + "shell", + "--build", + "--use-buildtree", + element_name, + "--", + "sh", + "-c", + "cat config.log .recc-log/* */.recc-log/*", + ], + ) + assert result.exit_code == 0 + + result = cli.run(project=project, args=["artifact", "checkout", element_name, "--directory", checkout1]) + assert result.exit_code == 0 + + assert_contains( + checkout1, + [ + "/usr", + "/usr/bin", + "/usr/share", + "/usr/bin/hello", + "/usr/share/doc", + "/usr/share/doc/amhello", + "/usr/share/doc/amhello/README", + ], + ) + + # Check the main build log + result = cli.run(project=project, args=["artifact", "log", element_name]) + assert result.exit_code == 0 + log = result.output + + # Verify we get expected output exactly once + assert log.count("Making all in src") == 1 + + result = cli.run( + project=project, + args=[ + "shell", + "--build", + "--use-buildtree", + element_name, + "--", + "sh", + "-c", + "cat src/.recc-log/recc.buildbox*", + ], + ) + assert result.exit_code == 0 + recc_log = result.output + + # Verify recc is successfully using remote execution for both, compiling and linking + assert recc_log.count("Executing action remotely") == 2 + assert recc_log.count("Remote execution finished with exit code 0") == 2 + + # Delete artifact from BuildStream cache to trigger a BuildStream rebuild with action cache hits for recc + result = cli.run(project=project, args=["artifact", "delete", element_name]) + assert result.exit_code == 0 + + result = cli.run(project=project, args=["--cache-buildtrees", "always", "build", element_name]) + assert result.exit_code == 0 + + result = cli.run(project=project, args=["artifact", "checkout", element_name, "--directory", checkout2]) + assert result.exit_code == 0 + + assert_contains( + checkout2, + [ + "/usr", + "/usr/bin", + "/usr/share", + "/usr/bin/hello", + "/usr/share/doc", + "/usr/share/doc/amhello", + "/usr/share/doc/amhello/README", + ], + ) + + result = cli.run( + project=project, + args=[ + "shell", + "--build", + "--use-buildtree", + element_name, + "--", + "sh", + "-c", + "cat src/.recc-log/recc.buildbox*", + ], + ) + assert result.exit_code == 0 + recc_log = result.output + + # Verify recc is getting action cache hits for both, compiling and linking + assert recc_log.count("Action Cache hit") == 2 + + +@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") +@pytest.mark.datafiles(DATA_DIR) +def test_cache_only(cli, datafiles): + project = str(datafiles) + checkout1 = os.path.join(cli.directory, "checkout1") + checkout2 = os.path.join(cli.directory, "checkout2") + element_name = "recc/cacheonly.bst" + + # Always cache buildtrees to be able to check recc logs + result = cli.run(project=project, args=["--cache-buildtrees", "always", "build", element_name]) + if result.exit_code != 0: + # Output recc logs in case of failure + cli.run( + project=project, + args=[ + "shell", + "--build", + "--use-buildtree", + element_name, + "--", + "sh", + "-c", + "cat config.log .recc-log/* */.recc-log/*", + ], + ) + assert result.exit_code == 0 + + result = cli.run(project=project, args=["artifact", "checkout", element_name, "--directory", checkout1]) + assert result.exit_code == 0 + + assert_contains( + checkout1, + [ + "/usr", + "/usr/bin", + "/usr/share", + "/usr/bin/hello", + "/usr/share/doc", + "/usr/share/doc/amhello", + "/usr/share/doc/amhello/README", + ], + ) + + # Check the main build log + result = cli.run(project=project, args=["artifact", "log", element_name]) + assert result.exit_code == 0 + log = result.output + + # Verify we get expected output exactly once + assert log.count("Making all in src") == 1 + + result = cli.run( + project=project, + args=[ + "shell", + "--build", + "--use-buildtree", + element_name, + "--", + "sh", + "-c", + "cat src/.recc-log/recc.buildbox*", + ], + ) + assert result.exit_code == 0 + recc_log = result.output + + # Verify recc is using local execution for both, compiling and linking + assert recc_log.count("Action not cached and running in cache-only mode, executing locally") == 2 + + # Delete artifact from BuildStream cache to trigger a BuildStream rebuild with action cache hits for recc + result = cli.run(project=project, args=["artifact", "delete", element_name]) + assert result.exit_code == 0 + + result = cli.run(project=project, args=["--cache-buildtrees", "always", "build", element_name]) + assert result.exit_code == 0 + + result = cli.run(project=project, args=["artifact", "checkout", element_name, "--directory", checkout2]) + assert result.exit_code == 0 + + assert_contains( + checkout2, + [ + "/usr", + "/usr/bin", + "/usr/share", + "/usr/bin/hello", + "/usr/share/doc", + "/usr/share/doc/amhello", + "/usr/share/doc/amhello/README", + ], + ) + + result = cli.run( + project=project, + args=[ + "shell", + "--build", + "--use-buildtree", + element_name, + "--", + "sh", + "-c", + "cat src/.recc-log/recc.buildbox*", + ], + ) + assert result.exit_code == 0 + recc_log = result.output + + # Verify recc is getting action cache hits for both, compiling and linking + assert recc_log.count("Action Cache hit") == 2 diff --git a/tests/integration/shellbuildtrees.py b/tests/integration/shellbuildtrees.py index c4f1d35bc..d042ea5fd 100644 --- a/tests/integration/shellbuildtrees.py +++ b/tests/integration/shellbuildtrees.py @@ -347,7 +347,7 @@ def test_shell_pull_cached_buildtree(share_with_buildtrees, datafiles, cli, pull @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_shell_pull_artifact_cached_buildtree(share_with_buildtrees, datafiles, cli): project = str(datafiles) - artifact_name = "test/build-shell-buildtree/4a47c98a10df39e65e99d471f96edc5b58d4ea5b9b1f221d0be832a8124b8099" + artifact_name = "test/build-shell-buildtree/f2f21aa730dcc49f682bb8406ef4705a51213ccf9739dc43b8b4fdc2d310e3f6" cli.configure({"artifacts": {"servers": [{"url": share_with_buildtrees.repo}]}})