30#include <gtest/gtest.h>
45 std::variant<std::string, double> data;
47 [[nodiscard]]
bool is_string()
const {
return data.index() == 0; }
48 [[nodiscard]]
bool is_number()
const {
return data.index() == 1; }
49 [[nodiscard]]
const std::string &str()
const {
return std::get<0>(data); }
50 [[nodiscard]]
double num()
const {
return std::get<1>(data); }
56 explicit Json_Parser(std::string text) : text_(
std::move(text)) {}
59 bool parse_object(std::map<std::string, Json_Value> &
out)
74 if (not parse_string(key))
80 if (not parse_value(
value))
82 out.emplace(std::move(key), std::move(
value));
97 char peek()
const {
return pos_ < text_.size() ? text_[pos_] :
'\0'; }
100 while (pos_ < text_.size()
101 and (text_[pos_] ==
' ' or text_[pos_] ==
'\t'
102 or text_[pos_] ==
'\n' or text_[pos_] ==
'\r'))
105 bool eat(
const char c)
117 return pos_ == text_.size();
120 bool parse_string(std::string &
out)
125 while (pos_ < text_.size())
127 const char c = text_[pos_++];
132 if (pos_ >= text_.size())
134 out.push_back(text_[pos_++]);
142 bool parse_value(Json_Value &
out)
148 if (not parse_string(s))
150 out.data = std::move(s);
153 const std::size_t start = pos_;
154 if (peek() ==
'-' or peek() ==
'+')
157 while (pos_ < text_.size())
159 const char c = text_[pos_];
160 if ((c >=
'0' and c <=
'9') or c ==
'.' or c ==
'e' or c ==
'E'
161 or c ==
'+' or c ==
'-')
171 out.data = std::stod(text_.substr(start, pos_ - start));
176std::map<std::string, Json_Value> emit_and_parse(
const Bench_Result &result)
178 std::ostringstream os;
180 std::string line = os.str();
181 EXPECT_FALSE(line.empty());
182 EXPECT_EQ(line.back(),
'\n');
183 Json_Parser parser(line);
184 std::map<std::string, Json_Value> obj;
185 EXPECT_TRUE(parser.parse_object(obj)) <<
"not well-formed JSON: " << line;
191TEST(CaBenchJson, EmitsWellFormedSchema)
195 r.wall_ns = 3377500712ull;
196 r.cells_per_sec = 31045914.995;
197 const auto obj = emit_and_parse(
r);
199 ASSERT_EQ(obj.count(
"name"), 1u);
200 ASSERT_EQ(obj.count(
"wall_ns"), 1u);
201 ASSERT_EQ(obj.count(
"cells_per_sec"), 1u);
202 ASSERT_EQ(obj.count(
"compiler"), 1u);
203 ASSERT_EQ(obj.count(
"cpu"), 1u);
204 EXPECT_EQ(obj.size(), 5u);
206 EXPECT_TRUE(obj.at(
"name").is_string());
207 EXPECT_EQ(obj.at(
"name").str(),
"gol_1024");
208 EXPECT_TRUE(obj.at(
"wall_ns").is_number());
209 EXPECT_DOUBLE_EQ(obj.at(
"wall_ns").num(), 3377500712.0);
210 EXPECT_TRUE(obj.at(
"cells_per_sec").is_number());
211 EXPECT_NEAR(obj.at(
"cells_per_sec").num(), 31045914.995, 1.0);
212 EXPECT_TRUE(obj.at(
"compiler").is_string());
213 EXPECT_FALSE(obj.at(
"compiler").str().empty());
214 EXPECT_TRUE(obj.at(
"cpu").is_string());
215 EXPECT_FALSE(obj.at(
"cpu").str().empty());
218TEST(CaBenchJson, HandlesZeroAndLargeValues)
221 zero.
name =
"edge_zero";
224 const auto a = emit_and_parse(zero);
225 EXPECT_EQ(a.at(
"name").str(),
"edge_zero");
226 EXPECT_DOUBLE_EQ(a.at(
"wall_ns").num(), 0.0);
227 EXPECT_DOUBLE_EQ(a.at(
"cells_per_sec").num(), 0.0);
230 big.
name =
"edge_big";
231 big.
wall_ns = 9000000000000ull;
233 const auto b = emit_and_parse(big);
234 EXPECT_DOUBLE_EQ(b.at(
"wall_ns").num(), 9000000000000.0);
235 EXPECT_GT(b.at(
"cells_per_sec").num(), 0.0);
238TEST(CaBenchJson, CpuStringIsJsonSafe)
243 EXPECT_EQ(cpu.find(
'"'), std::string::npos);
244 EXPECT_EQ(cpu.find(
','), std::string::npos);
245 EXPECT_EQ(cpu.find(
'\\'), std::string::npos);
Shared harness for the cellular-automata performance-gate anchors.
size_t size_t int32_t value
size_t size_t int32_t * out
void emit_json(const Bench_Result &result, std::ostream &out=std::cout)
Print one benchmark result as a single structured JSON object.
std::string cpu_string()
Read the CPU model name advertised by the host.
Outcome of one measured anchor benchmark.
std::string name
stable benchmark identifier.
double cells_per_sec
processed cells divided by the median run time.
std::uint64_t wall_ns
median wall-clock time of one run, nanoseconds.