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