Skip to content

Commit

Permalink
Tests for configuration hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
mosteo committed Aug 29, 2023
1 parent 19cdc8f commit 8b72292
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 22 deletions.
21 changes: 19 additions & 2 deletions src/alire/alire-roots.adb
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
22 changes: 20 additions & 2 deletions testsuite/drivers/builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -45,4 +46,21 @@ def path() -> str:
"""
Return the path to the shared build directory.
"""
return alr_builds_dir()
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()
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
description = "\"Hello, world!\" demonstration project support library"
name = "libhello"
version = "0.9.0"
maintainers = ["[email protected]"]
maintainers-logins = ["mylogin"]
tags = ["libhello-tag1"]

[configuration.variables]
Var1={type="Boolean"} # Without default on purpose

[origin]
url = "file:../../../crates/libhello_1.0.0"
13 changes: 5 additions & 8 deletions testsuite/tests/build/hashes/compiler-input/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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()}")
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -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")


Expand Down
5 changes: 4 additions & 1 deletion testsuite/tests/build/hashes/compiler-missing/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
30 changes: 30 additions & 0 deletions testsuite/tests/build/hashes/config-types/test.py
Original file line number Diff line number Diff line change
@@ -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")
3 changes: 3 additions & 0 deletions testsuite/tests/build/hashes/config-types/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
driver: python-script
indexes:
build_hash_index: {}
28 changes: 28 additions & 0 deletions testsuite/tests/build/hashes/incomplete-config/test.py
Original file line number Diff line number Diff line change
@@ -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")
3 changes: 3 additions & 0 deletions testsuite/tests/build/hashes/incomplete-config/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
driver: python-script
indexes:
build_hash_index: {}
16 changes: 7 additions & 9 deletions testsuite/tests/config/shared-deps/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions testsuite/tests/dockerized/misc/default-cache/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 8b72292

Please sign in to comment.