1a8c51b3fSopenharmony_ci#include <cstdio>
2a8c51b3fSopenharmony_ci#include <cstring>
3a8c51b3fSopenharmony_ci#include <fstream>
4a8c51b3fSopenharmony_ci#include <iostream>
5a8c51b3fSopenharmony_ci#include <map>
6a8c51b3fSopenharmony_ci#include <memory>
7a8c51b3fSopenharmony_ci#include <random>
8a8c51b3fSopenharmony_ci#include <sstream>
9a8c51b3fSopenharmony_ci#include <streambuf>
10a8c51b3fSopenharmony_ci
11a8c51b3fSopenharmony_ci#include "../src/benchmark_api_internal.h"
12a8c51b3fSopenharmony_ci#include "../src/check.h"  // NOTE: check.h is for internal use only!
13a8c51b3fSopenharmony_ci#include "../src/log.h"    // NOTE: log.h is for internal use only
14a8c51b3fSopenharmony_ci#include "../src/re.h"     // NOTE: re.h is for internal use only
15a8c51b3fSopenharmony_ci#include "output_test.h"
16a8c51b3fSopenharmony_ci
17a8c51b3fSopenharmony_ci// ========================================================================= //
18a8c51b3fSopenharmony_ci// ------------------------------ Internals -------------------------------- //
19a8c51b3fSopenharmony_ci// ========================================================================= //
20a8c51b3fSopenharmony_cinamespace internal {
21a8c51b3fSopenharmony_cinamespace {
22a8c51b3fSopenharmony_ci
23a8c51b3fSopenharmony_ciusing TestCaseList = std::vector<TestCase>;
24a8c51b3fSopenharmony_ci
25a8c51b3fSopenharmony_ci// Use a vector because the order elements are added matters during iteration.
26a8c51b3fSopenharmony_ci// std::map/unordered_map don't guarantee that.
27a8c51b3fSopenharmony_ci// For example:
28a8c51b3fSopenharmony_ci//  SetSubstitutions({{"%HelloWorld", "Hello"}, {"%Hello", "Hi"}});
29a8c51b3fSopenharmony_ci//     Substitute("%HelloWorld") // Always expands to Hello.
30a8c51b3fSopenharmony_ciusing SubMap = std::vector<std::pair<std::string, std::string>>;
31a8c51b3fSopenharmony_ci
32a8c51b3fSopenharmony_ciTestCaseList& GetTestCaseList(TestCaseID ID) {
33a8c51b3fSopenharmony_ci  // Uses function-local statics to ensure initialization occurs
34a8c51b3fSopenharmony_ci  // before first use.
35a8c51b3fSopenharmony_ci  static TestCaseList lists[TC_NumID];
36a8c51b3fSopenharmony_ci  return lists[ID];
37a8c51b3fSopenharmony_ci}
38a8c51b3fSopenharmony_ci
39a8c51b3fSopenharmony_ciSubMap& GetSubstitutions() {
40a8c51b3fSopenharmony_ci  // Don't use 'dec_re' from header because it may not yet be initialized.
41a8c51b3fSopenharmony_ci  // clang-format off
42a8c51b3fSopenharmony_ci  static std::string safe_dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
43a8c51b3fSopenharmony_ci  static std::string time_re = "([0-9]+[.])?[0-9]+";
44a8c51b3fSopenharmony_ci  static std::string percentage_re = "[0-9]+[.][0-9]{2}";
45a8c51b3fSopenharmony_ci  static SubMap map = {
46a8c51b3fSopenharmony_ci      {"%float", "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?"},
47a8c51b3fSopenharmony_ci      // human-readable float
48a8c51b3fSopenharmony_ci      {"%hrfloat", "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?[kKMGTPEZYmunpfazy]?i?"},
49a8c51b3fSopenharmony_ci      {"%percentage", percentage_re},
50a8c51b3fSopenharmony_ci      {"%int", "[ ]*[0-9]+"},
51a8c51b3fSopenharmony_ci      {" %s ", "[ ]+"},
52a8c51b3fSopenharmony_ci      {"%time", "[ ]*" + time_re + "[ ]+ns"},
53a8c51b3fSopenharmony_ci      {"%console_report", "[ ]*" + time_re + "[ ]+ns [ ]*" + time_re + "[ ]+ns [ ]*[0-9]+"},
54a8c51b3fSopenharmony_ci      {"%console_percentage_report", "[ ]*" + percentage_re + "[ ]+% [ ]*" + percentage_re + "[ ]+% [ ]*[0-9]+"},
55a8c51b3fSopenharmony_ci      {"%console_us_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us [ ]*[0-9]+"},
56a8c51b3fSopenharmony_ci      {"%console_ms_report", "[ ]*" + time_re + "[ ]+ms [ ]*" + time_re + "[ ]+ms [ ]*[0-9]+"},
57a8c51b3fSopenharmony_ci      {"%console_s_report", "[ ]*" + time_re + "[ ]+s [ ]*" + time_re + "[ ]+s [ ]*[0-9]+"},
58a8c51b3fSopenharmony_ci      {"%console_time_only_report", "[ ]*" + time_re + "[ ]+ns [ ]*" + time_re + "[ ]+ns"},
59a8c51b3fSopenharmony_ci      {"%console_us_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us [ ]*[0-9]+"},
60a8c51b3fSopenharmony_ci      {"%console_us_time_only_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us"},
61a8c51b3fSopenharmony_ci      {"%csv_header",
62a8c51b3fSopenharmony_ci       "name,iterations,real_time,cpu_time,time_unit,bytes_per_second,"
63a8c51b3fSopenharmony_ci       "items_per_second,label,error_occurred,error_message"},
64a8c51b3fSopenharmony_ci      {"%csv_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,,,,,"},
65a8c51b3fSopenharmony_ci      {"%csv_us_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",us,,,,,"},
66a8c51b3fSopenharmony_ci      {"%csv_ms_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ms,,,,,"},
67a8c51b3fSopenharmony_ci      {"%csv_s_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",s,,,,,"},
68a8c51b3fSopenharmony_ci      {"%csv_bytes_report",
69a8c51b3fSopenharmony_ci       "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns," + safe_dec_re + ",,,,"},
70a8c51b3fSopenharmony_ci      {"%csv_items_report",
71a8c51b3fSopenharmony_ci       "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,," + safe_dec_re + ",,,"},
72a8c51b3fSopenharmony_ci      {"%csv_bytes_items_report",
73a8c51b3fSopenharmony_ci       "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns," + safe_dec_re +
74a8c51b3fSopenharmony_ci       "," + safe_dec_re + ",,,"},
75a8c51b3fSopenharmony_ci      {"%csv_label_report_begin", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,,,"},
76a8c51b3fSopenharmony_ci      {"%csv_label_report_end", ",,"}};
77a8c51b3fSopenharmony_ci  // clang-format on
78a8c51b3fSopenharmony_ci  return map;
79a8c51b3fSopenharmony_ci}
80a8c51b3fSopenharmony_ci
81a8c51b3fSopenharmony_cistd::string PerformSubstitutions(std::string source) {
82a8c51b3fSopenharmony_ci  SubMap const& subs = GetSubstitutions();
83a8c51b3fSopenharmony_ci  using SizeT = std::string::size_type;
84a8c51b3fSopenharmony_ci  for (auto const& KV : subs) {
85a8c51b3fSopenharmony_ci    SizeT pos;
86a8c51b3fSopenharmony_ci    SizeT next_start = 0;
87a8c51b3fSopenharmony_ci    while ((pos = source.find(KV.first, next_start)) != std::string::npos) {
88a8c51b3fSopenharmony_ci      next_start = pos + KV.second.size();
89a8c51b3fSopenharmony_ci      source.replace(pos, KV.first.size(), KV.second);
90a8c51b3fSopenharmony_ci    }
91a8c51b3fSopenharmony_ci  }
92a8c51b3fSopenharmony_ci  return source;
93a8c51b3fSopenharmony_ci}
94a8c51b3fSopenharmony_ci
95a8c51b3fSopenharmony_civoid CheckCase(std::stringstream& remaining_output, TestCase const& TC,
96a8c51b3fSopenharmony_ci               TestCaseList const& not_checks) {
97a8c51b3fSopenharmony_ci  std::string first_line;
98a8c51b3fSopenharmony_ci  bool on_first = true;
99a8c51b3fSopenharmony_ci  std::string line;
100a8c51b3fSopenharmony_ci  while (remaining_output.eof() == false) {
101a8c51b3fSopenharmony_ci    BM_CHECK(remaining_output.good());
102a8c51b3fSopenharmony_ci    std::getline(remaining_output, line);
103a8c51b3fSopenharmony_ci    if (on_first) {
104a8c51b3fSopenharmony_ci      first_line = line;
105a8c51b3fSopenharmony_ci      on_first = false;
106a8c51b3fSopenharmony_ci    }
107a8c51b3fSopenharmony_ci    for (const auto& NC : not_checks) {
108a8c51b3fSopenharmony_ci      BM_CHECK(!NC.regex->Match(line))
109a8c51b3fSopenharmony_ci          << "Unexpected match for line \"" << line << "\" for MR_Not regex \""
110a8c51b3fSopenharmony_ci          << NC.regex_str << "\""
111a8c51b3fSopenharmony_ci          << "\n    actual regex string \"" << TC.substituted_regex << "\""
112a8c51b3fSopenharmony_ci          << "\n    started matching near: " << first_line;
113a8c51b3fSopenharmony_ci    }
114a8c51b3fSopenharmony_ci    if (TC.regex->Match(line)) return;
115a8c51b3fSopenharmony_ci    BM_CHECK(TC.match_rule != MR_Next)
116a8c51b3fSopenharmony_ci        << "Expected line \"" << line << "\" to match regex \"" << TC.regex_str
117a8c51b3fSopenharmony_ci        << "\""
118a8c51b3fSopenharmony_ci        << "\n    actual regex string \"" << TC.substituted_regex << "\""
119a8c51b3fSopenharmony_ci        << "\n    started matching near: " << first_line;
120a8c51b3fSopenharmony_ci  }
121a8c51b3fSopenharmony_ci  BM_CHECK(remaining_output.eof() == false)
122a8c51b3fSopenharmony_ci      << "End of output reached before match for regex \"" << TC.regex_str
123a8c51b3fSopenharmony_ci      << "\" was found"
124a8c51b3fSopenharmony_ci      << "\n    actual regex string \"" << TC.substituted_regex << "\""
125a8c51b3fSopenharmony_ci      << "\n    started matching near: " << first_line;
126a8c51b3fSopenharmony_ci}
127a8c51b3fSopenharmony_ci
128a8c51b3fSopenharmony_civoid CheckCases(TestCaseList const& checks, std::stringstream& output) {
129a8c51b3fSopenharmony_ci  std::vector<TestCase> not_checks;
130a8c51b3fSopenharmony_ci  for (size_t i = 0; i < checks.size(); ++i) {
131a8c51b3fSopenharmony_ci    const auto& TC = checks[i];
132a8c51b3fSopenharmony_ci    if (TC.match_rule == MR_Not) {
133a8c51b3fSopenharmony_ci      not_checks.push_back(TC);
134a8c51b3fSopenharmony_ci      continue;
135a8c51b3fSopenharmony_ci    }
136a8c51b3fSopenharmony_ci    CheckCase(output, TC, not_checks);
137a8c51b3fSopenharmony_ci    not_checks.clear();
138a8c51b3fSopenharmony_ci  }
139a8c51b3fSopenharmony_ci}
140a8c51b3fSopenharmony_ci
141a8c51b3fSopenharmony_ciclass TestReporter : public benchmark::BenchmarkReporter {
142a8c51b3fSopenharmony_ci public:
143a8c51b3fSopenharmony_ci  TestReporter(std::vector<benchmark::BenchmarkReporter*> reps)
144a8c51b3fSopenharmony_ci      : reporters_(std::move(reps)) {}
145a8c51b3fSopenharmony_ci
146a8c51b3fSopenharmony_ci  bool ReportContext(const Context& context) override {
147a8c51b3fSopenharmony_ci    bool last_ret = false;
148a8c51b3fSopenharmony_ci    bool first = true;
149a8c51b3fSopenharmony_ci    for (auto rep : reporters_) {
150a8c51b3fSopenharmony_ci      bool new_ret = rep->ReportContext(context);
151a8c51b3fSopenharmony_ci      BM_CHECK(first || new_ret == last_ret)
152a8c51b3fSopenharmony_ci          << "Reports return different values for ReportContext";
153a8c51b3fSopenharmony_ci      first = false;
154a8c51b3fSopenharmony_ci      last_ret = new_ret;
155a8c51b3fSopenharmony_ci    }
156a8c51b3fSopenharmony_ci    (void)first;
157a8c51b3fSopenharmony_ci    return last_ret;
158a8c51b3fSopenharmony_ci  }
159a8c51b3fSopenharmony_ci
160a8c51b3fSopenharmony_ci  void ReportRuns(const std::vector<Run>& report) override {
161a8c51b3fSopenharmony_ci    for (auto rep : reporters_) rep->ReportRuns(report);
162a8c51b3fSopenharmony_ci  }
163a8c51b3fSopenharmony_ci  void Finalize() override {
164a8c51b3fSopenharmony_ci    for (auto rep : reporters_) rep->Finalize();
165a8c51b3fSopenharmony_ci  }
166a8c51b3fSopenharmony_ci
167a8c51b3fSopenharmony_ci private:
168a8c51b3fSopenharmony_ci  std::vector<benchmark::BenchmarkReporter*> reporters_;
169a8c51b3fSopenharmony_ci};
170a8c51b3fSopenharmony_ci}  // namespace
171a8c51b3fSopenharmony_ci
172a8c51b3fSopenharmony_ci}  // end namespace internal
173a8c51b3fSopenharmony_ci
174a8c51b3fSopenharmony_ci// ========================================================================= //
175a8c51b3fSopenharmony_ci// -------------------------- Results checking ----------------------------- //
176a8c51b3fSopenharmony_ci// ========================================================================= //
177a8c51b3fSopenharmony_ci
178a8c51b3fSopenharmony_cinamespace internal {
179a8c51b3fSopenharmony_ci
180a8c51b3fSopenharmony_ci// Utility class to manage subscribers for checking benchmark results.
181a8c51b3fSopenharmony_ci// It works by parsing the CSV output to read the results.
182a8c51b3fSopenharmony_ciclass ResultsChecker {
183a8c51b3fSopenharmony_ci public:
184a8c51b3fSopenharmony_ci  struct PatternAndFn : public TestCase {  // reusing TestCase for its regexes
185a8c51b3fSopenharmony_ci    PatternAndFn(const std::string& rx, ResultsCheckFn fn_)
186a8c51b3fSopenharmony_ci        : TestCase(rx), fn(std::move(fn_)) {}
187a8c51b3fSopenharmony_ci    ResultsCheckFn fn;
188a8c51b3fSopenharmony_ci  };
189a8c51b3fSopenharmony_ci
190a8c51b3fSopenharmony_ci  std::vector<PatternAndFn> check_patterns;
191a8c51b3fSopenharmony_ci  std::vector<Results> results;
192a8c51b3fSopenharmony_ci  std::vector<std::string> field_names;
193a8c51b3fSopenharmony_ci
194a8c51b3fSopenharmony_ci  void Add(const std::string& entry_pattern, const ResultsCheckFn& fn);
195a8c51b3fSopenharmony_ci
196a8c51b3fSopenharmony_ci  void CheckResults(std::stringstream& output);
197a8c51b3fSopenharmony_ci
198a8c51b3fSopenharmony_ci private:
199a8c51b3fSopenharmony_ci  void SetHeader_(const std::string& csv_header);
200a8c51b3fSopenharmony_ci  void SetValues_(const std::string& entry_csv_line);
201a8c51b3fSopenharmony_ci
202a8c51b3fSopenharmony_ci  std::vector<std::string> SplitCsv_(const std::string& line);
203a8c51b3fSopenharmony_ci};
204a8c51b3fSopenharmony_ci
205a8c51b3fSopenharmony_ci// store the static ResultsChecker in a function to prevent initialization
206a8c51b3fSopenharmony_ci// order problems
207a8c51b3fSopenharmony_ciResultsChecker& GetResultsChecker() {
208a8c51b3fSopenharmony_ci  static ResultsChecker rc;
209a8c51b3fSopenharmony_ci  return rc;
210a8c51b3fSopenharmony_ci}
211a8c51b3fSopenharmony_ci
212a8c51b3fSopenharmony_ci// add a results checker for a benchmark
213a8c51b3fSopenharmony_civoid ResultsChecker::Add(const std::string& entry_pattern,
214a8c51b3fSopenharmony_ci                         const ResultsCheckFn& fn) {
215a8c51b3fSopenharmony_ci  check_patterns.emplace_back(entry_pattern, fn);
216a8c51b3fSopenharmony_ci}
217a8c51b3fSopenharmony_ci
218a8c51b3fSopenharmony_ci// check the results of all subscribed benchmarks
219a8c51b3fSopenharmony_civoid ResultsChecker::CheckResults(std::stringstream& output) {
220a8c51b3fSopenharmony_ci  // first reset the stream to the start
221a8c51b3fSopenharmony_ci  {
222a8c51b3fSopenharmony_ci    auto start = std::stringstream::pos_type(0);
223a8c51b3fSopenharmony_ci    // clear before calling tellg()
224a8c51b3fSopenharmony_ci    output.clear();
225a8c51b3fSopenharmony_ci    // seek to zero only when needed
226a8c51b3fSopenharmony_ci    if (output.tellg() > start) output.seekg(start);
227a8c51b3fSopenharmony_ci    // and just in case
228a8c51b3fSopenharmony_ci    output.clear();
229a8c51b3fSopenharmony_ci  }
230a8c51b3fSopenharmony_ci  // now go over every line and publish it to the ResultsChecker
231a8c51b3fSopenharmony_ci  std::string line;
232a8c51b3fSopenharmony_ci  bool on_first = true;
233a8c51b3fSopenharmony_ci  while (output.eof() == false) {
234a8c51b3fSopenharmony_ci    BM_CHECK(output.good());
235a8c51b3fSopenharmony_ci    std::getline(output, line);
236a8c51b3fSopenharmony_ci    if (on_first) {
237a8c51b3fSopenharmony_ci      SetHeader_(line);  // this is important
238a8c51b3fSopenharmony_ci      on_first = false;
239a8c51b3fSopenharmony_ci      continue;
240a8c51b3fSopenharmony_ci    }
241a8c51b3fSopenharmony_ci    SetValues_(line);
242a8c51b3fSopenharmony_ci  }
243a8c51b3fSopenharmony_ci  // finally we can call the subscribed check functions
244a8c51b3fSopenharmony_ci  for (const auto& p : check_patterns) {
245a8c51b3fSopenharmony_ci    BM_VLOG(2) << "--------------------------------\n";
246a8c51b3fSopenharmony_ci    BM_VLOG(2) << "checking for benchmarks matching " << p.regex_str << "...\n";
247a8c51b3fSopenharmony_ci    for (const auto& r : results) {
248a8c51b3fSopenharmony_ci      if (!p.regex->Match(r.name)) {
249a8c51b3fSopenharmony_ci        BM_VLOG(2) << p.regex_str << " is not matched by " << r.name << "\n";
250a8c51b3fSopenharmony_ci        continue;
251a8c51b3fSopenharmony_ci      }
252a8c51b3fSopenharmony_ci      BM_VLOG(2) << p.regex_str << " is matched by " << r.name << "\n";
253a8c51b3fSopenharmony_ci      BM_VLOG(1) << "Checking results of " << r.name << ": ... \n";
254a8c51b3fSopenharmony_ci      p.fn(r);
255a8c51b3fSopenharmony_ci      BM_VLOG(1) << "Checking results of " << r.name << ": OK.\n";
256a8c51b3fSopenharmony_ci    }
257a8c51b3fSopenharmony_ci  }
258a8c51b3fSopenharmony_ci}
259a8c51b3fSopenharmony_ci
260a8c51b3fSopenharmony_ci// prepare for the names in this header
261a8c51b3fSopenharmony_civoid ResultsChecker::SetHeader_(const std::string& csv_header) {
262a8c51b3fSopenharmony_ci  field_names = SplitCsv_(csv_header);
263a8c51b3fSopenharmony_ci}
264a8c51b3fSopenharmony_ci
265a8c51b3fSopenharmony_ci// set the values for a benchmark
266a8c51b3fSopenharmony_civoid ResultsChecker::SetValues_(const std::string& entry_csv_line) {
267a8c51b3fSopenharmony_ci  if (entry_csv_line.empty()) return;  // some lines are empty
268a8c51b3fSopenharmony_ci  BM_CHECK(!field_names.empty());
269a8c51b3fSopenharmony_ci  auto vals = SplitCsv_(entry_csv_line);
270a8c51b3fSopenharmony_ci  BM_CHECK_EQ(vals.size(), field_names.size());
271a8c51b3fSopenharmony_ci  results.emplace_back(vals[0]);  // vals[0] is the benchmark name
272a8c51b3fSopenharmony_ci  auto& entry = results.back();
273a8c51b3fSopenharmony_ci  for (size_t i = 1, e = vals.size(); i < e; ++i) {
274a8c51b3fSopenharmony_ci    entry.values[field_names[i]] = vals[i];
275a8c51b3fSopenharmony_ci  }
276a8c51b3fSopenharmony_ci}
277a8c51b3fSopenharmony_ci
278a8c51b3fSopenharmony_ci// a quick'n'dirty csv splitter (eliminating quotes)
279a8c51b3fSopenharmony_cistd::vector<std::string> ResultsChecker::SplitCsv_(const std::string& line) {
280a8c51b3fSopenharmony_ci  std::vector<std::string> out;
281a8c51b3fSopenharmony_ci  if (line.empty()) return out;
282a8c51b3fSopenharmony_ci  if (!field_names.empty()) out.reserve(field_names.size());
283a8c51b3fSopenharmony_ci  size_t prev = 0, pos = line.find_first_of(','), curr = pos;
284a8c51b3fSopenharmony_ci  while (pos != line.npos) {
285a8c51b3fSopenharmony_ci    BM_CHECK(curr > 0);
286a8c51b3fSopenharmony_ci    if (line[prev] == '"') ++prev;
287a8c51b3fSopenharmony_ci    if (line[curr - 1] == '"') --curr;
288a8c51b3fSopenharmony_ci    out.push_back(line.substr(prev, curr - prev));
289a8c51b3fSopenharmony_ci    prev = pos + 1;
290a8c51b3fSopenharmony_ci    pos = line.find_first_of(',', pos + 1);
291a8c51b3fSopenharmony_ci    curr = pos;
292a8c51b3fSopenharmony_ci  }
293a8c51b3fSopenharmony_ci  curr = line.size();
294a8c51b3fSopenharmony_ci  if (line[prev] == '"') ++prev;
295a8c51b3fSopenharmony_ci  if (line[curr - 1] == '"') --curr;
296a8c51b3fSopenharmony_ci  out.push_back(line.substr(prev, curr - prev));
297a8c51b3fSopenharmony_ci  return out;
298a8c51b3fSopenharmony_ci}
299a8c51b3fSopenharmony_ci
300a8c51b3fSopenharmony_ci}  // end namespace internal
301a8c51b3fSopenharmony_ci
302a8c51b3fSopenharmony_cisize_t AddChecker(const std::string& bm_name, const ResultsCheckFn& fn) {
303a8c51b3fSopenharmony_ci  auto& rc = internal::GetResultsChecker();
304a8c51b3fSopenharmony_ci  rc.Add(bm_name, fn);
305a8c51b3fSopenharmony_ci  return rc.results.size();
306a8c51b3fSopenharmony_ci}
307a8c51b3fSopenharmony_ci
308a8c51b3fSopenharmony_ciint Results::NumThreads() const {
309a8c51b3fSopenharmony_ci  auto pos = name.find("/threads:");
310a8c51b3fSopenharmony_ci  if (pos == name.npos) return 1;
311a8c51b3fSopenharmony_ci  auto end = name.find('/', pos + 9);
312a8c51b3fSopenharmony_ci  std::stringstream ss;
313a8c51b3fSopenharmony_ci  ss << name.substr(pos + 9, end);
314a8c51b3fSopenharmony_ci  int num = 1;
315a8c51b3fSopenharmony_ci  ss >> num;
316a8c51b3fSopenharmony_ci  BM_CHECK(!ss.fail());
317a8c51b3fSopenharmony_ci  return num;
318a8c51b3fSopenharmony_ci}
319a8c51b3fSopenharmony_ci
320a8c51b3fSopenharmony_cidouble Results::NumIterations() const { return GetAs<double>("iterations"); }
321a8c51b3fSopenharmony_ci
322a8c51b3fSopenharmony_cidouble Results::GetTime(BenchmarkTime which) const {
323a8c51b3fSopenharmony_ci  BM_CHECK(which == kCpuTime || which == kRealTime);
324a8c51b3fSopenharmony_ci  const char* which_str = which == kCpuTime ? "cpu_time" : "real_time";
325a8c51b3fSopenharmony_ci  double val = GetAs<double>(which_str);
326a8c51b3fSopenharmony_ci  auto unit = Get("time_unit");
327a8c51b3fSopenharmony_ci  BM_CHECK(unit);
328a8c51b3fSopenharmony_ci  if (*unit == "ns") {
329a8c51b3fSopenharmony_ci    return val * 1.e-9;
330a8c51b3fSopenharmony_ci  }
331a8c51b3fSopenharmony_ci  if (*unit == "us") {
332a8c51b3fSopenharmony_ci    return val * 1.e-6;
333a8c51b3fSopenharmony_ci  }
334a8c51b3fSopenharmony_ci  if (*unit == "ms") {
335a8c51b3fSopenharmony_ci    return val * 1.e-3;
336a8c51b3fSopenharmony_ci  }
337a8c51b3fSopenharmony_ci  if (*unit == "s") {
338a8c51b3fSopenharmony_ci    return val;
339a8c51b3fSopenharmony_ci  }
340a8c51b3fSopenharmony_ci  BM_CHECK(1 == 0) << "unknown time unit: " << *unit;
341a8c51b3fSopenharmony_ci  return 0;
342a8c51b3fSopenharmony_ci}
343a8c51b3fSopenharmony_ci
344a8c51b3fSopenharmony_ci// ========================================================================= //
345a8c51b3fSopenharmony_ci// -------------------------- Public API Definitions------------------------ //
346a8c51b3fSopenharmony_ci// ========================================================================= //
347a8c51b3fSopenharmony_ci
348a8c51b3fSopenharmony_ciTestCase::TestCase(std::string re, int rule)
349a8c51b3fSopenharmony_ci    : regex_str(std::move(re)),
350a8c51b3fSopenharmony_ci      match_rule(rule),
351a8c51b3fSopenharmony_ci      substituted_regex(internal::PerformSubstitutions(regex_str)),
352a8c51b3fSopenharmony_ci      regex(std::make_shared<benchmark::Regex>()) {
353a8c51b3fSopenharmony_ci  std::string err_str;
354a8c51b3fSopenharmony_ci  regex->Init(substituted_regex, &err_str);
355a8c51b3fSopenharmony_ci  BM_CHECK(err_str.empty())
356a8c51b3fSopenharmony_ci      << "Could not construct regex \"" << substituted_regex << "\""
357a8c51b3fSopenharmony_ci      << "\n    originally \"" << regex_str << "\""
358a8c51b3fSopenharmony_ci      << "\n    got error: " << err_str;
359a8c51b3fSopenharmony_ci}
360a8c51b3fSopenharmony_ci
361a8c51b3fSopenharmony_ciint AddCases(TestCaseID ID, std::initializer_list<TestCase> il) {
362a8c51b3fSopenharmony_ci  auto& L = internal::GetTestCaseList(ID);
363a8c51b3fSopenharmony_ci  L.insert(L.end(), il);
364a8c51b3fSopenharmony_ci  return 0;
365a8c51b3fSopenharmony_ci}
366a8c51b3fSopenharmony_ci
367a8c51b3fSopenharmony_ciint SetSubstitutions(
368a8c51b3fSopenharmony_ci    std::initializer_list<std::pair<std::string, std::string>> il) {
369a8c51b3fSopenharmony_ci  auto& subs = internal::GetSubstitutions();
370a8c51b3fSopenharmony_ci  for (auto KV : il) {
371a8c51b3fSopenharmony_ci    bool exists = false;
372a8c51b3fSopenharmony_ci    KV.second = internal::PerformSubstitutions(KV.second);
373a8c51b3fSopenharmony_ci    for (auto& EKV : subs) {
374a8c51b3fSopenharmony_ci      if (EKV.first == KV.first) {
375a8c51b3fSopenharmony_ci        EKV.second = std::move(KV.second);
376a8c51b3fSopenharmony_ci        exists = true;
377a8c51b3fSopenharmony_ci        break;
378a8c51b3fSopenharmony_ci      }
379a8c51b3fSopenharmony_ci    }
380a8c51b3fSopenharmony_ci    if (!exists) subs.push_back(std::move(KV));
381a8c51b3fSopenharmony_ci  }
382a8c51b3fSopenharmony_ci  return 0;
383a8c51b3fSopenharmony_ci}
384a8c51b3fSopenharmony_ci
385a8c51b3fSopenharmony_ci// Disable deprecated warnings temporarily because we need to reference
386a8c51b3fSopenharmony_ci// CSVReporter but don't want to trigger -Werror=-Wdeprecated-declarations
387a8c51b3fSopenharmony_ciBENCHMARK_DISABLE_DEPRECATED_WARNING
388a8c51b3fSopenharmony_ci
389a8c51b3fSopenharmony_civoid RunOutputTests(int argc, char* argv[]) {
390a8c51b3fSopenharmony_ci  using internal::GetTestCaseList;
391a8c51b3fSopenharmony_ci  benchmark::Initialize(&argc, argv);
392a8c51b3fSopenharmony_ci  auto options = benchmark::internal::GetOutputOptions(/*force_no_color*/ true);
393a8c51b3fSopenharmony_ci  benchmark::ConsoleReporter CR(options);
394a8c51b3fSopenharmony_ci  benchmark::JSONReporter JR;
395a8c51b3fSopenharmony_ci  benchmark::CSVReporter CSVR;
396a8c51b3fSopenharmony_ci  struct ReporterTest {
397a8c51b3fSopenharmony_ci    std::string name;
398a8c51b3fSopenharmony_ci    std::vector<TestCase>& output_cases;
399a8c51b3fSopenharmony_ci    std::vector<TestCase>& error_cases;
400a8c51b3fSopenharmony_ci    benchmark::BenchmarkReporter& reporter;
401a8c51b3fSopenharmony_ci    std::stringstream out_stream;
402a8c51b3fSopenharmony_ci    std::stringstream err_stream;
403a8c51b3fSopenharmony_ci
404a8c51b3fSopenharmony_ci    ReporterTest(const std::string& n, std::vector<TestCase>& out_tc,
405a8c51b3fSopenharmony_ci                 std::vector<TestCase>& err_tc,
406a8c51b3fSopenharmony_ci                 benchmark::BenchmarkReporter& br)
407a8c51b3fSopenharmony_ci        : name(n), output_cases(out_tc), error_cases(err_tc), reporter(br) {
408a8c51b3fSopenharmony_ci      reporter.SetOutputStream(&out_stream);
409a8c51b3fSopenharmony_ci      reporter.SetErrorStream(&err_stream);
410a8c51b3fSopenharmony_ci    }
411a8c51b3fSopenharmony_ci  } TestCases[] = {
412a8c51b3fSopenharmony_ci      {std::string("ConsoleReporter"), GetTestCaseList(TC_ConsoleOut),
413a8c51b3fSopenharmony_ci       GetTestCaseList(TC_ConsoleErr), CR},
414a8c51b3fSopenharmony_ci      {std::string("JSONReporter"), GetTestCaseList(TC_JSONOut),
415a8c51b3fSopenharmony_ci       GetTestCaseList(TC_JSONErr), JR},
416a8c51b3fSopenharmony_ci      {std::string("CSVReporter"), GetTestCaseList(TC_CSVOut),
417a8c51b3fSopenharmony_ci       GetTestCaseList(TC_CSVErr), CSVR},
418a8c51b3fSopenharmony_ci  };
419a8c51b3fSopenharmony_ci
420a8c51b3fSopenharmony_ci  // Create the test reporter and run the benchmarks.
421a8c51b3fSopenharmony_ci  std::cout << "Running benchmarks...\n";
422a8c51b3fSopenharmony_ci  internal::TestReporter test_rep({&CR, &JR, &CSVR});
423a8c51b3fSopenharmony_ci  benchmark::RunSpecifiedBenchmarks(&test_rep);
424a8c51b3fSopenharmony_ci
425a8c51b3fSopenharmony_ci  for (auto& rep_test : TestCases) {
426a8c51b3fSopenharmony_ci    std::string msg =
427a8c51b3fSopenharmony_ci        std::string("\nTesting ") + rep_test.name + std::string(" Output\n");
428a8c51b3fSopenharmony_ci    std::string banner(msg.size() - 1, '-');
429a8c51b3fSopenharmony_ci    std::cout << banner << msg << banner << "\n";
430a8c51b3fSopenharmony_ci
431a8c51b3fSopenharmony_ci    std::cerr << rep_test.err_stream.str();
432a8c51b3fSopenharmony_ci    std::cout << rep_test.out_stream.str();
433a8c51b3fSopenharmony_ci
434a8c51b3fSopenharmony_ci    internal::CheckCases(rep_test.error_cases, rep_test.err_stream);
435a8c51b3fSopenharmony_ci    internal::CheckCases(rep_test.output_cases, rep_test.out_stream);
436a8c51b3fSopenharmony_ci
437a8c51b3fSopenharmony_ci    std::cout << "\n";
438a8c51b3fSopenharmony_ci  }
439a8c51b3fSopenharmony_ci
440a8c51b3fSopenharmony_ci  // now that we know the output is as expected, we can dispatch
441a8c51b3fSopenharmony_ci  // the checks to subscribees.
442a8c51b3fSopenharmony_ci  auto& csv = TestCases[2];
443a8c51b3fSopenharmony_ci  // would use == but gcc spits a warning
444a8c51b3fSopenharmony_ci  BM_CHECK(csv.name == std::string("CSVReporter"));
445a8c51b3fSopenharmony_ci  internal::GetResultsChecker().CheckResults(csv.out_stream);
446a8c51b3fSopenharmony_ci}
447a8c51b3fSopenharmony_ci
448a8c51b3fSopenharmony_ciBENCHMARK_RESTORE_DEPRECATED_WARNING
449a8c51b3fSopenharmony_ci
450a8c51b3fSopenharmony_ciint SubstrCnt(const std::string& haystack, const std::string& pat) {
451a8c51b3fSopenharmony_ci  if (pat.length() == 0) return 0;
452a8c51b3fSopenharmony_ci  int count = 0;
453a8c51b3fSopenharmony_ci  for (size_t offset = haystack.find(pat); offset != std::string::npos;
454a8c51b3fSopenharmony_ci       offset = haystack.find(pat, offset + pat.length()))
455a8c51b3fSopenharmony_ci    ++count;
456a8c51b3fSopenharmony_ci  return count;
457a8c51b3fSopenharmony_ci}
458a8c51b3fSopenharmony_ci
459a8c51b3fSopenharmony_cistatic char ToHex(int ch) {
460a8c51b3fSopenharmony_ci  return ch < 10 ? static_cast<char>('0' + ch)
461a8c51b3fSopenharmony_ci                 : static_cast<char>('a' + (ch - 10));
462a8c51b3fSopenharmony_ci}
463a8c51b3fSopenharmony_ci
464a8c51b3fSopenharmony_cistatic char RandomHexChar() {
465a8c51b3fSopenharmony_ci  static std::mt19937 rd{std::random_device{}()};
466a8c51b3fSopenharmony_ci  static std::uniform_int_distribution<int> mrand{0, 15};
467a8c51b3fSopenharmony_ci  return ToHex(mrand(rd));
468a8c51b3fSopenharmony_ci}
469a8c51b3fSopenharmony_ci
470a8c51b3fSopenharmony_cistatic std::string GetRandomFileName() {
471a8c51b3fSopenharmony_ci  std::string model = "test.%%%%%%";
472a8c51b3fSopenharmony_ci  for (auto& ch : model) {
473a8c51b3fSopenharmony_ci    if (ch == '%') ch = RandomHexChar();
474a8c51b3fSopenharmony_ci  }
475a8c51b3fSopenharmony_ci  return model;
476a8c51b3fSopenharmony_ci}
477a8c51b3fSopenharmony_ci
478a8c51b3fSopenharmony_cistatic bool FileExists(std::string const& name) {
479a8c51b3fSopenharmony_ci  std::ifstream in(name.c_str());
480a8c51b3fSopenharmony_ci  return in.good();
481a8c51b3fSopenharmony_ci}
482a8c51b3fSopenharmony_ci
483a8c51b3fSopenharmony_cistatic std::string GetTempFileName() {
484a8c51b3fSopenharmony_ci  // This function attempts to avoid race conditions where two tests
485a8c51b3fSopenharmony_ci  // create the same file at the same time. However, it still introduces races
486a8c51b3fSopenharmony_ci  // similar to tmpnam.
487a8c51b3fSopenharmony_ci  int retries = 3;
488a8c51b3fSopenharmony_ci  while (--retries) {
489a8c51b3fSopenharmony_ci    std::string name = GetRandomFileName();
490a8c51b3fSopenharmony_ci    if (!FileExists(name)) return name;
491a8c51b3fSopenharmony_ci  }
492a8c51b3fSopenharmony_ci  std::cerr << "Failed to create unique temporary file name" << std::endl;
493a8c51b3fSopenharmony_ci  std::abort();
494a8c51b3fSopenharmony_ci}
495a8c51b3fSopenharmony_ci
496a8c51b3fSopenharmony_cistd::string GetFileReporterOutput(int argc, char* argv[]) {
497a8c51b3fSopenharmony_ci  std::vector<char*> new_argv(argv, argv + argc);
498a8c51b3fSopenharmony_ci  assert(static_cast<decltype(new_argv)::size_type>(argc) == new_argv.size());
499a8c51b3fSopenharmony_ci
500a8c51b3fSopenharmony_ci  std::string tmp_file_name = GetTempFileName();
501a8c51b3fSopenharmony_ci  std::cout << "Will be using this as the tmp file: " << tmp_file_name << '\n';
502a8c51b3fSopenharmony_ci
503a8c51b3fSopenharmony_ci  std::string tmp = "--benchmark_out=";
504a8c51b3fSopenharmony_ci  tmp += tmp_file_name;
505a8c51b3fSopenharmony_ci  new_argv.emplace_back(const_cast<char*>(tmp.c_str()));
506a8c51b3fSopenharmony_ci
507a8c51b3fSopenharmony_ci  argc = int(new_argv.size());
508a8c51b3fSopenharmony_ci
509a8c51b3fSopenharmony_ci  benchmark::Initialize(&argc, new_argv.data());
510a8c51b3fSopenharmony_ci  benchmark::RunSpecifiedBenchmarks();
511a8c51b3fSopenharmony_ci
512a8c51b3fSopenharmony_ci  // Read the output back from the file, and delete the file.
513a8c51b3fSopenharmony_ci  std::ifstream tmp_stream(tmp_file_name);
514a8c51b3fSopenharmony_ci  std::string output = std::string((std::istreambuf_iterator<char>(tmp_stream)),
515a8c51b3fSopenharmony_ci                                   std::istreambuf_iterator<char>());
516a8c51b3fSopenharmony_ci  std::remove(tmp_file_name.c_str());
517a8c51b3fSopenharmony_ci
518a8c51b3fSopenharmony_ci  return output;
519a8c51b3fSopenharmony_ci}
520