#include #include #include #include "fold/symbolic/cost_summary.hpp" TEST_CASE("cost summary enum names are stable for JSON sidecars", "[cost-summary]") { using namespace fold::symbolic; REQUIRE(std::string(costSummaryConfidenceName(CostSummaryConfidence::Proved)) == "proved"); REQUIRE(std::string(costSummaryConfidenceName(CostSummaryConfidence::Latent)) == "latent"); REQUIRE(std::string(costSummaryConfidenceName(CostSummaryConfidence::Heuristic)) == "heuristic"); REQUIRE(std::string(costSummaryConfidenceName(CostSummaryConfidence::Syntactic)) == "syntactic"); REQUIRE(costSummaryConfidenceFromName("not-a-confidence") == CostSummaryConfidence::Unknown); REQUIRE(std::string(costBoundKindName(CostBoundKind::Constant)) == "constant"); REQUIRE(std::string(costBoundKindName(CostBoundKind::Linear)) == "linear"); REQUIRE(std::string(costBoundKindName(CostBoundKind::Polynomial)) == "polynomial"); REQUIRE(std::string(costBoundKindName(CostBoundKind::UnboundedCandidate)) == "unbounded-candidate"); REQUIRE(costBoundKindFromName("not-a-bound") == CostBoundKind::Unknown); REQUIRE(std::string(costSecurityRelevanceName(CostSecurityRelevance::None)) == "none"); REQUIRE(std::string(costSecurityRelevanceName(CostSecurityRelevance::InputDependent)) == "input-dependent"); REQUIRE(std::string(costSecurityRelevanceName(CostSecurityRelevance::LatentDos)) == "latent-dos"); REQUIRE(std::string(costSecurityRelevanceName(CostSecurityRelevance::PotentialDos)) == "potential-dos"); REQUIRE(costSecurityRelevanceFromName("not-a-relevance") == CostSecurityRelevance::Unknown); } TEST_CASE("cost summary report round-trips source function and loop data", "[cost-summary]") { using namespace fold::symbolic; using namespace fold::symbolic::cost_json_keys; CostInputSymbol n{.name = "n", .subject = "param:0", .symbol_kind = "parameter"}; LoopCostSummary loop; loop.id = "loop:example.cpp:4:5"; loop.kind = "for"; loop.location = fold::ir::SourceLoc{.file = "example.cpp", .line = 4, .column = 5}; loop.nesting_depth = 1; loop.guard_expression = "i < n"; loop.induction_variable = "i"; loop.bound_kind = CostBoundKind::Linear; loop.bound_expression = "max(n, 0)"; loop.confidence = CostSummaryConfidence::Proved; loop.precision = fold::EncodingPrecision::Exact; loop.security_relevance = CostSecurityRelevance::InputDependent; loop.security_reasons = {"loop-bound-depends-on-input-symbol"}; loop.calls = {"work"}; loop.input_symbols = {n}; FunctionCostSummary summary; summary.backend = "source"; summary.function = "f"; summary.location = fold::ir::SourceLoc{.file = "example.cpp", .line = 2, .column = 1}; summary.cost_expression = "O(n)"; summary.confidence = CostSummaryConfidence::Proved; summary.precision = fold::EncodingPrecision::Exact; summary.security_relevance = CostSecurityRelevance::InputDependent; summary.security_reasons = {"loop-bound-depends-on-input-symbol"}; summary.input_symbols = {n}; summary.loops = {loop}; CostSummaryReport report; report.summaries = {summary}; const auto j = toJson(report); REQUIRE(j[kVersion] == 1); REQUIRE(j[kSummaries].is_array()); REQUIRE(j[kSummaries][0][kBackend] == "source"); REQUIRE(j[kSummaries][0][kFunction] == "f"); REQUIRE(j[kSummaries][0][kLocation][kFile] == "example.cpp"); REQUIRE(j[kSummaries][0][kSecurityRelevance] == "input-dependent"); REQUIRE(j[kSummaries][0][kLoops][0][kBoundKind] == "linear"); REQUIRE(j[kSummaries][0][kLoops][0][kSecurityRelevance] == "input-dependent"); REQUIRE(j[kSummaries][0][kLoops][0][kInputSymbols][0][kSubject] == "param:0"); const auto parsed = costSummaryReportFromJson(j); REQUIRE(parsed == report); } TEST_CASE("cost summary parsing is tolerant of absent optional arrays", "[cost-summary]") { using namespace fold::symbolic; using namespace fold::symbolic::cost_json_keys; const nlohmann::json j = { {kVersion, 7}, {kSummaries, nlohmann::json::array({{{kFunction, "g"}, {kLocation, {{kFile, "g.cpp"}, {kLine, 10}, {kColumn, 3}}}, {kCostExpression, "unknown"}, {kConfidence, "heuristic"}, {kPrecision, "unsupported-node"}}})}}; const auto parsed = costSummaryReportFromJson(j); REQUIRE(parsed.version == 7); REQUIRE(parsed.summaries.size() == 1); REQUIRE(parsed.summaries[0].backend == "source"); REQUIRE(parsed.summaries[0].function == "g"); REQUIRE(parsed.summaries[0].location.file == "g.cpp"); REQUIRE(parsed.summaries[0].confidence == CostSummaryConfidence::Heuristic); REQUIRE(parsed.summaries[0].precision == fold::EncodingPrecision::UnsupportedNode); REQUIRE(parsed.summaries[0].security_relevance == CostSecurityRelevance::None); REQUIRE(parsed.summaries[0].security_reasons.empty()); REQUIRE(parsed.summaries[0].input_symbols.empty()); REQUIRE(parsed.summaries[0].loops.empty()); }