Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca_bench_json_test.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11*/
12
24#include <cstddef>
25#include <map>
26#include <sstream>
27#include <string>
28#include <variant>
29
30#include <gtest/gtest.h>
31
33
36
37namespace
38{
39
40// Minimal but strict JSON reader: objects, strings and numbers are enough for
41// the benchmark schema. A leftover non-whitespace tail makes parsing fail, so
42// a malformed trailing byte cannot slip through.
43struct Json_Value
44{
45 std::variant<std::string, double> data;
46
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); }
51};
52
53class Json_Parser
54{
55public:
56 explicit Json_Parser(std::string text) : text_(std::move(text)) {}
57
58 // Parse a single object and assert the whole string was consumed.
59 bool parse_object(std::map<std::string, Json_Value> &out)
60 {
61 skip_ws();
62 if (not eat('{'))
63 return false;
64 skip_ws();
65 if (peek() == '}')
66 {
67 ++pos_;
68 return tail_clean();
69 }
70 while (true)
71 {
72 skip_ws();
73 std::string key;
74 if (not parse_string(key))
75 return false;
76 skip_ws();
77 if (not eat(':'))
78 return false;
79 Json_Value value;
80 if (not parse_value(value))
81 return false;
82 out.emplace(std::move(key), std::move(value));
83 skip_ws();
84 if (eat(','))
85 continue;
86 if (eat('}'))
87 break;
88 return false;
89 }
90 return tail_clean();
91 }
92
93private:
94 std::string text_;
95 std::size_t pos_ = 0;
96
97 char peek() const { return pos_ < text_.size() ? text_[pos_] : '\0'; }
98 void skip_ws()
99 {
100 while (pos_ < text_.size()
101 and (text_[pos_] == ' ' or text_[pos_] == '\t'
102 or text_[pos_] == '\n' or text_[pos_] == '\r'))
103 ++pos_;
104 }
105 bool eat(const char c)
106 {
107 if (peek() == c)
108 {
109 ++pos_;
110 return true;
111 }
112 return false;
113 }
114 bool tail_clean()
115 {
116 skip_ws();
117 return pos_ == text_.size();
118 }
119
120 bool parse_string(std::string &out)
121 {
122 if (not eat('"'))
123 return false;
124 out.clear();
125 while (pos_ < text_.size())
126 {
127 const char c = text_[pos_++];
128 if (c == '"')
129 return true;
130 if (c == '\\')
131 {
132 if (pos_ >= text_.size())
133 return false;
134 out.push_back(text_[pos_++]);
135 }
136 else
137 out.push_back(c);
138 }
139 return false; // unterminated string.
140 }
141
142 bool parse_value(Json_Value &out)
143 {
144 skip_ws();
145 if (peek() == '"')
146 {
147 std::string s;
148 if (not parse_string(s))
149 return false;
150 out.data = std::move(s);
151 return true;
152 }
153 const std::size_t start = pos_;
154 if (peek() == '-' or peek() == '+')
155 ++pos_;
156 bool any = false;
157 while (pos_ < text_.size())
158 {
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 == '-')
162 {
163 ++pos_;
164 any = true;
165 }
166 else
167 break;
168 }
169 if (not any)
170 return false;
171 out.data = std::stod(text_.substr(start, pos_ - start));
172 return true;
173 }
174};
175
176std::map<std::string, Json_Value> emit_and_parse(const Bench_Result &result)
177{
178 std::ostringstream os;
179 emit_json(result, 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;
186 return obj;
187}
188
189} // namespace
190
191TEST(CaBenchJson, EmitsWellFormedSchema)
192{
194 r.name = "gol_1024";
195 r.wall_ns = 3377500712ull;
196 r.cells_per_sec = 31045914.995;
197 const auto obj = emit_and_parse(r);
198
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);
205
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());
216}
217
218TEST(CaBenchJson, HandlesZeroAndLargeValues)
219{
220 Bench_Result zero;
221 zero.name = "edge_zero";
222 zero.wall_ns = 0;
223 zero.cells_per_sec = 0.0;
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);
228
229 Bench_Result big;
230 big.name = "edge_big";
231 big.wall_ns = 9000000000000ull;
232 big.cells_per_sec = 4.26843e8;
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);
236}
237
238TEST(CaBenchJson, CpuStringIsJsonSafe)
239{
240 // The live CPU string must never contain raw quotes, commas or backslashes
241 // that would corrupt the single-line JSON record.
242 const std::string cpu = Aleph::CA::Bench::cpu_string();
243 EXPECT_EQ(cpu.find('"'), std::string::npos);
244 EXPECT_EQ(cpu.find(','), std::string::npos);
245 EXPECT_EQ(cpu.find('\\'), std::string::npos);
246}
Shared harness for the cellular-automata performance-gate anchors.
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
#define TEST(name)
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.
STL namespace.
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.
gsl_rng * r