The Sparta Modeling Framework
Loading...
Searching...
No Matches
SpartaTester.hpp
Go to the documentation of this file.
1// <SpartaTester.hpp> -*- C++ -*-
2
3#pragma once
4
11#include <iostream>
12#include <fstream>
13#include <sstream>
14#include <cstring>
15#include <inttypes.h>
16#include <stdexcept>
17#include <string>
18#include <set>
19#include <boost/algorithm/string/predicate.hpp>
21#include "simdb/utils/MathUtils.hpp"
22
23namespace sparta
24{
52 {
53 public:
54 SpartaTester() : SpartaTester(0, std::cerr)
55 {}
56
57 bool expectAllReached(uint32_t expected_reached, const uint32_t line, const char * file) {
58 bool ret = true;
59 if(methods_reached_.size() != expected_reached)
60 {
61 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test failed to execute the "
62 << expected_reached << " expected methods at least once." << "\n"
63 "Instead, " << methods_reached_.size() << " were reached."
64 << std::endl;
65 //list the methods that were in fact reached.
66 cerr_ << "The test only reached the following: " << std::endl;
67 cerr_ << SPARTA_CURRENT_COLOR_GREEN;
68 for(auto s : methods_reached_)
69 {
70 cerr_ << "-> " <<s << "\n";
71 }
72 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "' FAILED on line "
73 << line << " in file " << file << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
74 ++num_errors_;
75 ret = false;
76 cerr_ << std::endl;
77 }
78 return ret;
79 }
80 bool expect(bool val, const char * test_type, const uint32_t line, const char * file) {
81 bool ret = true;
82 if(!val) {
83 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
84 << line << " in file " << file << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
85 ++num_errors_;
86 ret = false;
87 }
88 return ret;
89 }
90
91 // Try and compare bytes and display values on failure instead of characters
92 bool expectEqual(uint8_t v1, uint8_t v2, bool expected, const char * test_type, const uint32_t line, const char * file) {
93 bool ret = true;
94 if((v1 == v2) != expected) {
95 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
96 << line << " in file " << file
97 << ". Value: '" << (uint32_t)v1;
98 if(expected){
99 cerr_ << "' should equal '";
100 }else{
101 cerr_ << "' should NOT equal '";
102 }
103 cerr_ << (uint32_t)v2 << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
104 ++num_errors_;
105 ret = false;
106 }
107 return ret;
108 }
109
110 // Try and compare different types
111 template<typename T>
112 bool expectEqual(const T& v1, const T& v2, bool expected, const char * test_type, const uint32_t line, const char * file) {
113 bool ret = true;
114 if((v1 == v2) != expected) {
115 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
116 << line << " in file " << file
117 << ". Value: '" << v1;
118 if(expected){
119 cerr_ << "' should equal '";
120 }else{
121 cerr_ << "' should NOT equal '";
122 }
123 cerr_ << v2 << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
124 ++num_errors_;
125 ret = false;
126 }
127 return ret;
128 }
129
130 // Try and compare different types
131 template<typename T, typename U=T>
132 bool expectEqual(const T& v1, const U& v2, bool expected, const char * test_type, const uint32_t line, const char * file) {
133 bool ret = true;
134 if(compare<T,U>(v1,v2) != expected) {
135 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
136 << line << " in file " << file
137 << ". Value: '" << v1;
138 if(expected){
139 cerr_ << "' should equal '";
140 }else{
141 cerr_ << "' should NOT equal '";
142 }
143 cerr_ << v2 << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
144 ++num_errors_;
145 ret = false;
146 }
147 return ret;
148 }
149
150 // Comparison operation for two integers having different signed-ness
151 template<typename T, typename U>
152 typename std::enable_if<std::is_integral<T>::value
153 && std::is_integral<U>::value
154 && (std::is_signed<T>::value != std::is_signed<U>::value), bool>::type
155 compare(const T& t, const U& u) {
156 return (t == (T)u);
157 }
158
159 // Comparison operation for everything else
160 template<typename T, typename U>
161 typename std::enable_if<!(std::is_integral<T>::value
162 && std::is_integral<U>::value
163 && (std::is_signed<T>::value != std::is_signed<U>::value)), bool>::type
164 compare(const T& t, const U& u) {
165 return (t == u);
166 }
167
168
169 // Overload for comparison with nullptr
170 template<typename T>
171 bool expectEqual(const T& v1, const std::nullptr_t, bool expected, const char * test_type, const uint32_t line, const char * file) {
172 bool ret = true;
173 if((v1 == nullptr) != expected) {
174 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
175 << line << " in file " << file
176 << ". Value: '" << v1;
177 if(expected){
178 cerr_ << "' should equal '";
179 }else{
180 cerr_ << "' should NOT equal '";
181 }
182 cerr_ << "null" << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
183 ++num_errors_;
184 ret = false;
185 }
186 return ret;
187 }
188
189 // Overload for comparison of nullptr with a var
190 template<typename T>
191 bool expectEqual(const std::nullptr_t, const T& v1, bool expected, const char * test_type, const uint32_t line, const char * file) {
192 bool ret = true;
193 if((nullptr == v1) != expected) {
194 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
195 << line << " in file " << file
196 << ". Value: '" << v1;
197 if(expected){
198 cerr_ << "' should equal '";
199 }else{
200 cerr_ << "' should NOT equal '";
201 }
202 cerr_ << "null" << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
203 ++num_errors_;
204 ret = false;
205 }
206 return ret;
207 }
208
209 template<typename T>
210 typename std::enable_if<
211 std::is_floating_point<T>::value,
212 bool>::type
213 expectEqualWithinTolerance(const T & v1, const T & v2, const T & tol,
214 const char * test_type, const uint32_t line,
215 const char * file) {
216 bool ret = true;
217 if (tol < 0) {
218 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '"
219 << test_type << "' FAILED on line "
220 << line << " in file " << file
221 << ". Negative tolerance supplied."
222 << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
223 ++num_errors_;
224 ret = false;
225 } else {
226 ret = simdb::utils::approximatelyEqual(v1, v2, tol);
227 if (!ret) {
228 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '"
229 << test_type << "' FAILED on line "
230 << line << " in file " << file
231 << ". Value: '" << v1 << "' should be equal to '"
232 << v2 << "' within tolerance '" << tol << "'";
233 ++num_errors_;
234 }
235 }
236 return ret;
237 }
238
239 void throwTestFailed(const char * test_type, const uint32_t line, const char * file, const char * exception_what="") {
240 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Throw Test Fail:'" << test_type << "' FAILED on line "
241 << line << " in file " << file << std::endl;
242 if(exception_what != 0 && strlen(exception_what) != 0) {
243 cerr_ << " Exception: " << exception_what << std::endl;
244 }
245 cerr_ << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
246 ++num_errors_;
247 }
248
268 void expectFilesEqual(const std::string& a, const std::string& b, bool expected, uint32_t line, const char * file, const bool ignore_commented_lines = true) {
269 std::ifstream fa, fb;
270 std::stringstream err;
271 try{
272 fa.open(a, std::ios_base::in);
273 }catch(std::exception&){
274 }
275 if(fa.fail()){
276 err.str("");
277 err << "Could not open file \"" << a << "\"";
278 fileComparisonFailed(a, b, line, file, err.str());
279 }
280 try{
281 fb.open(b, std::ios_base::in);
282 }catch(std::exception&){
283 }
284 if(fb.fail()){
285 err.str("");
286 err << "Could not open file \"" << b << "\"";
287 fileComparisonFailed(a, b, line, file, err.str());
288 }
289
290 if(!fa.fail() && !fb.fail()){
291 uint32_t line_num = 0;
292 uint32_t last_line_pos = 0;
293 uint64_t pos = 0;
294 bool was_newline = true;
295 while(1){
296 char cho, chn;
297 cho = fa.get();
298 chn = fb.get();
299
300 // Ignore lines starting with '#'
301 if(was_newline && ignore_commented_lines){
302 was_newline = false;
303 if(cho == '#'){
304 while(1){
305 // Read until after newline
306 cho = fa.get();
307 if(cho == '\n'){
308 cho = fa.get();
309 if(cho != '#'){
310 break;
311 }
312 }
313 ++pos; // Increment pos on this file, but not the other
314 }
315 }
316 if(chn == '#'){
317 // Read until after newline
318 while(1){
319 chn = fb.get();
320 if(chn == '\n'){
321 chn = fb.get();
322 if(chn != '#'){
323 break;
324 }
325 }
326 }
327 }
328 }
329
330 if(!fa.good() || !fb.good()){
331 if((fa.good() != fb.good()) && expected == true){
332 std::stringstream msg;
333 msg << "Files were different lengths: ";
334 if(!fa.good()){
335 msg << a << " was shorter than " << b << " at char '" << chn << "' #" << pos;
336 }else{
337 msg << b << " was shorter than " << a << " at char '" << cho << "' #" << pos;
338 }
339 fileComparisonFailed(a, b, line, file, msg.str());
340 }
341 break;
342 }
343
344 if(cho != chn){
345 err.str("");
346 err << "Files differed at pos " << pos << " (line "
347 << line_num << ", col " << pos - last_line_pos
348 << ") with chars: '" << cho << "' != '" << chn << "'";
349 if(expected == true){
350 fileComparisonFailed(a, b, line, file, err.str());
351 }
352 return;
353 }
354 ++pos;
355 if(cho == '\n'){ // prev char (pos-1)
356 ++line_num;
357 last_line_pos = pos; // Line starts here
358 was_newline = true;
359 }
360 }
361
362 if(expected == false){
363 fileComparisonFailed(a, b, line, file, "Files were the same");
364 }
365 }
366 }
367
368 void fileComparisonFailed(const std::string& a, const std::string& b, const uint32_t line, const char * file, const std::string& error) {
369 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "File comparison test between \"" << a << "\" and \"" << b << "\" FAILED on line "
370 << line << " in file " << file << std::endl;
371 cerr_ << " Exception: " << error << std::endl;
372 cerr_ << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
373 ++num_errors_;
374 }
375
376 void reachedMethod(const std::string& method_title)
377 {
378 methods_reached_.insert(method_title);
379 }
380 static SpartaTester * getInstance() {
381 static SpartaTester inst;
382 return & inst;
383 }
384
385 static std::unique_ptr<SpartaTester> makeTesterWithUserCError(std::ostream & cerr) {
386 return std::unique_ptr<SpartaTester>(new SpartaTester(0, cerr));
387 }
388
389 static uint32_t getErrorCode(const SpartaTester * tester = getInstance()) {
390 return tester->num_errors_;
391 }
392
393 private:
394 SpartaTester(const uint32_t num_errors,
395 std::ostream & cerr) :
396 num_errors_(num_errors),
397 methods_reached_(),
398 cerr_(cerr)
399 {}
400
401 uint32_t num_errors_;
402 std::set<std::string> methods_reached_;
403 std::ostream & cerr_;
404 };
405
406
412#define TEST_INIT
413
414
420#define EXPECT_REACHED() sparta::SpartaTester::getInstance()->reachedMethod(__FUNCTION__)
421
422
429#define ENSURE_ALL_REACHED(x) sparta::SpartaTester::getInstance()->expectAllReached(x, __LINE__, __FUNCTION__)
430
431
446#define EXPECT_TRUE(x) sparta::SpartaTester::getInstance()->expect((x), #x, __LINE__, __FILE__)
447
466#define EXPECT_EQUAL(x, y) sparta::SpartaTester::getInstance()->expectEqual((x), (y), true, #x, __LINE__, __FILE__)
467
486#define EXPECT_NOTEQUAL(x, y) sparta::SpartaTester::getInstance()->expectEqual((x), (y), false, #x, __LINE__, __FILE__)
487
508#define EXPECT_WITHIN_TOLERANCE(x, y, tol) sparta::SpartaTester::getInstance()-> \
509 expectEqualWithinTolerance((x), (y), (tol), #x, __LINE__, __FILE__)
510
532#define EXPECT_WITHIN_EPSILON(x, y) sparta::SpartaTester::getInstance()-> \
533 expectEqualWithinTolerance((x), (y), \
534 (std::numeric_limits<decltype(x)>::epsilon()), #x, __LINE__, __FILE__)
535
550#define EXPECT_FALSE(x) sparta::SpartaTester::getInstance()->expect(!(x), #x, __LINE__, __FILE__)
551
561#define EXPECT_THROW(x) { \
562 bool did_it_throw = false; \
563 try {x;} \
564 catch(...) \
565 { did_it_throw = true; } \
566 if(did_it_throw == false) { \
567 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__); \
568 } \
569 }
570
589#define EXPECT_THROW_MSG_SHORT(x, expected_msg) { \
590 bool did_it_throw = false; \
591 try { x; } \
592 catch(sparta::SpartaException& ex) \
593 { did_it_throw = true; \
594 if (strcmp(expected_msg, ex.rawReason().c_str()) != 0) { \
595 std::cerr << "Expected msg: " << expected_msg << std::endl; \
596 std::cerr << "Actual msg: " << ex.what() << std::endl; \
597 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, ex.what()); \
598 } \
599 } \
600 if(did_it_throw == false) { \
601 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, "did not throw"); \
602 } \
603 }
604
623#define EXPECT_THROW_MSG_LONG(x, expected_msg) { \
624 bool did_it_throw = false; \
625 try { x; } \
626 catch(sparta::SpartaException& ex) \
627 { did_it_throw = true; \
628 if (strcmp(expected_msg, ex.what()) != 0) { \
629 std::cerr << "Expected msg: " << expected_msg << std::endl; \
630 std::cerr << "Actual msg: " << ex.what() << std::endl; \
631 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, ex.what()); \
632 } \
633 } \
634 if(did_it_throw == false) { \
635 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, "did not throw"); \
636 } \
637 }
638
639#define EXPECT_THROW_MSG_CONTAINS(x, expected_msg) { \
640 bool did_it_throw = false; \
641 try { x; } \
642 catch(sparta::SpartaException& ex) \
643 { did_it_throw = true; \
644 if (boost::algorithm::contains(ex.what(), expected_msg) != true) { \
645 std::cerr << "Expected msg: " << expected_msg << std::endl; \
646 std::cerr << "Actual msg: " << ex.what() << std::endl; \
647 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, ex.what()); \
648 } \
649 } \
650 if(did_it_throw == false) { \
651 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, "did not throw"); \
652 } \
653 }
654
655
656
657
667#define EXPECT_NOTHROW(x) { \
668 bool did_it_throw = false; \
669 std::string exception_what; \
670 try { x; } \
671 catch(std::exception& e) \
672 { did_it_throw = true; \
673 exception_what = e.what(); \
674 } \
675 catch(...) \
676 { did_it_throw = true; } \
677 if(did_it_throw == true) { \
678 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, exception_what.c_str()); \
679 } \
680 }
681
692#define EXPECT_FILES_EQUAL(a, b) { \
693 sparta::SpartaTester::getInstance()->expectFilesEqual(a, b, true, __LINE__, __FILE__); \
694 }
695
706#define EXPECT_FILES_NOTEQUAL(a, b) { \
707 sparta::SpartaTester::getInstance()->expectFilesEqual(a, b, false, __LINE__, __FILE__); \
708 }
709
714#define ERROR_CODE sparta::SpartaTester::getErrorCode()
715
737#define REPORT_ERROR \
738 if(ERROR_CODE != 0) { \
739 std::cout << std::dec << "\n" << SPARTA_UNMANAGED_COLOR_BRIGHT_RED << ERROR_CODE \
740 << " ERROR(S) found during test.\n" << SPARTA_UNMANAGED_COLOR_NORMAL << std::endl; \
741 } else { \
742 std::cout << std::dec << "\n" \
743 << "TESTS PASSED -- No errors found during test.\n" << std::endl; \
744 }
745}
Color code for SPARTA.
#define SPARTA_CURRENT_COLOR_NORMAL
Macros for accessing the colors through the default scheme.
Definition Colors.hpp:40
A simple testing class.
void expectFilesEqual(const std::string &a, const std::string &b, bool expected, uint32_t line, const char *file, const bool ignore_commented_lines=true)
Compares two files.
Macros for handling exponential backoff.