# kache-e2e fixture: minimal C++ project built with make.
#
# C/C++ caching is live (see c-hello for the full rationale). The C++
# side additionally stresses the preprocessor cache key through real
# STL headers — std::vector / std::string_view / template
# instantiation all expand via `cc -E -P`, so the key reflects the
# full transitive include of `<vector>`, `<string_view>`, etc.

name = "e2e-cpp-hello"
tags = ["suite:e2e", "lang:cpp"]

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

[env]
CXX = "$KACHE c++"

[commands]
build = "make"
clean = "make clean"

[verify]
run = "./build/foo"
expected_stdout_contains = ["hello from bar", "sum=15"]

# Differential test: the cache-restored object must be byte-identical
# to the cold build's real-compiler output. See c-hello for rationale.
[diff]
artifacts = ["build/foo.o"]

# Invalidation test: relocate AND edit bar.hpp's greeting — the changed
# source must force a recompile (cache miss), never a stale hit.
[modify]
file = "src/bar.hpp"
find = "hello from bar"
replace = "edited greeting"

# Cold: the `-c` compile of foo.cpp is a miss → one cache entry.
# Op-counts: one preprocessor run (cache key) + one compiler run.
[assertions.cold]
min_entries_after = 1
max_compiler_runs = 1
max_preprocessor_runs = 1

# Warm: clean + rebuild → the compile hits.
# `max_compiler_runs = 0` proves the hit skipped the compile; the
# preprocessor still runs once to derive the key.
[assertions.warm]
min_hits = 1
max_compiler_runs = 0
max_preprocessor_runs = 1

[assertions.noop]
should_not_recompile = false

# Relocate: preprocessor-expansion cache key is path-independent →
# the relocated build's compile hits.
[assertions.relocate]
min_hits = 1
max_compiler_runs = 0
max_preprocessor_runs = 1

# Relocate-modified: relocated AND edited — the changed source MUST
# miss and recompile.
[assertions.relocate-modified]
min_misses = 1
max_compiler_runs = 1
max_preprocessor_runs = 1
