Source code for ramble.test.version

# Copyright 2022-2026 The Ramble Authors
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.

import os

import pytest

import ramble.variants
import ramble.workspace
from ramble.appkit import ExecutableApplication
from ramble.definitions.versions import ObjectVersion
from ramble.error import ObjectValidationError
from ramble.language.language_base import DirectiveError
from ramble.main import RambleCommand

pytestmark = pytest.mark.usefixtures(
    "mutable_config",
    "mutable_mock_workspace_path",
    "mutable_mock_apps_repo",
    "mock_modifiers",
    "mock_base_applications",
)

config = RambleCommand("config")
workspace = RambleCommand("workspace")


[docs] def test_only_one_preferred_version_allowed(): app_inst = ExecutableApplication("/not/a/path") app_inst.preferred_version = ObjectVersion(version_number="1.0", preferred=True) with pytest.raises(DirectiveError, match="Only one version can be marked preferred."): app_inst.version("1.1", preferred=True)
[docs] @pytest.mark.parametrize( "version,expected_zlib", [ ("2.0a1", {"zlib-greater"}), ("1.0", {"zlib-exact", "zlib-greater", "zlib-range"}), ("0.9", {"zlib-range", "zlib-less"}), ("0.8", {"zlib-less"}), ], ) def test_application_version_variant_when(workspace_name, version, expected_zlib): global_args = ["-w", workspace_name] zlib_versions = { ("zlib-exact", "zlib@1.2.14"), ("zlib-greater", "zlib@1.2.13"), ("zlib-range", "zlib@1.2.12"), ("zlib-less", "zlib@1.2.11"), } with ramble.workspace.create(workspace_name) as ws: workspace( "manage", "experiments", f"versions@{version}", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-p", "spack", global_args=global_args, ) workspace("concretize", global_args=global_args) with open(ws.config_file_path) as f: data = f.read() for zlib_name, zlib_version in zlib_versions: if zlib_name in expected_zlib: assert zlib_version in data else: assert zlib_version not in data
[docs] def test_strict_versions_config(workspace_name, capsys): global_args = ["-w", workspace_name] with ramble.workspace.create(workspace_name) as ws: workspace( "manage", "experiments", "versions@0.1", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-p", "spack", global_args=global_args, ) with pytest.raises(ObjectValidationError): captured = workspace("concretize", global_args=global_args) assert "You must select from defined versions" in captured config("add", "config:enable_strict_versions:false", global_args=global_args) ws._re_read() workspace("concretize", global_args=global_args) with open(ws.config_file_path) as f: data = f.read() assert "versions@0.1" in data
[docs] def test_strict_versions_directive(workspace_name): """Test that the strict_versions(False) directive works correctly.""" global_args = ["-w", workspace_name] with ramble.workspace.create(workspace_name) as ws: workspace( "manage", "experiments", "versions-strict-disabled@0.1", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-p", "spack", global_args=global_args, ) workspace("concretize", global_args=global_args) with open(ws.config_file_path) as f: data = f.read() assert "versions-strict-disabled@0.1" in data
[docs] def test_versions_inherited_from_base_app(workspace_name): global_args = ["-w", workspace_name] with ramble.workspace.create(workspace_name) as ws: # Add a version inherited from base application workspace( "manage", "experiments", "versions@0.8", "--wf", "test_wl_base", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-p", "spack", global_args=global_args, ) # Add a version from application workspace( "manage", "experiments", "versions@1.0", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-p", "spack", global_args=global_args, ) workspace("concretize", global_args=global_args) with open(ws.config_file_path) as f: data = f.read() assert "versions@0.8" in data assert "versions@1.0" in data
[docs] def test_non_pep440_version_number(workspace_name): global_args = ["-w", workspace_name] with ramble.workspace.create(workspace_name) as ws: workspace( "manage", "experiments", "versions-nonstandard@1_01", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-p", "spack", global_args=global_args, ) workspace("concretize", global_args=global_args) with open(ws.config_file_path) as f: data = f.read() assert "versions-nonstandard@1_01" in data assert "zlib@1.2.14" in data output = workspace("info", "-v", global_args=global_args) assert "Rendered package: versions-nonstandard-1_01" in output assert "Spec: versions-nonstandard@1_01" in output assert "versions-nonstandard-1_01 = versions-nonstandard@1_01" in output assert "zlib-exact = zlib@1.2.14" in output
[docs] @pytest.mark.parametrize( "version,expected_zlib", [ ("1_01", {"zlib-exact", "zlib-range", "zlib-less"}), ("1_02", {"zlib-greater", "zlib-range"}), ("2_0a1", {"zlib-greater"}), ("0.8", {"zlib-less"}), ], ) def test_non_pep440_application_version_variant_when(workspace_name, version, expected_zlib): global_args = ["-w", workspace_name] zlib_versions = { ("zlib-exact", "zlib@1.2.14"), ("zlib-greater", "zlib@1.2.13"), ("zlib-range", "zlib@1.2.12"), ("zlib-less", "zlib@1.2.11"), } with ramble.workspace.create(workspace_name) as ws: workspace( "manage", "experiments", f"versions-nonstandard@{version}", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-p", "spack", global_args=global_args, ) workspace("concretize", global_args=global_args) with open(ws.config_file_path) as f: data = f.read() for zlib_name, zlib_version in zlib_versions: if zlib_name in expected_zlib: assert zlib_version in data else: assert zlib_version not in data
[docs] def test_version_variable_expansion_info(workspace_name): global_args = ["-w", workspace_name] with ramble.workspace.create(workspace_name) as ws: workspace( "manage", "experiments", "versions@{version}", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-v", "version=['1.0', '2.0a1']", "-v", "custom_var='val_{version}'", "-p", "spack", global_args=global_args, ) workspace("concretize", global_args=global_args) output = workspace("info", global_args=global_args) assert "versions@1.0.test_wl.generated" in output assert "versions@2.0a1.test_wl.generated" in output # Verify expansion in the ramble.yaml file with open(ws.config_file_path) as f: config_content = f.read() assert "versions@{version}" in config_content assert "val_{version}" in config_content # Verify application_version logic works for expanded experiments # versions@1.0 should have zlib@1.2.14, versions@2.0a1 should not. output_v = workspace("info", "-vv", global_args=global_args) assert "versions@1.0.test_wl.generated" in output_v assert "versions@2.0a1.test_wl.generated" in output_v assert "custom_var = 'val_{version}' ==> val_1.0" in output_v assert "custom_var = 'val_{version}' ==> val_2.0a1" in output_v assert "zlib-exact = zlib@1.2.14" in output_v assert "zlib-greater = zlib@1.2.13" in output_v workspace("setup", "--dry-run", global_args=global_args) # Verify paths for both experiments exp1_path = os.path.join(ws.experiment_dir, "versions@1.0", "test_wl", "generated") exp2_path = os.path.join(ws.experiment_dir, "versions@2.0a1", "test_wl", "generated") assert os.path.exists(exp1_path) assert os.path.exists(exp2_path)
[docs] def test_repeat_modifier_versions_error(workspace_name): global_args = ["-w", workspace_name] ramble.workspace.create(workspace_name) workspace( "manage", "experiments", "versions@0.9", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", global_args=global_args, ) workspace( "manage", "modifiers", "--add", "--scope", "workspace", "--name", "versions-mod@1.0", "--on-executable", "foo", global_args=global_args, ) workspace( "manage", "modifiers", "--add", "--scope", "workspace", "--name", "versions-mod@2.0", "--on-executable", "bar", global_args=global_args, ) err_str = ( "Two modifier definitions conflict by having the same name and different version numbers." ) with pytest.raises(ramble.error.ConflictingModifiersError, match=err_str): workspace("info", global_args=global_args)
[docs] def test_multi_modifier_versions(workspace_name): global_args = ["-w", workspace_name, "--overwrite-inventories"] with ramble.workspace.create(workspace_name) as ws: workspace( "manage", "experiments", "versions@0.9", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", global_args=global_args, ) workspace( "manage", "modifiers", "--add", "--scope", "workspace", "--name", "versions-mod@1.0", global_args=global_args, ) workspace( "manage", "modifiers", "--add", "--scope", "workspace", "--name", "info@2.0", global_args=global_args, ) exec_tpl = os.path.join(ws.config_dir, "execute_experiment.tpl") with open(exec_tpl, "a+") as f: f.write("versions-mod version: {modifier::versions-mod::version}\n") f.write("info version: {modifier::info::version}\n") workspace("setup", "--dry-run", global_args=global_args) exec_script = os.path.join( ws.experiment_dir, "versions@0.9", "test_wl", "generated", "execute_experiment" ) with open(exec_script) as f: data = f.read() assert "versions-mod version: 1.0" in data assert "info version: 2.0" in data
[docs] def test_define_version_variables_errors(workspace_name): global_args = ["-w", workspace_name, "--overwrite-inventories"] ramble.workspace.create(workspace_name) workspace( "manage", "experiments", "versions@0.9", "--wf", "test_wl", "-v", "n_ranks=1", "-v", "n_nodes=1", "-v", "processes_per_node=1", "-v", "application::versions::version=foo", global_args=global_args, ) err_str = ( 'Keyword "application::versions::version" has been defined, but is reserved by ramble' ) with pytest.raises(ramble.experiment_set.RambleVariableDefinitionError, match=err_str): workspace("info", global_args=global_args)