# kache-e2e fixture: locks the safety boundary around OUT_DIR
# normalization in the cache key.
#
# main.rs uses `const OUT_DIR: &str = env!("OUT_DIR")` to bake the
# absolute path into the binary. build.rs writes a data file at that
# path. At runtime, main reads back from the embedded path.
#
# Why this fixture exists:
#   PathNormalizer normalizes env_dep:OUT_DIR aggressively for the
#   common case (serde-style include!() pattern, where OUT_DIR is
#   only used to splice file CONTENT into the AST and the compiled
#   binary doesn't depend on the path). For crates that use OUT_DIR
#   as a runtime VALUE (this fixture), aggressive normalization
#   would produce a false cache hit: relocated build restores a
#   binary embedding the original path, but build.rs at the new
#   path didn't write data.txt to that location.
#
#   The cache_key code distinguishes the two patterns by checking
#   whether dep-info source files include any path under OUT_DIR.
#   If yes (include!() pattern): safe to normalize → keys match
#   across worktrees. If no (env! value pattern, this fixture):
#   keep absolute → keys diverge, relocated build refreshes.
#
# What this fixture proves:
#   - cold/warm/noop: standard lifecycle, runtime reads succeed
#   - relocate: keys MUST diverge so a fresh build runs at the new
#     path. If they didn't, verify would fail (read from stale path)

name = "e2e-out-dir-runtime"
tags = ["suite:e2e", "lang:rust", "case:path-runtime"]

[source]
kind = "fixture"
path = "source"

[env]
RUSTC_WRAPPER = "$KACHE"
# This fixture's whole point is to exercise OUT_DIR baked into the
# compiled `bin` artifact. The default `is_user_facing_executable`
# gate skips `bin` crates from the cache; opt in here so the bin
# itself enters the store. Without this, cold/warm/noop trivially
# pass (nothing cached, nothing to compare) and the false-hit bug
# class on relocate goes untested.
KACHE_CACHE_EXECUTABLES = "1"

[commands]
build = "cargo build --release"
clean = "rm -rf target"

[verify]
run = "./target/release/out-dir-runtime"
expected_stdout_contains = ["hello from build.rs"]

[assertions.cold]
min_entries_after = 1

[assertions.warm]
min_hits = 1

[assertions.noop]
should_not_recompile = true
recompile_marker = "Compiling"

# Relocate must MISS for THIS specific crate, not hit. Cache key
# must include OUT_DIR as absolute so different worktrees produce
# different keys and the build re-runs (writing data.txt to the
# new OUT_DIR).
#
# Why per-crate (`min_misses_per_crate`) instead of aggregate
# (`min_misses`): the build graph also compiles `build_script_build`
# which legitimately hits on relocate (its own compilation doesn't
# depend on OUT_DIR). An aggregate `min_misses = 1` would pass
# even if `out_dir_runtime` falsely hit while `build_script_build`
# happened to miss for an unrelated reason. Per-crate pins the
# contract to the SPECIFIC crate this fixture was built to test.
#
# The `verify` step (separate from this assertion block, applied to
# every phase) plus the post-relocate wipe of fixture.dir/target are
# the belt-and-braces backup: even if metrics looked right, a false
# hit's binary would try to read from the wiped original path and
# fail at runtime.
[assertions.relocate]
min_misses_per_crate = { "out_dir_runtime" = 1 }
