xref: /netbsd-src/external/apache2/llvm/dist/libcxx/utils/google-benchmark/test/output_test.h (revision 4d6fc14bc9b0c5bf3e30be318c143ee82cadd108)
1*4d6fc14bSjoerg #ifndef TEST_OUTPUT_TEST_H
2*4d6fc14bSjoerg #define TEST_OUTPUT_TEST_H
3*4d6fc14bSjoerg 
4*4d6fc14bSjoerg #undef NDEBUG
5*4d6fc14bSjoerg #include <functional>
6*4d6fc14bSjoerg #include <initializer_list>
7*4d6fc14bSjoerg #include <memory>
8*4d6fc14bSjoerg #include <sstream>
9*4d6fc14bSjoerg #include <string>
10*4d6fc14bSjoerg #include <utility>
11*4d6fc14bSjoerg #include <vector>
12*4d6fc14bSjoerg 
13*4d6fc14bSjoerg #include "../src/re.h"
14*4d6fc14bSjoerg #include "benchmark/benchmark.h"
15*4d6fc14bSjoerg 
16*4d6fc14bSjoerg #define CONCAT2(x, y) x##y
17*4d6fc14bSjoerg #define CONCAT(x, y) CONCAT2(x, y)
18*4d6fc14bSjoerg 
19*4d6fc14bSjoerg #define ADD_CASES(...) int CONCAT(dummy, __LINE__) = ::AddCases(__VA_ARGS__)
20*4d6fc14bSjoerg 
21*4d6fc14bSjoerg #define SET_SUBSTITUTIONS(...) \
22*4d6fc14bSjoerg   int CONCAT(dummy, __LINE__) = ::SetSubstitutions(__VA_ARGS__)
23*4d6fc14bSjoerg 
24*4d6fc14bSjoerg enum MatchRules {
25*4d6fc14bSjoerg   MR_Default,  // Skip non-matching lines until a match is found.
26*4d6fc14bSjoerg   MR_Next,     // Match must occur on the next line.
27*4d6fc14bSjoerg   MR_Not  // No line between the current position and the next match matches
28*4d6fc14bSjoerg           // the regex
29*4d6fc14bSjoerg };
30*4d6fc14bSjoerg 
31*4d6fc14bSjoerg struct TestCase {
32*4d6fc14bSjoerg   TestCase(std::string re, int rule = MR_Default);
33*4d6fc14bSjoerg 
34*4d6fc14bSjoerg   std::string regex_str;
35*4d6fc14bSjoerg   int match_rule;
36*4d6fc14bSjoerg   std::string substituted_regex;
37*4d6fc14bSjoerg   std::shared_ptr<benchmark::Regex> regex;
38*4d6fc14bSjoerg };
39*4d6fc14bSjoerg 
40*4d6fc14bSjoerg enum TestCaseID {
41*4d6fc14bSjoerg   TC_ConsoleOut,
42*4d6fc14bSjoerg   TC_ConsoleErr,
43*4d6fc14bSjoerg   TC_JSONOut,
44*4d6fc14bSjoerg   TC_JSONErr,
45*4d6fc14bSjoerg   TC_CSVOut,
46*4d6fc14bSjoerg   TC_CSVErr,
47*4d6fc14bSjoerg 
48*4d6fc14bSjoerg   TC_NumID  // PRIVATE
49*4d6fc14bSjoerg };
50*4d6fc14bSjoerg 
51*4d6fc14bSjoerg // Add a list of test cases to be run against the output specified by
52*4d6fc14bSjoerg // 'ID'
53*4d6fc14bSjoerg int AddCases(TestCaseID ID, std::initializer_list<TestCase> il);
54*4d6fc14bSjoerg 
55*4d6fc14bSjoerg // Add or set a list of substitutions to be performed on constructed regex's
56*4d6fc14bSjoerg // See 'output_test_helper.cc' for a list of default substitutions.
57*4d6fc14bSjoerg int SetSubstitutions(
58*4d6fc14bSjoerg     std::initializer_list<std::pair<std::string, std::string>> il);
59*4d6fc14bSjoerg 
60*4d6fc14bSjoerg // Run all output tests.
61*4d6fc14bSjoerg void RunOutputTests(int argc, char* argv[]);
62*4d6fc14bSjoerg 
63*4d6fc14bSjoerg // Count the number of 'pat' substrings in the 'haystack' string.
64*4d6fc14bSjoerg int SubstrCnt(const std::string& haystack, const std::string& pat);
65*4d6fc14bSjoerg 
66*4d6fc14bSjoerg // Run registered benchmarks with file reporter enabled, and return the content
67*4d6fc14bSjoerg // outputted by the file reporter.
68*4d6fc14bSjoerg std::string GetFileReporterOutput(int argc, char* argv[]);
69*4d6fc14bSjoerg 
70*4d6fc14bSjoerg // ========================================================================= //
71*4d6fc14bSjoerg // ------------------------- Results checking ------------------------------ //
72*4d6fc14bSjoerg // ========================================================================= //
73*4d6fc14bSjoerg 
74*4d6fc14bSjoerg // Call this macro to register a benchmark for checking its results. This
75*4d6fc14bSjoerg // should be all that's needed. It subscribes a function to check the (CSV)
76*4d6fc14bSjoerg // results of a benchmark. This is done only after verifying that the output
77*4d6fc14bSjoerg // strings are really as expected.
78*4d6fc14bSjoerg // bm_name_pattern: a name or a regex pattern which will be matched against
79*4d6fc14bSjoerg //                  all the benchmark names. Matching benchmarks
80*4d6fc14bSjoerg //                  will be the subject of a call to checker_function
81*4d6fc14bSjoerg // checker_function: should be of type ResultsCheckFn (see below)
82*4d6fc14bSjoerg #define CHECK_BENCHMARK_RESULTS(bm_name_pattern, checker_function) \
83*4d6fc14bSjoerg   size_t CONCAT(dummy, __LINE__) = AddChecker(bm_name_pattern, checker_function)
84*4d6fc14bSjoerg 
85*4d6fc14bSjoerg struct Results;
86*4d6fc14bSjoerg typedef std::function<void(Results const&)> ResultsCheckFn;
87*4d6fc14bSjoerg 
88*4d6fc14bSjoerg size_t AddChecker(const char* bm_name_pattern, ResultsCheckFn fn);
89*4d6fc14bSjoerg 
90*4d6fc14bSjoerg // Class holding the results of a benchmark.
91*4d6fc14bSjoerg // It is passed in calls to checker functions.
92*4d6fc14bSjoerg struct Results {
93*4d6fc14bSjoerg   // the benchmark name
94*4d6fc14bSjoerg   std::string name;
95*4d6fc14bSjoerg   // the benchmark fields
96*4d6fc14bSjoerg   std::map<std::string, std::string> values;
97*4d6fc14bSjoerg 
ResultsResults98*4d6fc14bSjoerg   Results(const std::string& n) : name(n) {}
99*4d6fc14bSjoerg 
100*4d6fc14bSjoerg   int NumThreads() const;
101*4d6fc14bSjoerg 
102*4d6fc14bSjoerg   double NumIterations() const;
103*4d6fc14bSjoerg 
104*4d6fc14bSjoerg   typedef enum { kCpuTime, kRealTime } BenchmarkTime;
105*4d6fc14bSjoerg 
106*4d6fc14bSjoerg   // get cpu_time or real_time in seconds
107*4d6fc14bSjoerg   double GetTime(BenchmarkTime which) const;
108*4d6fc14bSjoerg 
109*4d6fc14bSjoerg   // get the real_time duration of the benchmark in seconds.
110*4d6fc14bSjoerg   // it is better to use fuzzy float checks for this, as the float
111*4d6fc14bSjoerg   // ASCII formatting is lossy.
DurationRealTimeResults112*4d6fc14bSjoerg   double DurationRealTime() const {
113*4d6fc14bSjoerg     return NumIterations() * GetTime(kRealTime);
114*4d6fc14bSjoerg   }
115*4d6fc14bSjoerg   // get the cpu_time duration of the benchmark in seconds
DurationCPUTimeResults116*4d6fc14bSjoerg   double DurationCPUTime() const {
117*4d6fc14bSjoerg     return NumIterations() * GetTime(kCpuTime);
118*4d6fc14bSjoerg   }
119*4d6fc14bSjoerg 
120*4d6fc14bSjoerg   // get the string for a result by name, or nullptr if the name
121*4d6fc14bSjoerg   // is not found
GetResults122*4d6fc14bSjoerg   const std::string* Get(const char* entry_name) const {
123*4d6fc14bSjoerg     auto it = values.find(entry_name);
124*4d6fc14bSjoerg     if (it == values.end()) return nullptr;
125*4d6fc14bSjoerg     return &it->second;
126*4d6fc14bSjoerg   }
127*4d6fc14bSjoerg 
128*4d6fc14bSjoerg   // get a result by name, parsed as a specific type.
129*4d6fc14bSjoerg   // NOTE: for counters, use GetCounterAs instead.
130*4d6fc14bSjoerg   template <class T>
131*4d6fc14bSjoerg   T GetAs(const char* entry_name) const;
132*4d6fc14bSjoerg 
133*4d6fc14bSjoerg   // counters are written as doubles, so they have to be read first
134*4d6fc14bSjoerg   // as a double, and only then converted to the asked type.
135*4d6fc14bSjoerg   template <class T>
GetCounterAsResults136*4d6fc14bSjoerg   T GetCounterAs(const char* entry_name) const {
137*4d6fc14bSjoerg     double dval = GetAs<double>(entry_name);
138*4d6fc14bSjoerg     T tval = static_cast<T>(dval);
139*4d6fc14bSjoerg     return tval;
140*4d6fc14bSjoerg   }
141*4d6fc14bSjoerg };
142*4d6fc14bSjoerg 
143*4d6fc14bSjoerg template <class T>
GetAs(const char * entry_name)144*4d6fc14bSjoerg T Results::GetAs(const char* entry_name) const {
145*4d6fc14bSjoerg   auto* sv = Get(entry_name);
146*4d6fc14bSjoerg   CHECK(sv != nullptr && !sv->empty());
147*4d6fc14bSjoerg   std::stringstream ss;
148*4d6fc14bSjoerg   ss << *sv;
149*4d6fc14bSjoerg   T out;
150*4d6fc14bSjoerg   ss >> out;
151*4d6fc14bSjoerg   CHECK(!ss.fail());
152*4d6fc14bSjoerg   return out;
153*4d6fc14bSjoerg }
154*4d6fc14bSjoerg 
155*4d6fc14bSjoerg //----------------------------------
156*4d6fc14bSjoerg // Macros to help in result checking. Do not use them with arguments causing
157*4d6fc14bSjoerg // side-effects.
158*4d6fc14bSjoerg 
159*4d6fc14bSjoerg // clang-format off
160*4d6fc14bSjoerg 
161*4d6fc14bSjoerg #define _CHECK_RESULT_VALUE(entry, getfn, var_type, var_name, relationship, value) \
162*4d6fc14bSjoerg     CONCAT(CHECK_, relationship)                                        \
163*4d6fc14bSjoerg     (entry.getfn< var_type >(var_name), (value)) << "\n"                \
164*4d6fc14bSjoerg     << __FILE__ << ":" << __LINE__ << ": " << (entry).name << ":\n"     \
165*4d6fc14bSjoerg     << __FILE__ << ":" << __LINE__ << ": "                              \
166*4d6fc14bSjoerg     << "expected (" << #var_type << ")" << (var_name)                   \
167*4d6fc14bSjoerg     << "=" << (entry).getfn< var_type >(var_name)                       \
168*4d6fc14bSjoerg     << " to be " #relationship " to " << (value) << "\n"
169*4d6fc14bSjoerg 
170*4d6fc14bSjoerg // check with tolerance. eps_factor is the tolerance window, which is
171*4d6fc14bSjoerg // interpreted relative to value (eg, 0.1 means 10% of value).
172*4d6fc14bSjoerg #define _CHECK_FLOAT_RESULT_VALUE(entry, getfn, var_type, var_name, relationship, value, eps_factor) \
173*4d6fc14bSjoerg     CONCAT(CHECK_FLOAT_, relationship)                                  \
174*4d6fc14bSjoerg     (entry.getfn< var_type >(var_name), (value), (eps_factor) * (value)) << "\n" \
175*4d6fc14bSjoerg     << __FILE__ << ":" << __LINE__ << ": " << (entry).name << ":\n"     \
176*4d6fc14bSjoerg     << __FILE__ << ":" << __LINE__ << ": "                              \
177*4d6fc14bSjoerg     << "expected (" << #var_type << ")" << (var_name)                   \
178*4d6fc14bSjoerg     << "=" << (entry).getfn< var_type >(var_name)                       \
179*4d6fc14bSjoerg     << " to be " #relationship " to " << (value) << "\n"                \
180*4d6fc14bSjoerg     << __FILE__ << ":" << __LINE__ << ": "                              \
181*4d6fc14bSjoerg     << "with tolerance of " << (eps_factor) * (value)                   \
182*4d6fc14bSjoerg     << " (" << (eps_factor)*100. << "%), "                              \
183*4d6fc14bSjoerg     << "but delta was " << ((entry).getfn< var_type >(var_name) - (value)) \
184*4d6fc14bSjoerg     << " (" << (((entry).getfn< var_type >(var_name) - (value))         \
185*4d6fc14bSjoerg                /                                                        \
186*4d6fc14bSjoerg                ((value) > 1.e-5 || value < -1.e-5 ? value : 1.e-5)*100.) \
187*4d6fc14bSjoerg     << "%)"
188*4d6fc14bSjoerg 
189*4d6fc14bSjoerg #define CHECK_RESULT_VALUE(entry, var_type, var_name, relationship, value) \
190*4d6fc14bSjoerg     _CHECK_RESULT_VALUE(entry, GetAs, var_type, var_name, relationship, value)
191*4d6fc14bSjoerg 
192*4d6fc14bSjoerg #define CHECK_COUNTER_VALUE(entry, var_type, var_name, relationship, value) \
193*4d6fc14bSjoerg     _CHECK_RESULT_VALUE(entry, GetCounterAs, var_type, var_name, relationship, value)
194*4d6fc14bSjoerg 
195*4d6fc14bSjoerg #define CHECK_FLOAT_RESULT_VALUE(entry, var_name, relationship, value, eps_factor) \
196*4d6fc14bSjoerg     _CHECK_FLOAT_RESULT_VALUE(entry, GetAs, double, var_name, relationship, value, eps_factor)
197*4d6fc14bSjoerg 
198*4d6fc14bSjoerg #define CHECK_FLOAT_COUNTER_VALUE(entry, var_name, relationship, value, eps_factor) \
199*4d6fc14bSjoerg     _CHECK_FLOAT_RESULT_VALUE(entry, GetCounterAs, double, var_name, relationship, value, eps_factor)
200*4d6fc14bSjoerg 
201*4d6fc14bSjoerg // clang-format on
202*4d6fc14bSjoerg 
203*4d6fc14bSjoerg // ========================================================================= //
204*4d6fc14bSjoerg // --------------------------- Misc Utilities ------------------------------ //
205*4d6fc14bSjoerg // ========================================================================= //
206*4d6fc14bSjoerg 
207*4d6fc14bSjoerg namespace {
208*4d6fc14bSjoerg 
209*4d6fc14bSjoerg const char* const dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
210*4d6fc14bSjoerg 
211*4d6fc14bSjoerg }  //  end namespace
212*4d6fc14bSjoerg 
213*4d6fc14bSjoerg #endif  // TEST_OUTPUT_TEST_H
214