blob: cf8587230df491a5765470069193192ec640f6f2 [file] [log] [blame]
# Pylint doesn't play well with fixtures and dependency injection from pytest
# pylint: disable=redefined-outer-name
import os
import re
import shutil
import pytest
from buildstream.testing import cli_remote_execution as cli # pylint: disable=unused-import
from buildstream.testing.integration import assert_contains
pytestmark = pytest.mark.remoteexecution
# subdirectories of the buildtree
SRC = "src"
DEPS = os.path.join(SRC, ".deps")
AUTO = "autom4te.cache"
DIRS = [os.sep + SRC, os.sep + DEPS, os.sep + AUTO]
DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project")
MAIN = os.path.join(SRC, "main.c")
MAINO = os.path.join(SRC, "main.o")
CFGMARK = "config-time"
BLDMARK = "build-time"
def files():
_input_files = [
".bstproject.yaml",
"aclocal.m4",
"missing",
"README",
"install-sh",
"depcomp",
"configure.ac",
"compile",
SRC,
MAIN,
os.path.join(SRC, "Makefile.am"),
"Makefile.am",
]
input_files = [os.sep + fname for fname in _input_files]
_generated_files = [
"Makefile",
"Makefile.in",
AUTO,
os.path.join(AUTO, "traces.1"),
os.path.join(AUTO, "traces.0"),
os.path.join(AUTO, "requests"),
os.path.join(AUTO, "output.0"),
os.path.join(AUTO, "output.1"),
"config.h",
"config.h.in",
"config.log",
"config.status",
"configure",
"configure.lineno",
os.path.join(SRC, "hello"),
DEPS,
os.path.join(DEPS, "main.Po"),
os.path.join(SRC, "Makefile"),
MAINO,
CFGMARK,
BLDMARK,
os.path.join(SRC, "Makefile.in"),
"stamp-h1",
]
generated_files = [os.sep + fname for fname in _generated_files]
_artifacts = [
"usr",
os.path.join("usr", "lib"),
os.path.join("usr", "bin"),
os.path.join("usr", "share"),
os.path.join("usr", "bin", "hello"),
os.path.join("usr", "share", "doc"),
os.path.join("usr", "share", "doc", "amhello"),
os.path.join("usr", "share", "doc", "amhello", "README"),
]
artifacts = [os.sep + fname for fname in _artifacts]
return input_files, generated_files, artifacts
def _get_mtimes(root):
assert os.path.exists(root)
# timestamps on subdirs are not currently semantically meaningful
for dirname, _, filenames in os.walk(root):
filenames.sort()
for filename in filenames:
fname = os.path.join(dirname, filename)
yield fname[len(root) :], os.stat(fname).st_mtime
def get_mtimes(root):
return dict(set(_get_mtimes(root)))
def check_buildtree(
cli, project, element_name, input_files, generated_files, incremental=False,
):
# check modified workspace dir was cached
# - generated files are present
# - generated files are newer than inputs
# - check the date recorded in the marker file
# - check that the touched file mtime is preserved from before
assert cli and project and element_name and input_files and generated_files
result = cli.run(
project=project,
args=[
"shell",
"--build",
element_name,
"--use-buildtree",
"--",
"find",
".",
"-mindepth",
"1",
"-exec",
"stat",
"-c",
"%n::%Y",
"{}",
";",
],
)
result.assert_success()
buildtree = {}
output = result.output.splitlines()
typ_inptime = None
typ_gentime = None
for line in output:
assert "::" in line
fname, mtime = line.split("::")
# remove the symbolic dir
fname = fname[1:]
mtime = int(mtime)
buildtree[fname] = mtime
if incremental:
# directory timestamps are not meaningful
if fname in DIRS:
continue
if fname in input_files:
if fname != os.sep + MAIN and not typ_inptime:
typ_inptime = mtime
if fname in generated_files:
if fname != os.sep + MAINO and not typ_gentime:
typ_gentime = mtime
# all expected files should have been found
for filename in input_files + generated_files:
assert filename in buildtree
if incremental:
# the source file was changed so should be more recent than other input files
# it should be older than the main object.
# The main object should be more recent than generated files.
assert buildtree[os.sep + MAIN] > typ_inptime
assert buildtree[os.sep + MAINO] > buildtree[os.sep + MAIN]
assert buildtree[os.sep + MAINO] > typ_gentime
for fname in DIRS:
del buildtree[fname]
return buildtree
def get_timemark(cli, project, element_name, marker):
result = cli.run(
project=project, args=["shell", "--build", element_name, "--use-buildtree", "--", "cat", marker[1:]],
)
result.assert_success()
marker_time = int(result.output)
return marker_time
@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.parametrize(
"modification", [pytest.param("content"), pytest.param("time"),],
)
def test_workspace_build(cli, tmpdir, datafiles, modification):
project = str(datafiles)
checkout = os.path.join(cli.directory, "checkout")
workspace = os.path.join(cli.directory, "workspace")
element_name = "autotools/amhello.bst"
# cli args
artifact_checkout = ["artifact", "checkout", element_name, "--directory", checkout]
build = ["--cache-buildtrees", "always", "build", element_name]
input_files, generated_files, artifacts = files()
services = cli.ensure_services()
assert set(services) == set(["action-cache", "execution", "storage"])
# open a workspace for the element in the workspace directory
result = cli.run(project=project, args=["workspace", "open", "--directory", workspace, element_name])
result.assert_success()
# check that the workspace path exists
assert os.path.exists(workspace)
# add a file (asserting later that this is in the buildtree)
newfile = "newfile.cfg"
newfile_path = os.path.join(workspace, newfile)
with open(newfile_path, "w") as fdata:
fdata.write("somestring")
input_files.append(os.sep + newfile)
# check that the workspace *only* contains the expected input files
assert_contains(workspace, input_files, strict=True)
# save the mtimes for later comparison
ws_times = get_mtimes(workspace)
# build the element and cache the buildtree
result = cli.run(project=project, args=build)
result.assert_success()
assert cli.get_element_state(project, element_name) == "cached"
build_key = cli.get_element_key(project, element_name)
# check that the local workspace is unchanged
assert_contains(workspace, input_files, strict=True)
assert ws_times == get_mtimes(workspace)
# check modified workspace dir was cached and save the time
# build was run. Incremental build conditions do not apply since the workspace
# was initially opened using magic timestamps.
build_times = check_buildtree(cli, project, element_name, input_files, generated_files, incremental=False)
build_timemark = get_timemark(cli, project, element_name, (os.sep + BLDMARK))
# check that the artifacts are available
result = cli.run(project=project, args=artifact_checkout)
result.assert_success()
assert_contains(checkout, artifacts)
shutil.rmtree(checkout)
# rebuild the element
result = cli.run(project=project, args=build)
result.assert_success()
assert cli.get_element_state(project, element_name) == "cached"
rebuild_key = cli.get_element_key(project, element_name)
assert rebuild_key == build_key
rebuild_times = check_buildtree(cli, project, element_name, input_files, generated_files, incremental=False)
rebuild_timemark = get_timemark(cli, project, element_name, (os.sep + BLDMARK))
# buildmark time should be the same
assert build_timemark == rebuild_timemark
assert all([rebuild_times[fname] == build_times[fname] for fname in rebuild_times]), "{}\n{}".format(
rebuild_times, build_times
)
# modify the open workspace and rebuild
main_path = os.path.join(workspace, MAIN)
assert os.path.exists(main_path)
if modification == "time":
# touch a file in the workspace and save the mtime
os.utime(main_path)
touched_time = int(os.stat(main_path).st_mtime)
elif modification == "content":
# change a source file (there's a race here but it's not serious)
with open(main_path, "r") as fdata:
data = fdata.readlines()
with open(main_path, "w") as fdata:
for line in data:
fdata.write(re.sub(r"Hello", "Goodbye", line))
touched_time = int(os.stat(main_path).st_mtime)
# refresh input times
ws_times = get_mtimes(workspace)
# rebuild the element
result = cli.run(project=project, args=build)
result.assert_success()
rebuild_times = check_buildtree(cli, project, element_name, input_files, generated_files, incremental=True)
rebuild_timemark = get_timemark(cli, project, element_name, (os.sep + BLDMARK))
assert rebuild_timemark > build_timemark
# check the times of the changed files
assert rebuild_times[os.sep + MAIN] == touched_time
del rebuild_times[os.sep + MAIN]
del rebuild_times[os.sep + MAINO]
del rebuild_times[os.sep + SRC + os.sep + "hello"]
del rebuild_times[os.sep + DEPS + os.sep + "main.Po"]
del rebuild_times[os.sep + BLDMARK]
# check the times of the unmodified files
assert all([rebuild_times[fname] == build_times[fname] for fname in rebuild_times]), "{}\n{}".format(
rebuild_times, build_times
)
# Check workspace is unchanged
assert_contains(workspace, input_files, strict=True)
assert ws_times == get_mtimes(workspace)