fold/tests/unit/test_production_reachability.cpp
trvon fd4a68dfff
test: add coverage for project graph, pcode encoder, and detector behaviors
Unit + integration coverage for the session's work: incremental project
graph/index, pcode encoder, unreachable/dead-branch, equiv/cross-TU,
div-by-zero/sign-conversion/int-truncation, use-after-free, quality-precision,
scan-router profile, source TU runner, detector plan, and the eval fixtures.
2026-08-16 16:46:07 -06:00

368 lines
16 KiB
C++

#include <algorithm>
#include <catch2/catch_test_macros.hpp>
#include <chrono>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
#include "fold/analysis/production_reachability.hpp"
#include "fold/cli/production_reachability_findings.hpp"
#include "fold/config/configuration_profile.hpp"
#include "fold/ir/call_graph.hpp"
#include "fold/ir/config_guard.hpp"
#include "fold/report/finding_json.hpp"
namespace {
using fold::analysis::ProductionReachabilityClass;
using fold::analysis::ProductionReachabilityRecord;
using fold::analysis::ProductionRootInventory;
using fold::analysis::ProductionRootKind;
using fold::analysis::ProductionRootRecord;
ProductionRootRecord root(std::string id, bool production = true, bool conditional = false) {
return {
.function_usr = id,
.qualified_name = id,
.source_file = production ? "/src/app.cpp" : "/tests/app_test.cpp",
.kind = production ? ProductionRootKind::Main : ProductionRootKind::TestEntry,
.evidence_file = production ? "/src/app.cpp" : "/tests/app_test.cpp",
.evidence_line = 1,
.evidence_column = 1,
.production = production,
.conditional = conditional,
};
}
void addFunction(fold::ir::ProjectCallGraph& graph, const std::string& id, bool escapes = false) {
graph.addFunction(id, id, "/src/app.cpp", true, "body-" + id, escapes);
}
const ProductionReachabilityRecord&
findRecord(const fold::analysis::ProductionReachabilityResult& result, const std::string& id) {
const auto found =
std::ranges::find(result.records, id, &ProductionReachabilityRecord::function_usr);
REQUIRE(found != result.records.end());
return *found;
}
fold::SolverBudget budget() {
fold::SolverBudget result;
result.per_query = std::chrono::milliseconds(100);
result.total = std::chrono::milliseconds(2000);
return result;
}
fold::index::GuardedCallsiteRecord guarded(const std::string& caller, const std::string& callee,
const fold::ir::ConfigGuard& guard,
std::string suffix = "1") {
return {
.callsite_id = caller + "-" + callee + "-" + suffix,
.caller_usr = caller,
.direct_target_usr = callee,
.edge_kind = fold::index::EdgeKind::Direct,
.guard = guard,
};
}
} // namespace
TEST_CASE("closed-world reachability separates production test and unreachable functions",
"[production-reachability]") {
fold::ir::ProjectCallGraph graph;
for (const auto* id : {"main", "live", "test_main", "test_helper", "dead"})
addFunction(graph, id);
graph.addCallEdge("main", "live");
graph.addCallEdge("test_main", "test_helper");
ProductionRootInventory roots;
roots.production_roots.push_back(root("main"));
roots.test_roots.push_back(root("test_main", false));
const auto result =
fold::analysis::classifyProductionReachability(graph, roots, {}, nullptr, budget());
CHECK(findRecord(result, "main").classification ==
ProductionReachabilityClass::ProductionReachable);
const auto& live = findRecord(result, "live");
CHECK(live.classification == ProductionReachabilityClass::ProductionReachable);
CHECK(live.witness == std::vector<std::string>{"main", "live"});
CHECK(findRecord(result, "test_helper").classification ==
ProductionReachabilityClass::TestOnly);
const auto& dead = findRecord(result, "dead");
CHECK(dead.classification == ProductionReachabilityClass::UnreachableProduction);
CHECK(dead.removal_candidate);
CHECK(result.stats.removal_candidates == 1);
CHECK(result.stats.solver_queries == 0);
const auto findings = fold::cli::makeProductionReachabilityFindings(result);
const auto dead_finding = std::ranges::find_if(findings, [](const auto& finding) {
return finding.evidence.reachability &&
finding.evidence.reachability->classification == "unreachable-production";
});
REQUIRE(dead_finding != findings.end());
REQUIRE(dead_finding->evidence.reachability);
CHECK(dead_finding->evidence.reachability->removal_candidate);
CHECK(dead_finding->evidence.path_trace.empty());
REQUIRE(dead_finding->evidence.quality);
CHECK(dead_finding->evidence.quality->member_ids.empty());
const auto round_trip = fold::findingFromJson(fold::findingToJson(*dead_finding));
CHECK(round_trip.kind == fold::FindingKind::ProductionReachability);
REQUIRE(round_trip.evidence.reachability);
CHECK(round_trip.evidence.reachability->classification == "unreachable-production");
CHECK(round_trip.evidence.reachability->removal_candidate);
}
TEST_CASE("incomplete evidence and conditional roots cannot prove production unreachability",
"[production-reachability][fail-closed]") {
fold::ir::ProjectCallGraph graph;
addFunction(graph, "main");
addFunction(graph, "maybe");
addFunction(graph, "escaped", true);
addFunction(graph, "escape_child");
addFunction(graph, "virtual");
graph.addCallEdge("escaped", "escape_child");
graph.addCallEdge("virtual", "maybe");
ProductionRootInventory roots;
roots.production_roots.push_back(root("main"));
roots.conditional_roots.push_back(root("virtual", true, true));
roots.closed_world = false;
roots.has_unresolved_dynamic_calls = true;
const auto result =
fold::analysis::classifyProductionReachability(graph, roots, {}, nullptr, budget());
CHECK(result.stats.solver_queries == 0);
for (const auto* id : {"maybe", "escaped", "escape_child", "virtual"}) {
const auto& record = findRecord(result, id);
CHECK(record.classification == ProductionReachabilityClass::ProductionReachable);
CHECK_FALSE(record.complete);
CHECK_FALSE(record.removal_candidate);
CHECK(std::ranges::find(record.blockers, "unresolved-dynamic-call") !=
record.blockers.end());
}
}
TEST_CASE("guards distinguish default-inactive and configuration-infeasible paths",
"[production-reachability][guards]") {
const auto parsed = fold::config::parseConfigurationProfile(nlohmann::json{
{"version", 1},
{"entrypoints", {"main"}},
{"assumptions", {{{"kind", "global"}, {"selector", "feature"}, {"value", false}}}},
});
REQUIRE(parsed.ok());
const auto& profile = *parsed.profile;
const std::string symbol = profile.assumptions().front().symbolId().value();
fold::ir::ProjectCallGraph graph;
for (const auto* id : {"main", "feature", "impossible"})
addFunction(graph, id);
graph.addCallEdge("main", "feature");
graph.addCallEdge("main", "impossible");
ProductionRootInventory roots;
roots.production_roots.push_back(root("main"));
const std::vector calls = {
guarded("main", "feature",
fold::ir::ConfigGuard::projected(fold::ir::ConfigGuardExpr::boolSymbol(symbol))),
guarded("main", "impossible",
fold::ir::ConfigGuard::projected(fold::ir::ConfigGuardExpr::boolean(false))),
};
const auto result =
fold::analysis::classifyProductionReachability(graph, roots, calls, &profile, budget());
const auto& feature = findRecord(result, "feature");
CHECK(feature.classification == ProductionReachabilityClass::DefaultInactive);
CHECK(feature.default_result == fold::analysis::ReachabilityProofResult::Unsat);
CHECK(feature.free_configuration_result == fold::analysis::ReachabilityProofResult::Sat);
CHECK_FALSE(feature.removal_candidate);
const auto& impossible = findRecord(result, "impossible");
CHECK(impossible.classification == ProductionReachabilityClass::ConfigurationInfeasible);
CHECK(impossible.free_configuration_result == fold::analysis::ReachabilityProofResult::Unsat);
CHECK_FALSE(impossible.removal_candidate);
}
TEST_CASE("guard constraints remain correlated across a path",
"[production-reachability][guards]") {
const auto parsed = fold::config::parseConfigurationProfile(nlohmann::json{
{"version", 1},
{"entrypoints", {"main"}},
{"assumptions", {{{"kind", "global"}, {"selector", "feature"}, {"value", true}}}},
});
REQUIRE(parsed.ok());
const auto& profile = *parsed.profile;
const auto enabled =
fold::ir::ConfigGuardExpr::boolSymbol(profile.assumptions().front().symbolId().value());
const auto disabled = fold::ir::ConfigGuardExpr::apply(fold::ir::ConfigGuardOp::Not, {enabled});
fold::ir::ProjectCallGraph graph;
for (const auto* id : {"main", "middle", "target"})
addFunction(graph, id);
graph.addCallEdge("main", "middle");
graph.addCallEdge("middle", "target");
ProductionRootInventory roots;
roots.production_roots.push_back(root("main"));
const std::vector calls = {
guarded("main", "middle", fold::ir::ConfigGuard::projected(enabled)),
guarded("middle", "target", fold::ir::ConfigGuard::projected(disabled)),
};
const auto result =
fold::analysis::classifyProductionReachability(graph, roots, calls, &profile, budget());
const auto& target = findRecord(result, "target");
CHECK(target.classification == ProductionReachabilityClass::ConfigurationInfeasible);
CHECK(target.free_configuration_result == fold::analysis::ReachabilityProofResult::Unsat);
CHECK_FALSE(target.removal_candidate);
CHECK(result.stats.solver_queries == 3);
}
TEST_CASE("equivalent guarded reachability is reused through unconditional chains",
"[production-reachability][guards][reuse]") {
const auto parsed = fold::config::parseConfigurationProfile(nlohmann::json{
{"version", 1},
{"entrypoints", {"main"}},
{"assumptions", {{{"kind", "global"}, {"selector", "feature"}, {"value", false}}}},
});
REQUIRE(parsed.ok());
const auto& profile = *parsed.profile;
const auto enabled =
fold::ir::ConfigGuardExpr::boolSymbol(profile.assumptions().front().symbolId().value());
fold::ir::ProjectCallGraph graph;
for (const auto* id : {"main", "gated", "middle", "target"})
addFunction(graph, id);
graph.addCallEdge("main", "gated");
graph.addCallEdge("gated", "middle");
graph.addCallEdge("middle", "target");
ProductionRootInventory roots;
roots.production_roots.push_back(root("main"));
const std::vector calls = {
guarded("main", "gated", fold::ir::ConfigGuard::projected(enabled)),
guarded("gated", "middle", fold::ir::ConfigGuard::unconditional()),
guarded("middle", "target", fold::ir::ConfigGuard::unconditional()),
};
const auto result =
fold::analysis::classifyProductionReachability(graph, roots, calls, &profile, budget());
CHECK(result.stats.solver_queries == 2);
for (const auto* id : {"gated", "middle", "target"}) {
CHECK(findRecord(result, id).classification ==
ProductionReachabilityClass::DefaultInactive);
}
CHECK(findRecord(result, "target").witness ==
std::vector<std::string>{"main", "gated", "middle", "target"});
const auto findings = fold::cli::makeProductionReachabilityFindings(result);
const auto target_finding = std::ranges::find_if(findings, [](const auto& finding) {
return finding.evidence.reachability &&
finding.evidence.reachability->witness ==
std::vector<std::string>{"main", "gated", "middle", "target"};
});
REQUIRE(target_finding != findings.end());
CHECK(target_finding->evidence.path_trace.empty());
REQUIRE(target_finding->evidence.quality);
CHECK(target_finding->evidence.quality->member_ids.empty());
const auto wire = fold::findingToJson(*target_finding);
CHECK(wire["evidence"]["path_trace"] ==
nlohmann::json::array({"main", "gated", "middle", "target"}));
CHECK(wire["evidence"]["quality"]["member_ids"] ==
nlohmann::json::array({"main", "gated", "middle", "target"}));
const auto limited = fold::analysis::classifyProductionReachability(
graph, roots, calls, &profile, budget(), {.max_witness_functions = 2});
CHECK(findRecord(limited, "target").witness == std::vector<std::string>{"middle", "target"});
CHECK(limited.stats.truncated_witnesses == 2);
}
TEST_CASE("multiple guarded callsites are alternatives rather than a conjunction",
"[production-reachability][guards]") {
const auto parsed = fold::config::parseConfigurationProfile(nlohmann::json{
{"version", 1},
{"entrypoints", {"main"}},
{"assumptions", {{{"kind", "global"}, {"selector", "feature"}, {"value", false}}}},
});
REQUIRE(parsed.ok());
const auto& profile = *parsed.profile;
const auto symbol = profile.assumptions().front().symbolId().value();
const auto enabled = fold::ir::ConfigGuardExpr::boolSymbol(symbol);
const auto disabled = fold::ir::ConfigGuardExpr::apply(fold::ir::ConfigGuardOp::Not, {enabled});
fold::ir::ProjectCallGraph graph;
addFunction(graph, "main");
addFunction(graph, "target");
graph.addCallEdge("main", "target");
ProductionRootInventory roots;
roots.production_roots.push_back(root("main"));
const std::vector calls = {
guarded("main", "target", fold::ir::ConfigGuard::projected(enabled), "enabled"),
guarded("main", "target", fold::ir::ConfigGuard::projected(disabled), "disabled"),
};
const auto result =
fold::analysis::classifyProductionReachability(graph, roots, calls, &profile, budget());
CHECK(findRecord(result, "target").classification ==
ProductionReachabilityClass::ProductionReachable);
auto reversed_calls = calls;
std::ranges::reverse(reversed_calls);
const auto reversed = fold::analysis::classifyProductionReachability(
graph, roots, reversed_calls, &profile, budget());
CHECK(findRecord(reversed, "target").classification ==
findRecord(result, "target").classification);
CHECK(findRecord(reversed, "target").witness == findRecord(result, "target").witness);
}
TEST_CASE("recursive SCCs require a rooted incoming path", "[production-reachability][scc]") {
fold::ir::ProjectCallGraph graph;
for (const auto* id : {"main", "a", "b"})
addFunction(graph, id);
graph.addCallEdge("a", "b");
graph.addCallEdge("b", "a");
ProductionRootInventory roots;
roots.production_roots.push_back(root("main"));
const auto disconnected =
fold::analysis::classifyProductionReachability(graph, roots, {}, nullptr, budget());
CHECK(findRecord(disconnected, "a").classification ==
ProductionReachabilityClass::UnreachableProduction);
CHECK(findRecord(disconnected, "b").removal_candidate);
graph.addCallEdge("main", "a");
const auto connected =
fold::analysis::classifyProductionReachability(graph, roots, {}, nullptr, budget());
CHECK(findRecord(connected, "a").classification ==
ProductionReachabilityClass::ProductionReachable);
CHECK(findRecord(connected, "b").classification ==
ProductionReachabilityClass::ProductionReachable);
}
TEST_CASE("limits and unknown guards fail closed", "[production-reachability][fail-closed]") {
fold::ir::ProjectCallGraph graph;
addFunction(graph, "main");
addFunction(graph, "target");
graph.addCallEdge("main", "target");
ProductionRootInventory roots;
roots.production_roots.push_back(root("main"));
auto limited = fold::analysis::classifyProductionReachability(graph, roots, {}, nullptr,
budget(), {.max_functions = 1});
CHECK_FALSE(limited.complete);
CHECK_FALSE(findRecord(limited, "target").removal_candidate);
const std::vector calls = {
guarded("main", "target", fold::ir::ConfigGuard::unknown()),
};
const auto unknown =
fold::analysis::classifyProductionReachability(graph, roots, calls, nullptr, budget());
const auto& target = findRecord(unknown, "target");
CHECK(target.classification == ProductionReachabilityClass::ProductionReachable);
CHECK_FALSE(target.complete);
CHECK_FALSE(target.removal_candidate);
CHECK(std::ranges::find(target.blockers, "unknown-guard") != target.blockers.end());
auto exhausted_budget = budget();
exhausted_budget.total = std::chrono::milliseconds(0);
const auto exhausted = fold::analysis::classifyProductionReachability(
graph, roots, calls, nullptr, exhausted_budget);
const auto& exhausted_target = findRecord(exhausted, "target");
CHECK(exhausted_target.classification == ProductionReachabilityClass::ProductionReachable);
CHECK_FALSE(exhausted_target.complete);
CHECK_FALSE(exhausted_target.removal_candidate);
CHECK(exhausted.stats.solver_timeouts > 0);
}