diff options
Diffstat (limited to 'tests/remoteexecution/workspace.py')
-rw-r--r-- | tests/remoteexecution/workspace.py | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/tests/remoteexecution/workspace.py b/tests/remoteexecution/workspace.py new file mode 100644 index 000000000..4ac743490 --- /dev/null +++ b/tests/remoteexecution/workspace.py @@ -0,0 +1,297 @@ +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +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 + + +DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project") +MKFILEAM = os.path.join("src", "Makefile.am") +MKFILE = os.path.join("src", "Makefile") +MAIN = 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", + os.path.join("src", "main.c"), + MKFILEAM, + "Makefile.am", + ] + input_files = [os.sep + fname for fname in _input_files] + + _generated_files = [ + "Makefile", + "Makefile.in", + "autom4te.cache", + os.path.join("autom4te.cache", "traces.1"), + os.path.join("autom4te.cache", "traces.0"), + os.path.join("autom4te.cache", "requests"), + os.path.join("autom4te.cache", "output.0"), + os.path.join("autom4te.cache", "output.1"), + "config.h", + "config.h.in", + "config.log", + "config.status", + "configure", + "configure.lineno", + os.path.join("src", "hello"), + os.path.join("src", ".deps"), + os.path.join("src", ".deps", "main.Po"), + MKFILE, + MAIN, + 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) + for dirname, dirnames, filenames in os.walk(root): + dirnames.sort() + filenames.sort() + for subdirname in dirnames: + fname = os.path.join(dirname, subdirname) + yield fname[len(root) :], os.stat(fname).st_mtime + for filename in filenames: + fname = os.path.join(dirname, filename) + yield fname[len(root) :], os.stat(fname).st_mtime + + +def get_mtimes(root): + return {k: v for (k, v) in 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", + "always", + "--", + "find", + ".", + "-mindepth", + "1", + "-exec", + "stat", + "-c", + "%n::%Y", + "{}", + ";", + ], + ) + result.assert_success() + + buildtree = {} + inp_times = [] + gen_times = [] + output = result.output.splitlines() + + 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: + if fname in input_files: + inp_times.append(mtime) + else: + gen_times.append(mtime) + + # all expected files should have been found + for filename in input_files + generated_files: + assert filename in buildtree + + if incremental: + # at least inputs should be older than generated files + assert not any([inp_time > gen_time for inp_time in inp_times for gen_time in gen_times]) + + makefile = os.sep + "Makefile" + makefile_am = os.sep + "Makefile.am" + mainc = os.sep + os.path.join("src", "main.c") + maino = os.sep + os.path.join("src", "hello") + testfiles = [makefile, makefile_am, mainc, maino] + if all([testfile in buildtree for testfile in testfiles]): + assert buildtree[makefile] < buildtree[makefile_am] + assert buildtree[mainc] < buildtree[maino] + + return buildtree + + +def get_timemark(cli, project, element_name, marker): + result = cli.run( + project=project, args=["shell", "--build", element_name, "--use-buildtree", "always", "--", "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("none"), + pytest.param("content"), + pytest.param("time", marks=pytest.mark.xfail(reason="mtimes are set to a magic value and not stored in CAS")), + ], +) +@pytest.mark.parametrize( + "buildtype", + [ + pytest.param("non-incremental"), + pytest.param( + "incremental", marks=pytest.mark.xfail(reason="incremental workspace builds are not yet supported") + ), + ], +) +def test_workspace_build(cli, tmpdir, datafiles, modification, buildtype): + incremental = False + if buildtype == "incremental": + incremental = True + + 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() + + # 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 + build_mtimes = check_buildtree(cli, project, element_name, input_files, generated_files, incremental=incremental) + 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() + # this should all be cached + # so the buildmark time should be the same + rebuild_mtimes = check_buildtree(cli, project, element_name, input_files, generated_files, incremental=incremental) + rebuild_timemark = get_timemark(cli, project, element_name, (os.sep + BLDMARK)) + + assert build_timemark == rebuild_timemark + assert build_mtimes == rebuild_mtimes + + # modify the open workspace and rebuild + if modification != "none": + assert os.path.exists(newfile_path) + + if modification == "time": + # touch a file in the workspace and save the mtime + os.utime(newfile_path) + + elif modification == "content": + # change a source file + with open(newfile_path, "w") as fdata: + fdata.write("anotherstring") + + # refresh input times + ws_times = get_mtimes(workspace) + + # rebuild the element + result = cli.run(project=project, args=build) + result.assert_success() + + rebuild_mtimes = check_buildtree( + cli, project, element_name, input_files, generated_files, incremental=incremental + ) + rebuild_timemark = get_timemark(cli, project, element_name, (os.sep + BLDMARK)) + assert build_timemark != rebuild_timemark + + # check the times of the changed files + if incremental: + touched_time = os.stat(newfile_path).st_mtime + assert rebuild_mtimes[newfile] == touched_time + + # Check workspace is unchanged + assert_contains(workspace, input_files, strict=True) + assert ws_times == get_mtimes(workspace) |