|
| 1 | +"""Test ignore_keys functionality with context variables.""" |
| 2 | + |
| 3 | +from pathlib import Path |
| 4 | +from helpers import RattlerBuild |
| 5 | + |
| 6 | + |
| 7 | +def test_eigen_abi_profile_ignore_keys( |
| 8 | + rattler_build: RattlerBuild, recipes: Path, tmp_path: Path |
| 9 | +): |
| 10 | + """Test the eigen recipe pattern where eigen ignores eigen_abi_profile but eigen-abi includes it. |
| 11 | +
|
| 12 | + This test validates the specific use case from the issue where: |
| 13 | + - The 'eigen' output should have the SAME hash across all eigen_abi_profile values (ignored) |
| 14 | + - The 'eigen-abi' output should have DIFFERENT hashes for each eigen_abi_profile value (not ignored) |
| 15 | + - The 'eigen-abi' package version should include the abi profile (e.g., 3.4.0.100) |
| 16 | + """ |
| 17 | + recipe_path = recipes / "variants" / "issue_variant_ignore.yaml" |
| 18 | + variant_config = recipes / "variants" / "variant_config.yaml" |
| 19 | + |
| 20 | + # Render the recipe to get all variants |
| 21 | + rendered = rattler_build.render( |
| 22 | + recipe_path, tmp_path, variant_config=variant_config |
| 23 | + ) |
| 24 | + |
| 25 | + # Separate the outputs by package name |
| 26 | + eigen_outputs = [] |
| 27 | + eigen_abi_outputs = [] |
| 28 | + eigen_abi_other_outputs = [] |
| 29 | + |
| 30 | + for item in rendered: |
| 31 | + package_name = item["recipe"]["package"]["name"] |
| 32 | + build_config = item.get("build_configuration", {}) |
| 33 | + variant = build_config.get("variant", {}) |
| 34 | + build_string = item["recipe"]["build"]["string"] |
| 35 | + version = item["recipe"]["package"]["version"] |
| 36 | + |
| 37 | + if package_name == "eigen": |
| 38 | + eigen_outputs.append( |
| 39 | + { |
| 40 | + "version": version, |
| 41 | + "build_string": build_string, |
| 42 | + "variant": variant, |
| 43 | + "abi_profile": variant.get("eigen_abi_profile", "none"), |
| 44 | + "some_key": variant.get("some_key", "none"), |
| 45 | + } |
| 46 | + ) |
| 47 | + elif package_name == "eigen-abi": |
| 48 | + eigen_abi_outputs.append( |
| 49 | + { |
| 50 | + "version": version, |
| 51 | + "build_string": build_string, |
| 52 | + "variant": variant, |
| 53 | + "abi_profile": variant.get("eigen_abi_profile", "none"), |
| 54 | + "some_key": variant.get("some_key", "none"), |
| 55 | + } |
| 56 | + ) |
| 57 | + elif package_name == "eigen-abi-other": |
| 58 | + eigen_abi_other_outputs.append( |
| 59 | + { |
| 60 | + "version": version, |
| 61 | + "build_string": build_string, |
| 62 | + "variant": variant, |
| 63 | + "abi_profile": variant.get("eigen_abi_profile", "none"), |
| 64 | + "some_key": variant.get("some_key", "none"), |
| 65 | + } |
| 66 | + ) |
| 67 | + |
| 68 | + # Verify we have the expected number of outputs |
| 69 | + # eigen: ignores eigen_abi_profile -> 1 build (same hash for all abi profiles) |
| 70 | + # eigen-abi: does NOT ignore eigen_abi_profile -> 2 builds (one per abi profile: 100, 80) |
| 71 | + # eigen-abi-other: uses use_keys: [some_key] and ignore_keys: [eigen_abi_profile] |
| 72 | + # use_keys forces some_key into the variant -> 2 builds (one per some_key: 1, 2) |
| 73 | + # ignore_keys excludes eigen_abi_profile from hash |
| 74 | + assert len(eigen_outputs) == 1, ( |
| 75 | + f"Expected 1 eigen output (since ignore_keys makes all abi_profile variants identical), " |
| 76 | + f"got {len(eigen_outputs)}" |
| 77 | + ) |
| 78 | + assert ( |
| 79 | + len(eigen_abi_outputs) == 2 |
| 80 | + ), f"Expected 2 eigen-abi outputs (one per abi_profile), got {len(eigen_abi_outputs)}" |
| 81 | + assert len(eigen_abi_other_outputs) == 2, ( |
| 82 | + f"Expected 2 eigen-abi-other outputs (one per some_key via use_keys), " |
| 83 | + f"got {len(eigen_abi_other_outputs)}" |
| 84 | + ) |
| 85 | + |
| 86 | + # Test 1: eigen output should only be built once (all abi profiles produce same hash) |
| 87 | + # This is the whole point of ignore_keys! |
| 88 | + eigen_build_string = eigen_outputs[0]["build_string"] |
| 89 | + |
| 90 | + # Test 2: eigen-abi output should have DIFFERENT build strings for each abi profile |
| 91 | + eigen_abi_build_strings = [o["build_string"] for o in eigen_abi_outputs] |
| 92 | + unique_eigen_abi_builds = set(eigen_abi_build_strings) |
| 93 | + assert len(unique_eigen_abi_builds) == 2, ( |
| 94 | + f"eigen-abi package should have different build strings for each abi_profile value, " |
| 95 | + f"but got: {eigen_abi_build_strings}" |
| 96 | + ) |
| 97 | + |
| 98 | + # Test 3: Verify the variant information displayed |
| 99 | + # eigen should NOT show eigen_abi_profile in its variant (it's ignored) |
| 100 | + for output in eigen_outputs: |
| 101 | + variant = output["variant"] |
| 102 | + # The variant dict should NOT contain eigen_abi_profile for the eigen package |
| 103 | + # because it's in ignore_keys |
| 104 | + assert ( |
| 105 | + "eigen_abi_profile" not in variant |
| 106 | + or variant.get("eigen_abi_profile") is None |
| 107 | + ), ( |
| 108 | + f"eigen variant should not include eigen_abi_profile (it's ignored), " |
| 109 | + f"but variant is: {variant}" |
| 110 | + ) |
| 111 | + # Should only have target_platform |
| 112 | + assert "target_platform" in variant, "eigen variant should have target_platform" |
| 113 | + assert ( |
| 114 | + output["version"] == "3.4.0" |
| 115 | + ), f"eigen version should be 3.4.0, got {output['version']}" |
| 116 | + |
| 117 | + # Test 4: eigen-abi SHOULD show eigen_abi_profile in its variant (not ignored) |
| 118 | + abi_profiles_found = set() |
| 119 | + for output in eigen_abi_outputs: |
| 120 | + variant = output["variant"] |
| 121 | + # The variant dict SHOULD contain eigen_abi_profile for eigen-abi |
| 122 | + assert ( |
| 123 | + "eigen_abi_profile" in variant |
| 124 | + ), f"eigen-abi variant should include eigen_abi_profile, but variant is: {variant}" |
| 125 | + abi_profile = variant["eigen_abi_profile"] |
| 126 | + abi_profiles_found.add(str(abi_profile)) |
| 127 | + |
| 128 | + # Version should include the abi profile (e.g., 3.4.0.100) |
| 129 | + expected_version = f"3.4.0.{abi_profile}" |
| 130 | + assert ( |
| 131 | + output["version"] == expected_version |
| 132 | + ), f"eigen-abi version should be {expected_version}, got {output['version']}" |
| 133 | + |
| 134 | + # Verify we saw both abi profiles |
| 135 | + assert abi_profiles_found == { |
| 136 | + "100", |
| 137 | + "80", |
| 138 | + }, f"Expected to find abi profiles 100 and 80, but found: {abi_profiles_found}" |
| 139 | + |
| 140 | + # eigen-abi should have different hashes |
| 141 | + eigen_abi_100 = next( |
| 142 | + (o for o in eigen_abi_outputs if o["abi_profile"] == "100"), None |
| 143 | + ) |
| 144 | + eigen_abi_80 = next( |
| 145 | + (o for o in eigen_abi_outputs if o["abi_profile"] == "80"), None |
| 146 | + ) |
| 147 | + |
| 148 | + assert ( |
| 149 | + eigen_abi_100 is not None |
| 150 | + ), "Could not find eigen-abi output for abi_profile 100" |
| 151 | + assert ( |
| 152 | + eigen_abi_80 is not None |
| 153 | + ), "Could not find eigen-abi output for abi_profile 80" |
| 154 | + |
| 155 | + hash_100 = eigen_abi_100["build_string"].split("_")[0] |
| 156 | + hash_80 = eigen_abi_80["build_string"].split("_")[0] |
| 157 | + |
| 158 | + # Verify they have different hashes (the actual hash values may vary) |
| 159 | + assert hash_100 != hash_80, ( |
| 160 | + f"eigen-abi outputs for different abi_profiles should have different hashes, " |
| 161 | + f"but both have: {hash_100}" |
| 162 | + ) |
| 163 | + |
| 164 | + # Test 6: eigen-abi-other validates that use_keys and ignore_keys work together |
| 165 | + # use_keys forces some_key into variant even though it's not referenced |
| 166 | + # ignore_keys prevents eigen_abi_profile from affecting the hash |
| 167 | + some_keys_found = set() |
| 168 | + eigen_abi_other_by_some_key = {} |
| 169 | + |
| 170 | + for output in eigen_abi_other_outputs: |
| 171 | + variant = output["variant"] |
| 172 | + |
| 173 | + # Should have some_key (via use_keys) |
| 174 | + assert ( |
| 175 | + "some_key" in variant |
| 176 | + ), f"eigen-abi-other variant should include some_key (via use_keys), but variant is: {variant}" |
| 177 | + |
| 178 | + # Should NOT have eigen_abi_profile (via ignore_keys) |
| 179 | + assert ( |
| 180 | + "eigen_abi_profile" not in variant |
| 181 | + or variant.get("eigen_abi_profile") is None |
| 182 | + ), ( |
| 183 | + f"eigen-abi-other variant should not include eigen_abi_profile (it's ignored), " |
| 184 | + f"but variant is: {variant}" |
| 185 | + ) |
| 186 | + |
| 187 | + some_key_value = variant["some_key"] |
| 188 | + some_keys_found.add(str(some_key_value)) |
| 189 | + eigen_abi_other_by_some_key[str(some_key_value)] = output |
| 190 | + |
| 191 | + # Verify we saw both some_key values |
| 192 | + assert some_keys_found == { |
| 193 | + "1", |
| 194 | + "2", |
| 195 | + }, f"Expected to find some_key values 1 and 2, but found: {some_keys_found}" |
| 196 | + |
| 197 | + # Verify different some_key values produce different hashes |
| 198 | + hash_1 = eigen_abi_other_by_some_key["1"]["build_string"].split("_")[0] |
| 199 | + hash_2 = eigen_abi_other_by_some_key["2"]["build_string"].split("_")[0] |
| 200 | + |
| 201 | + assert hash_1 != hash_2, ( |
| 202 | + f"eigen-abi-other outputs for different some_key values should have different hashes, " |
| 203 | + f"but both have: {hash_1}" |
| 204 | + ) |
| 205 | + |
| 206 | + print("\n✓ Test passed!") |
| 207 | + print(f" eigen (ignores abi_profile): {eigen_build_string}") |
| 208 | + print(f" eigen-abi-100: {eigen_abi_100['build_string']}") |
| 209 | + print(f" eigen-abi-80: {eigen_abi_80['build_string']}") |
| 210 | + print( |
| 211 | + f" eigen-abi-other (some_key=1, ignores abi_profile): {eigen_abi_other_by_some_key['1']['build_string']}" |
| 212 | + ) |
| 213 | + print( |
| 214 | + f" eigen-abi-other (some_key=2, ignores abi_profile): {eigen_abi_other_by_some_key['2']['build_string']}" |
| 215 | + ) |
0 commit comments