From 8b72292fcf8a36be7f1dd7baa09ee1880bf92461 Mon Sep 17 00:00:00 2001 From: "Alejandro R. Mosteo" Date: Mon, 28 Aug 2023 23:39:09 +0200 Subject: [PATCH] Tests for configuration hashing --- src/alire/alire-roots.adb | 21 +++++++++++-- testsuite/drivers/builds.py | 22 ++++++++++++-- .../li/libhello/libhello-0.9.0.toml | 12 ++++++++ .../tests/build/hashes/compiler-input/test.py | 13 ++++---- .../build/hashes/compiler-missing/test.py | 5 +++- .../tests/build/hashes/config-types/test.py | 30 +++++++++++++++++++ .../tests/build/hashes/config-types/test.yaml | 3 ++ .../build/hashes/incomplete-config/test.py | 28 +++++++++++++++++ .../build/hashes/incomplete-config/test.yaml | 3 ++ testsuite/tests/config/shared-deps/test.py | 16 +++++----- .../dockerized/misc/default-cache/test.py | 4 +++ 11 files changed, 135 insertions(+), 22 deletions(-) create mode 100644 testsuite/fixtures/build_hash_index/li/libhello/libhello-0.9.0.toml create mode 100644 testsuite/tests/build/hashes/config-types/test.py create mode 100644 testsuite/tests/build/hashes/config-types/test.yaml create mode 100644 testsuite/tests/build/hashes/incomplete-config/test.py create mode 100644 testsuite/tests/build/hashes/incomplete-config/test.yaml diff --git a/src/alire/alire-roots.adb b/src/alire/alire-roots.adb index 420b653db..03c8bc99b 100644 --- a/src/alire/alire-roots.adb +++ b/src/alire/alire-roots.adb @@ -212,7 +212,7 @@ package body Alire.Roots is -- Changes in configuration may require new build dirs end if; - if Export_Build_Env then + if Export_Build_Env or else not Builds.Sandboxed_Dependencies then This.Export_Build_Environment; end if; @@ -765,7 +765,24 @@ package body Alire.Roots is -- dependencies from there. Post-fetch may happen even with shared -- builds for linked and binary dependencies. - This.Export_Build_Environment; + if Builds.Sandboxed_Dependencies then + This.Export_Build_Environment; + else + null; + -- When using shared dependencies we have a conflict between crates + -- "in-place" (without syncing, e.g. links/binaries), which should + -- have its post-fetch run immediately, and regular crates, which + -- get the post-fetch run after sync. Since the complete environment + -- cannot be known for the former until build time (as config could + -- be incomplete otherwise), we need to delay post-fetch for all + -- crates to build time, in a follow-up PR. Meanwhile, in some corner + -- cases post-fetch could fail when using shared deps (in-place + -- crates with a post-fetch that relies on the environment). + + -- TODO: delay post-fetch for binary/linked crates to the build + -- moment too. Do this for both sandboxed/shared, for the sake + -- of simplicity? + end if; -- Visit dependencies in a safe order to be fetched, and their actions -- ran diff --git a/testsuite/drivers/builds.py b/testsuite/drivers/builds.py index 7a2f73d50..32397ced5 100644 --- a/testsuite/drivers/builds.py +++ b/testsuite/drivers/builds.py @@ -5,7 +5,8 @@ from glob import glob import os from shutil import rmtree -from drivers.alr import alr_builds_dir +import subprocess +from drivers.alr import alr_builds_dir, run_alr def clear_builds_dir() -> None: @@ -45,4 +46,21 @@ def path() -> str: """ Return the path to the shared build directory. """ - return alr_builds_dir() \ No newline at end of file + return alr_builds_dir() + + +def sync() -> None: + """ + Sync the shared build directory + """ + # We force the sync by running a build, no matter if it succeeds or not + try: + subprocess.run(["alr", "-q", "-d", "build"] + , stdout=subprocess.DEVNULL + , stderr=subprocess.DEVNULL + ) + except: + pass + +def sync_builds() -> None: + sync() \ No newline at end of file diff --git a/testsuite/fixtures/build_hash_index/li/libhello/libhello-0.9.0.toml b/testsuite/fixtures/build_hash_index/li/libhello/libhello-0.9.0.toml new file mode 100644 index 000000000..0a2b0cef9 --- /dev/null +++ b/testsuite/fixtures/build_hash_index/li/libhello/libhello-0.9.0.toml @@ -0,0 +1,12 @@ +description = "\"Hello, world!\" demonstration project support library" +name = "libhello" +version = "0.9.0" +maintainers = ["alejandro@mosteo.com"] +maintainers-logins = ["mylogin"] +tags = ["libhello-tag1"] + +[configuration.variables] +Var1={type="Boolean"} # Without default on purpose + +[origin] +url = "file:../../../crates/libhello_1.0.0" diff --git a/testsuite/tests/build/hashes/compiler-input/test.py b/testsuite/tests/build/hashes/compiler-input/test.py index 5a127ac36..16090b69b 100644 --- a/testsuite/tests/build/hashes/compiler-input/test.py +++ b/testsuite/tests/build/hashes/compiler-input/test.py @@ -7,6 +7,7 @@ from drivers.alr import external_compiler_version, run_alr, init_local_crate, alr_with from drivers.asserts import assert_match from drivers.builds import clear_builds_dir, hash_input +from drivers import builds def check_hash(signature: str) -> None: @@ -28,7 +29,7 @@ def check_hash(signature: str) -> None: # Init a crate without explicit compiler dependency init_local_crate("xxx") alr_with("crate_real") # A regular crate in the index -run_alr("update") # Ensure the hash inputs are written to disk +builds.sync() # Ensure the hash inputs are written to disk # Check the external compiler is in the hash inputs check_hash(f"version:gnat_external={external_compiler_version()}") @@ -40,11 +41,7 @@ def check_hash(signature: str) -> None: run_alr("toolchain", "--select", "gnat_native") # Clear the build cache so we are able to locate the new hash clear_builds_dir() -run_alr("update") -run_alr("update") -# Twice necessary because otherwise the hash inputs cannot be written (during -# the first update the destination folder does not yet exist, and the crate -# sync would remove the hash inputs file) +builds.sync() # Check the expected compiler is in the hash inputs check_hash("version:gnat_native=8888.0.0") @@ -55,7 +52,7 @@ def check_hash(signature: str) -> None: clear_builds_dir() alr_with("gnat=7777") # Downgrade the compiler with an explicit dependency -run_alr("update") +builds.sync() # Check the expected compiler is in the hash inputs check_hash("version:gnat_native=7777.0.0") @@ -67,7 +64,7 @@ def check_hash(signature: str) -> None: clear_builds_dir() alr_with("gnat_native") -run_alr("update") +builds.sync() check_hash("version:gnat_native=7777.0.0") diff --git a/testsuite/tests/build/hashes/compiler-missing/test.py b/testsuite/tests/build/hashes/compiler-missing/test.py index caa3f7f56..8a838a359 100644 --- a/testsuite/tests/build/hashes/compiler-missing/test.py +++ b/testsuite/tests/build/hashes/compiler-missing/test.py @@ -13,7 +13,10 @@ # Init a crate without explicit compiler dependency init_local_crate("xxx") -p = run_alr("with", "libhello", complain_on_error=False) # This should fail +run_alr("with", "libhello") + +# The build fails because we cannot compute the build hash without a compiler +p = run_alr("build", complain_on_error=False) assert_match(".*Unable to determine compiler version", p.out) print("SUCCESS") diff --git a/testsuite/tests/build/hashes/config-types/test.py b/testsuite/tests/build/hashes/config-types/test.py new file mode 100644 index 000000000..69348e127 --- /dev/null +++ b/testsuite/tests/build/hashes/config-types/test.py @@ -0,0 +1,30 @@ +""" +Check the different config types in the hash inputs +""" + +from drivers.alr import alr_with, external_compiler_version, init_local_crate, run_alr +from drivers.builds import hash_input +from drivers.asserts import assert_eq +from drivers import builds + +run_alr("config", "--set", "--global", "dependencies.shared", "true") + +init_local_crate() +alr_with("hello=1.0.1") +builds.sync() + +# Chech that the hash inputs contains exactly what we expect it to contain + +assert_eq( + 'config:hello.var1=true\n' + 'config:hello.var2=str\n' + 'config:hello.var3=A\n' + 'config:hello.var4=0\n' + 'config:hello.var5=0\n' + 'config:hello.var6=0.00000000000000E+00\n' + 'config:hello.var7=0.00000000000000E+00\n' + 'profile:hello=RELEASE\n' + f'version:gnat_external={external_compiler_version()}\n', + hash_input("hello")) + +print("SUCCESS") diff --git a/testsuite/tests/build/hashes/config-types/test.yaml b/testsuite/tests/build/hashes/config-types/test.yaml new file mode 100644 index 000000000..8e25447d9 --- /dev/null +++ b/testsuite/tests/build/hashes/config-types/test.yaml @@ -0,0 +1,3 @@ +driver: python-script +indexes: + build_hash_index: {} diff --git a/testsuite/tests/build/hashes/incomplete-config/test.py b/testsuite/tests/build/hashes/incomplete-config/test.py new file mode 100644 index 000000000..c649ea5a9 --- /dev/null +++ b/testsuite/tests/build/hashes/incomplete-config/test.py @@ -0,0 +1,28 @@ +""" +Test that a crate with incomplete config (values without defaults) cannot be +built/hashed +""" + +import shutil +from drivers.alr import alr_with, external_compiler_version, init_local_crate, run_alr +from drivers.builds import find_hash, hash_input +from drivers.asserts import assert_eq, assert_match +from drivers import builds + +run_alr("config", "--set", "--global", "dependencies.shared", "true") + +init_local_crate() +alr_with("libhello=0.9") + +run_alr("build", complain_on_error=False) +# This must fail as there are unset config varibles, but what really we are +# interested in is in checking that no build folder with an incorrect hash has +# been generated + +try: + hash = hash_input("libhello") # Must fail because no build folder exists + assert False, "Build folder with incomplete config should not be hashed" +except AssertionError: + pass + +print("SUCCESS") diff --git a/testsuite/tests/build/hashes/incomplete-config/test.yaml b/testsuite/tests/build/hashes/incomplete-config/test.yaml new file mode 100644 index 000000000..8e25447d9 --- /dev/null +++ b/testsuite/tests/build/hashes/incomplete-config/test.yaml @@ -0,0 +1,3 @@ +driver: python-script +indexes: + build_hash_index: {} diff --git a/testsuite/tests/config/shared-deps/test.py b/testsuite/tests/config/shared-deps/test.py index 7cb67adfe..966680b9d 100644 --- a/testsuite/tests/config/shared-deps/test.py +++ b/testsuite/tests/config/shared-deps/test.py @@ -5,8 +5,9 @@ import glob import os -from drivers.alr import (alr_builds_dir, alr_vault_dir, alr_with, alr_workspace_cache, - init_local_crate, run_alr) +from drivers import builds +from drivers.alr import (alr_builds_dir, alr_vault_dir, alr_with, + alr_workspace_cache, init_local_crate, run_alr) from drivers.asserts import assert_contents, assert_file_exists from drivers.helpers import lines_of @@ -35,17 +36,14 @@ # Check the contents in the build dir, that should not include generated config # because no build has been attempted yet, hence a sync has not been performed. +# And, since there's no sync yet, neither the build dir exists: -# We need to find the hash first -base = glob.glob(os.path.join(build_dir, "hello_1.0.1_filesystem_*"))[0] - -assert_contents(base, - [f'{base}/alire', - f'{base}/alire/build_hash_inputs' - ]) +assert len(glob.glob(os.path.join(build_dir, "hello_1.0.1_filesystem_*"))) == 0, \ + "Build dir should not exist yet" # Do a build, and now the sync should have happened and the build dir be filled run_alr("build") +base = builds.find_dir("hello_1.0.1_filesystem") assert_contents(base, [f'{base}/alire', diff --git a/testsuite/tests/dockerized/misc/default-cache/test.py b/testsuite/tests/dockerized/misc/default-cache/test.py index a2f6dabf3..1437dacc2 100644 --- a/testsuite/tests/dockerized/misc/default-cache/test.py +++ b/testsuite/tests/dockerized/misc/default-cache/test.py @@ -36,6 +36,10 @@ # Shared builds +# This generates the synced build dir. It fails because there is no toolchain +# configured, but that is not relevant for this test. +run_alr("build", complain_on_error=False) + # We hardcode this hash so we detect unwilling changes to our hashing scheme. # Every time this hash changes we must know the reason (changes in the hashing # procedures)