112 lines
5.2 KiB
C++
112 lines
5.2 KiB
C++
#include <catch2/catch_test_macros.hpp>
|
|
#include <nlohmann/json.hpp>
|
|
#include <string>
|
|
|
|
#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());
|
|
}
|